1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
|
/* mtx.c
This program was written to control the ADIC 1200G autoloading DAT drive.
It may also work on other drives, but take into consideration the fact
that I am new at SCSI programming and I put this together in a few days to
test the device. Try it and let me know of successes/failures.
This program was written (and it seem to work) on a HP9000/735 with HP-UX
9.01. If you have HP-UX 10.x you can find a similar program in patch
PHCO_10848.
It needs the Autochanger driver compiled in the kernel, by default it's
not, so you'll have to rebuild the kernel.
It needs to know BOTH the device file for the Autoloader and for the Tape
as explained in the comments (See the Unload Function). Set the default
values in the proper variables later. If you know a way of having it work
without knowing the tape too, please let me know as it bugged me no end.
It is important that the autochanger device is created with the proper
major number (on HP-UX 9.x it's 33, check it in /etc/master)
Syntax is explained in the "Usage:" section that is printed when mtx is
run without parameters. I hope it's clear enough not to need any further
explanation.
In case of problems it will exit with status 1 and print a (hopefully)
explanatory message.
(c) 1998 GiP, (GianPiero Puccioni)gip@ino.it
Permission to use, copy, modify, distribute, and sell this software and
its documentation for any purpose is hereby granted without fee, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. The author makes no representations about the suitability
of this software for any purpose.It is provided "as is" without express or
implied warranty.
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/scsi.h>
#define MAXSLOT 16
struct element_addresses el_ad;
struct element_status el_st;
struct move_medium_parms move;
struct inquiry_2 inq;
struct reservation_parms res;
char load[2][6]={"Empty","Full"};
char errmsg[80];
int slot[MAXSLOT+1];
int ns,fs,fd,ft,from;
int dev,rc;
int last,first;
char *prog;
int SCSI_Open(void);
void SCSI_Close(void);
void Ferror(void);
void Help(void);
void Init(void);
void PrintStatus(void);
void Load(int sl);
void MtOff(void);
void Unload(int sl);
void Next(int i);
void Prev(int i);
/* defaults for Autochanger and Tape device files */
/* Set your own */
char chgf[50]="/dev/scsi/adic";
char tapf[50]="/dev/rmt/dat";
int main(int argc, char **argv)
{
int i,j,ch;
char action;
int where;
int numopt=0;
/* collect the options */
prog=argv[0];
action='z';
while ((ch = getopt(argc, argv, ":hsl:u:ipnf:t:")) != EOF)
{
switch(ch)
{
case 'h' : action='h'; /* help */
numopt++;
break;
case 's' : action='s'; /* status */
numopt++;
break;
case 'u' : action='u'; /* unload */
numopt++;
sscanf(optarg,"%d",&where);
break;
case 'l' : action='l'; /* load */
numopt++;
sscanf(optarg,"%d",&where);
break;
case 'n' : action='n'; /* next */
numopt++;
break;
case 'p' : action='p'; /* prev */
numopt++;
break;
case 'i' : action='i'; /* init */
numopt++;
break;
case 'f' : strncpy(chgf,optarg,50) ; /* set changer */
break;
case 't' : strncpy(tapf,optarg,50) ; /* set tape */
break;
case ':' : if (optopt == 'u') /* 'u' without parms */
{ where=0;
numopt++;
action='u';
break;
}
printf("option -%c requires argument \n",optopt);
Help();
break;
case '?' : printf("option -%c is unrecognised\n",optopt);
Help();
exit(1);
break;
}
}
/* Too many options Print Usage:*/
if (numopt > 1)
{ printf(" Too many Options ! \n");
Help();
exit(1);
}
/* Either no parms or 'h' => Print "Usage:" */
if(action == 'z'||action == 'h')
{ Help();
exit(0);
}
/* Open Autochanger device */
dev=SCSI_Open();
/* Check if it's recognized as a CHANGER */
rc = ioctl(dev,SIOC_INQUIRY,&inq);
if (rc)
{ sprintf(errmsg,"ioctl INQUIRY failed: %d\n",errno);
Ferror();
}
else if(inq.dev_type!= SCSI_AUTOCHANGER )
{
sprintf(errmsg,"Wrong dev type: %d\n",inq.dev_type);
Ferror();
}
/* Get (internal) address of elements */
rc = ioctl(dev,SIOC_ELEMENT_ADDRESSES,&el_ad);
if (rc)
{ fprintf(stderr,"ioctl failed: %d\n",errno);
close(dev);
exit(1);
}
else
{
fs=el_ad.first_storage;
ns=el_ad.num_storages;
fd=el_ad.first_data_transfer;
ft=el_ad.first_transport;
first=fs; /* address of first element */
last=fs+ns-1; /* address of last element */
}
if (ns > MAXSLOT) /* just in case... */
{ sprintf(errmsg,"Too many slots: %d MAX=%d\n",ns,MAXSLOT);
SCSI_Close();
Ferror();
}
/* get status of transfer element (i.e. tape)
In case it's loaded get address of element loaded. */
from=0;
el_st.element=fd;
rc = ioctl(dev,SIOC_ELEMENT_STATUS,&el_st);
slot[0]=el_st.full;
if(slot[0])
from=el_st.source_element;
/* get status of each slot (Empty or Full)
First Slot is 1
*/
for(i=1,j=fs;i<=ns;i++,j++)
{ el_st.element=j;
rc = ioctl(dev,SIOC_ELEMENT_STATUS,&el_st);
if (rc)
{ sprintf(errmsg,"ioctl STAT failed: %d\n",errno);
SCSI_Close();
Ferror();
}
slot[i]=el_st.full;
}
/* Do the job */
switch(action)
{
case 'i': Init();
break;
case 's': PrintStatus();
break;
case 'l': Load(where);
break;
case 'u': Unload(where);
break;
case 'n': Next(0);
break;
case 'p': Prev(0);
break;
case 'N': Next(1);
break;
case 'P': Prev(1);
break;
}
SCSI_Close();
exit(0);
}
/* End Of Main */
/* Open the SCSI device for the changer */
int SCSI_Open()
{
int d;
d = open(chgf, O_RDONLY);
if (d < 0)
{ sprintf(errmsg,"Cannot open SCSI device '%s'\n", chgf);
Ferror();
}
return d;
}
/* Close the SCSI device for the changer */
void SCSI_Close()
{
if (close(dev) < 0)
{ sprintf(errmsg,"Cannot close SCSI device '%s'\n", chgf);
Ferror();
}
}
/* Print the Error message and exit with 1 */
void Ferror()
{
fprintf(stderr,errmsg);
exit(1);
}
/* Print "Usage:" message */
void Help()
{
printf ("\n Usage: %s [-f changer] [-t tape] option\n",prog);
printf (" option: -s print status\n");
printf (" -lN load from slot N (first slot is 1)\n");
printf (" -u[N] unload to N or to slot last loaded\n");
printf (" -n load from next available slot\n");
printf (" -N like -n but if Last tape loaded load First\n");
printf (" -p load from previous available slot\n");
printf (" -P like -p but if First tape loaded load Last\n");
printf (" -i init device\n");
printf (" -h print this help\n\n");
printf (" if changer is not specified %s will be used\n",chgf);
printf (" if tape is not specified %s will be used\n",tapf);
printf (" if media is loaded a 'load' request will imply a -u\n\n");
exit(0);
}
/* Init the changer:
Test each slot for presence of a tape
It will fail if a tape is in the driver
*/
void Init()
{
if(slot[0])
{ printf("Cannot Initialize: a tape is loaded\n");
exit(1);
}
rc = ioctl(dev,SIOC_INIT_ELEM_STAT,&el_ad);
if (rc)
{ sprintf(errmsg,"ioctl INIT failed: %d\n",errno);
Ferror();
}
}
/* Print the status of the changer:
the values of changer and tape devices
if media is loaded (and from where it came)
if each slot is Empty or Full
*/
void PrintStatus()
{
int i;
printf("Changer:%s\n",chgf);
printf("Tape :%s\n",tapf);
if(slot[0])
printf(" Data element: Full (Element %d loaded)\n",from-fs+1);
else
printf(" Data element: Empty\n");
for(i=1;i<=ns;i++)
printf(" Slot %2d: %s\n", i,load[slot[i]]);
}
/* Load media from slot sl
if a tape is already loaded unload it
to the slot it came from
*/
void Load(int sl)
{
if(slot[0])
Unload(0);
move.transport=ft;
move.source=fs+sl-1;
move.destination=fd;
move.invert=0;
rc = ioctl(dev,SIOC_MOVE_MEDIUM,&move);
if (rc)
{ sprintf(errmsg,"load failed: errno=%d\n",errno);
SCSI_Close();
Ferror();
}
}
/* Load Next Tape
if k=1 then last=>first
*/
void Next(int k)
{
if (from == last)
{
if(!k)
{ sprintf(errmsg,"Last Tape loaded\n",errno);
SCSI_Close();
Ferror();
}
Load(first);
}
else
Load(from+1);
}
/* Load Prev Tape
if k=1 then first=>last
*/
void Prev(int k)
{
if (from == first)
{
if(!k)
{ sprintf(errmsg,"First Tape loaded\n",errno);
SCSI_Close();
Ferror();
}
Load(last);
}
else
Load(from-1);
}
/* Unload a tape !
This was the big problem and the reason why I need to know the device for the
tape too. What happens is that when I load a tape the Autochanger goes into a
"locked" status and I couldn't find a way of "unlocking" it. According to the
SCSI docs a "move" out of the transfer element should unmount the media, but
it doesn't. Only way I could find to do this is send the equivalent of a
"mt -t tape rewoffl" to the TAPE device. If you know how to do this some other
way let me know.
*/
void Unload(int sl)
{
if (!slot[0])
{ printf("No tape loaded\n");
exit(0);
}
/* unload to original slot or to selected slot */
if (sl == 0)
sl=from;
else
sl=sl+fs-1;
/* I don't know if it makes sense to unload to a different slot. It seems
it's supported by the SCSI but I don't think the robot can do it.
So I disabled it for now.
If you want to try comment out the following line
*/
sl=from; /* unload from the original slot anyway */
MtOff(); /* this will rewind the tape and put it offline */
move.transport=ft;
move.source=fd;
move.destination=sl;
move.invert=0;
rc = ioctl(dev,SIOC_MOVE_MEDIUM,&move);
if (rc)
{ sprintf(errmsg,"unload failed: errno=%d\n",errno);
SCSI_Close();
Ferror();
}
}
/* Tape stuff:
Open the device, send the command to rewind it and put it offline
then close the device.
If the device can't be opened it waits a little and tries again,
sometimes it happens when two commands are given one after the other
*/
#include <sys/mtio.h>
void MtOff()
{
int devd,rc;
struct mtop mop;
mop.mt_op=MTOFFL;
mop.mt_count=1;
devd = open(tapf,O_RDWR);
if (devd < 0)
{ printf("Wait....\n");
sleep(5);
devd = open(tapf,O_RDWR);
if (devd < 0)
{ sprintf(errmsg,"cannot open DAT device:%d errno=%d \n",devd, errno);
Ferror();
exit(1);
}
}
rc = ioctl(devd,MTIOCTOP,&mop);
if (rc)
{ sprintf(errmsg,"Offline failed: errno=%d\n",errno);
close(devd);
SCSI_Close();
Ferror();
exit(1);
}
close(devd);
}
|