1 /* MTX -- SCSI Tape Attached Medium Changer Control Program
3 Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
5 $Date: 2002/02/05 16:51:11 $
8 This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
9 extracted from mtx.c, plus some additional routines.
11 This program is free software; you may redistribute and/or modify it under
12 the terms of the GNU General Public License Version 2 as published by the
13 Free Software Foundation.
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
17 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 /* FatalError: changed Feb. 2000 elg@estinc.com to eliminate a buffer
23 overflow :-(. That could be important if mtxl is SUID for some reason.
29 /* #define DEBUG_MODE_SENSE 1 */
31 /* #define DEBUG_SCSI */
33 /* zap the following define when we finally add real import/export support */
34 #define IMPORT_EXPORT_HACK 1 /* for the moment, import/export == storage */
36 /* First, do some SCSI routines: */
38 /* the camlib is used on FreeBSD. */
40 # include "scsi_freebsd.c"
43 /* the scsi_ctl interface is used on HP/UX. */
44 #if HAVE_SYS_SCSI_CTL_H
45 # include "scsi_hpux.c"
48 /* the 'sg' interface is used on Linux. */
50 # include "scsi_linux.c"
53 /* The 'uscsi' interface is used on Solaris. */
54 #if HAVE_SYS_SCSI_IMPL_USCSI_H
55 # include "scsi_sun.c"
58 /* The 'tm_buf' interface, as used on AIX. */
59 #ifdef HAVE_SYS_SCSI_H
60 # include "scsi_aix.c"
63 /* The 'dslib' interface is used on SGI. */
65 # include "scsi_sgi.c"
68 /* Hmm, dunno what to do about Digital Unix at the moment. */
75 #include "[.vms]scsi.c"
78 extern char *argv0; /* something to let us do good error messages. */
80 /* Now for some low-level SCSI stuff: */
82 Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense) {
86 Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));
88 CDB[0] = 0x12; /* INQUIRY */
89 CDB[1] = 0; /* EVPD = 0 */
90 CDB[2] = 0; /* Page Code */
91 CDB[3] = 0; /* Reserved */
92 CDB[4] = sizeof(Inquiry_T); /* Allocation Length */
93 CDB[5] = 0; /* Control */
95 /* set us a very short timeout, sigh... */
96 SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
98 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
99 Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
102 return NULL; /* sorry! */
108 int BigEndian16(unsigned char *BigEndianData)
110 return (BigEndianData[0] << 8) + BigEndianData[1];
114 int BigEndian24(unsigned char *BigEndianData)
116 return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
123 void FatalError(char *ErrorMessage, ...)
125 #define FORMAT_BUF_LEN 1024
126 char FormatBuffer[FORMAT_BUF_LEN];
128 char *TargetPointer = FormatBuffer;
129 int Character, LastCharacter = '\0';
132 va_list ArgumentPointer;
133 va_start(ArgumentPointer, ErrorMessage);
134 /* SourcePointer = "mtx: "; */
135 sprintf(TargetPointer,"%s: ",argv0);
136 numchars=strlen(TargetPointer);
138 /* while ((Character = *SourcePointer++) != '\0') { */
139 /* *TargetPointer++ = Character; */
144 while ((Character = *ErrorMessage++) != '\0')
145 if (LastCharacter == '%')
147 if (Character == 'm')
149 SourcePointer = strerror(errno);
150 while ((Character = *SourcePointer++) != '\0') {
151 *TargetPointer++ = Character;
153 if (numchars==FORMAT_BUF_LEN-1) break;
155 if (numchars==FORMAT_BUF_LEN-1) break; /* break outer loop... */
159 *TargetPointer++ = '%';
160 *TargetPointer++ = Character;
162 if (numchars==FORMAT_BUF_LEN-1) break;
164 LastCharacter = '\0';
168 if (Character != '%') {
169 *TargetPointer++ = Character;
171 if (numchars==FORMAT_BUF_LEN-1) break;
173 LastCharacter = Character;
175 *TargetPointer = '\0'; /* works even if we had to break from above... */
176 vfprintf(stderr, FormatBuffer, ArgumentPointer);
177 va_end(ArgumentPointer);
181 sys$exit(VMS_ExitCode);
185 /* This is a really slow and stupid 'bzero' implementation'... */
186 void slow_bzero(char *buffer, int numchars) {
187 while (numchars--) *buffer++ = 0;
191 /* malloc some memory while checking for out-of-memory conditions. */
192 void *xmalloc(size_t Size)
194 void *Result = (void *) malloc(Size);
196 FatalError("cannot allocate %d bytes of memory\n", Size);
200 /* malloc some memory, zeroing it too: */
201 void *xzmalloc(size_t Size)
203 void *Result=(void *)xmalloc(Size);
205 slow_bzero(Result,Size);
210 int min(int x, int y)
212 return (x < y ? x : y);
216 int max(int x, int y)
218 return (x > y ? x : y);
222 /* Routine to inventory the library. Needed by, e.g., some Breece Hill
223 * loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
224 * has no parameters, such as a range to scan :-(.
227 int Inventory(DEVICE_TYPE MediumChangerFD) {
229 RequestSense_T RequestSense;
231 /* okay, now for the command: */
233 CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
235 /* set us a very long timeout, sigh... */
236 SCSI_Set_Timeout(30*60); /* 30 minutes, sigh! */
238 if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
239 return -1; /* could not do! */
241 return 0; /* did do! */
244 /* Routine to send an UNLOAD from the SSC spec to a device. This can be
245 * used either to eject a tape (when sent to a tape drive), or to eject
246 * a magzine (on some Seagate changers, when sent to LUN 1 ).
249 int Eject(DEVICE_TYPE fd) {
251 RequestSense_T RequestSense;
252 /* okay, now for the command: */
255 CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
257 if (SCSI_ExecuteCommand(fd,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
258 return -1; /* could not do! */
260 return 0; /* did do! */
263 /* Routine to read the Mode Sense Element Address Assignment Page */
264 /* We try to read the page. If we can't read the page, we return NULL.
265 * Our caller really isn't too worried about why we could not read the
266 * page, it will simply default to some kind of default values.
268 ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD) {
270 ElementModeSense_T *retval; /* processed SCSI. */
271 unsigned char input_buffer[136];
272 ElementModeSensePage_T *sense_page; /* raw SCSI. */
273 RequestSense_T RequestSense; /* see what retval of raw SCSI was... */
275 /* okay, now for the command: */
276 CDB[0]=0x1a; /* Mode Sense(6) */
278 CDB[2]=0x1d; /* Mode Sense Element Address Assignment Page */
280 CDB[4]=136; /* allocation_length... */
283 /* clear the data buffer: */
284 slow_bzero((char *)&RequestSense,sizeof(RequestSense_T));
285 slow_bzero((char *)input_buffer,sizeof(input_buffer));
287 if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,
288 &input_buffer,sizeof(input_buffer),&RequestSense) != 0) {
289 #ifdef DEBUG_MODE_SENSE
290 fprintf(stderr,"Could not execute mode sense...\n");
293 return NULL; /* sorry, couldn't do it. */
296 /* Could do it, now build return value: */
298 #ifdef DEBUG_MODE_SENSE
301 for (i=0;i<30;i+=3) {
302 fprintf(stderr,"ib[%d]=%d ib[%d]=%d ib[%d]=%d\n",
303 i,input_buffer[i],i+1,input_buffer[i+1],
304 i+2,input_buffer[i+2]);
305 /* fprintf(stderr,"input_buffer[0]=%d input_buffer[3]=%d\n",
306 * input_buffer[0], input_buffer[3]);
312 /* first, skip past: mode data header, and block descriptor header if any */
313 sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
315 #ifdef DEBUG_MODE_SENSE
316 fprintf(stderr,"*sense_page=%x %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
320 retval=(ElementModeSense_T *) xzmalloc(sizeof(ElementModeSense_T));
322 /* Remember that all SCSI values are big-endian: */
323 retval->MediumTransportStart=(((int)sense_page->MediumTransportStartHi)<<8) +
324 sense_page->MediumTransportStartLo;
325 retval->NumMediumTransport=(((int)(sense_page->NumMediumTransportHi))<<8) +
326 sense_page->NumMediumTransportLo;
328 /* HACK! Some HP autochangers don't report NumMediumTransport right! */
329 /* ARG! TAKE OUT THE %#@!# HACK! */
330 #ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
331 if (!retval->NumMediumTransport) {
332 retval->NumMediumTransport=1;
336 #ifdef DEBUG_MODE_SENSE
337 fprintf(stderr,"rawNumStorage= %d %d rawNumImportExport= %d %d\n",
338 sense_page->NumStorageHi,sense_page->NumStorageLo,
339 sense_page->NumImportExportHi, sense_page->NumImportExportLo);
340 fprintf(stderr,"rawNumTransport=%d %d rawNumDataTransfer=%d %d\n",
341 sense_page->NumMediumTransportHi,sense_page->NumMediumTransportLo,
342 sense_page->NumDataTransferHi,sense_page->NumDataTransferLo);
346 retval->StorageStart=(((int)(sense_page->StorageStartHi))<<8) +
347 sense_page->StorageStartLo;
348 retval->NumStorage=(((int)(sense_page->NumStorageHi))<<8) +
349 sense_page->NumStorageLo;
350 retval->ImportExportStart=(((int)(sense_page->ImportExportStartHi))<<8)+
351 sense_page->ImportExportStartLo;
352 retval->NumImportExport=(((int)(sense_page->NumImportExportHi))<<8)+
353 sense_page->NumImportExportLo;
354 retval->DataTransferStart=(((int)(sense_page->DataTransferStartHi))<<8)+
355 sense_page->DataTransferStartLo;
356 retval->NumDataTransfer=(((int)(sense_page->NumDataTransferHi))<<8)+
357 sense_page->NumDataTransferLo;
359 /* allocate a couple spares 'cause some HP autochangers and maybe others
360 * don't properly report the robotics arm(s) count here...
362 retval->NumElements=retval->NumStorage+retval->NumImportExport+
363 retval->NumDataTransfer+retval->NumMediumTransport;
365 retval->MaxReadElementStatusData=(sizeof(ElementStatusDataHeader_T) +
366 4 * sizeof(ElementStatusPage_T) +
367 (retval->NumElements) *
368 sizeof(TransportElementDescriptor_T));
371 #ifdef IMPORT_EXPORT_HACK
372 retval->NumStorage=retval->NumStorage+retval->NumImportExport;
375 #ifdef DEBUG_MODE_SENSE
376 fprintf(stderr,"NumMediumTransport=%d\n",retval->NumMediumTransport);
377 fprintf(stderr,"NumStorage=%d\n",retval->NumStorage);
378 fprintf(stderr,"NumImportExport=%d\n",retval->NumImportExport);
379 fprintf(stderr,"NumDataTransfer=%d\n",retval->NumDataTransfer);
380 fprintf(stderr,"MaxReadElementStatusData=%d\n",retval->MaxReadElementStatusData);
381 fprintf(stderr,"NumElements=%d\n",retval->NumElements);
388 static void FreeElementData(ElementStatus_T *data)
390 free(data->DataTransferElementAddress);
391 free(data->DataTransferElementSourceStorageElementNumber);
392 free(data->DataTransferPrimaryVolumeTag);
393 free(data->DataTransferAlternateVolumeTag);
394 free(data->PrimaryVolumeTag);
395 free(data->AlternateVolumeTag);
396 free(data->StorageElementAddress);
397 free(data->StorageElementIsImportExport);
398 free(data->StorageElementFull);
399 free(data->DataTransferElementFull);
406 static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense) {
407 ElementStatus_T *retval;
409 retval=(ElementStatus_T *) xzmalloc(sizeof(ElementStatus_T));
411 /* now for the invidual component arrays.... */
413 retval->DataTransferElementAddress = (int *) xzmalloc(sizeof(int)*
414 (mode_sense->NumDataTransfer + 1));
415 retval->DataTransferElementSourceStorageElementNumber =
416 (int *) xzmalloc(sizeof(int)*(mode_sense->NumDataTransfer+1));
417 retval->DataTransferPrimaryVolumeTag=
418 (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
419 retval->DataTransferAlternateVolumeTag=
420 (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
421 retval->PrimaryVolumeTag=
422 (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
423 retval->AlternateVolumeTag=
424 (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
425 retval->StorageElementAddress=
426 (int *)xzmalloc(sizeof(int)*(mode_sense->NumStorage+1));
427 retval->StorageElementIsImportExport=
428 (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));
429 retval->StorageElementFull=
430 (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));
431 retval->DataTransferElementFull=
432 (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumDataTransfer+1));
434 return retval; /* sigh! */
438 void copy_barcode(unsigned char *src, unsigned char *dest) {
441 for (i=0; i < 36; i++) {
444 *dest=0; /* null-terminate, sigh. */
448 /* This #%!@# routine has more parameters than I can count! */
449 unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
450 RequestSense_T *RequestSense,
451 Inquiry_T *inquiry_info,
459 boolean is_attached = false;
461 unsigned char *DataBuffer; /* size of data... */
463 #ifdef HAVE_GET_ID_LUN
466 if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
469 if (flags->no_attached) { /* override, sigh */
473 DataBuffer=(unsigned char *) xmalloc(NumBytes+1);
475 slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
476 #ifdef HAVE_GET_ID_LUN
477 scsi_id = SCSI_GetIDLun(MediumChangerFD);
481 CDB[0] = 0xB8; /* READ ELEMENT STATUS */
483 CDB[0] = 0xB4; /* whoops, READ_ELEMENT_STATUS_ATTACHED! */
485 #ifdef HAVE_GET_ID_LUN
486 CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Lun + VolTag + Type code */
489 CDB[1] = ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Element Type Code = 0, VolTag = 1 */
491 CDB[2] = (ElementStart >> 8) & 0xff; /* Starting Element Address MSB */
492 CDB[3] = ElementStart & 0xff; /* Starting Element Address LSB */
495 CDB[4]= (NumElements >> 8) & 0xff; /* Number Of Elements MSB */
496 CDB[5]= NumElements & 0xff ; /* Number of elements LSB */
498 /* CDB[5]=127; */ /* test */
500 fprintf(stderr,"CDB[5]=%d\n" , CDB[5]);
502 CDB[6] = 0; /* Reserved */
504 CDB[7]= (NumBytes >> 16) & 0xff; /* allocation length MSB */
505 CDB[8]= (NumBytes >> 8) & 0xff;
506 CDB[9]= NumBytes & 0xff; /* allocation length LSB */
508 CDB[10] = 0; /* Reserved */
509 CDB[11] = 0; /* Control */
514 fprintf(stderr,"CDB= ");
516 fprintf(stderr,"%x ",CDB[i]);
518 fprintf(stderr,"\n");
523 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
524 DataBuffer,NumBytes, RequestSense) != 0){
525 /* okay, first see if we have sense key of 'illegal request',
526 additional sense code of '24', additional sense qualfier of
527 '0', and field in error of '4'. This means that we issued a request
528 w/bar code reader and did not have one, thus must re-issue the request
532 /* Okay, most autochangers and tape libraries set a sense key here if
533 * they do not have a bar code reader. For some reason, the ADIC DAT
534 * uses a different sense key? Let's retry w/o bar codes for *ALL*
535 * sense keys, sigh sigh sigh.
538 if ( RequestSense->SenseKey > 1 ) {
539 /* we issued a code requesting bar codes, there is no bar code reader? */
540 /* clear out our sense buffer first... */
541 slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
543 CDB[1] &= ~0x10; /* clear bar code flag! */
548 fprintf(stderr,"CDB= ");
550 fprintf(stderr,"%x ",CDB[i]);
552 fprintf(stderr,"\n");
557 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
558 DataBuffer, NumBytes, RequestSense) != 0){
569 /* print a bunch of extra debug data :-(. */
570 PrintRequestSense(RequestSense); /* see what it sez :-(. */
573 fprintf(stderr,"Data:");
575 fprint(stderr,"%02x ",DataBuffer[i]);
577 fprintf(stderr,"\n");
580 return DataBuffer; /* we succeeded! */
583 /******************* ParseElementStatus ***********************************/
584 /* This does the actual grunt work of parsing element status data. It fills
585 * in appropriate pieces of its input data. It may be called multiple times
586 * while we are gathering element status.
589 static void ParseElementStatus(int *EmptyStorageElementAddress,
590 int *EmptyStorageElementCount,
591 unsigned char *DataBuffer,
592 ElementStatus_T *ElementStatus,
593 ElementModeSense_T *mode_sense
597 unsigned char *DataPointer=DataBuffer;
598 TransportElementDescriptor_T TEBuf;
599 TransportElementDescriptor_T *TransportElementDescriptor;
600 ElementStatusDataHeader_T *ElementStatusDataHeader;
601 Element2StatusPage_T *ElementStatusPage;
602 Element2StatusPage_T ESBuf;
604 int TransportElementDescriptorLength;
607 ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataBuffer;
611 ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
612 DataPointer += sizeof(ElementStatusDataHeader_T);
614 BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
616 fprintf(stderr,"ElementCount=%d\n",ElementCount);
619 while (ElementCount > 0)
622 int got_element_num=0;
624 fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
628 memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
629 ElementStatusPage = &ESBuf;
630 DataPointer += sizeof(ElementStatusPage_T);
631 TransportElementDescriptorLength =
632 BigEndian16(ElementStatusPage->ElementDescriptorLength);
633 if (TransportElementDescriptorLength <
634 sizeof(TransportElementDescriptorShort_T)) {
636 /* Foo, Storage Element Descriptors can be 4 bytes?! */
637 if ( (ElementStatusPage->ElementTypeCode != MediumTransportElement &&
638 ElementStatusPage->ElementTypeCode != StorageElement &&
639 ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
640 TransportElementDescriptorLength < 4) {
642 fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
644 FatalError("Transport Element Descriptor Length too short: %d\n",TransportElementDescriptorLength);
649 fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
652 BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
653 if (BytesAvailable <= 0) {
656 while (BytesAvailable > 0)
658 /* TransportElementDescriptor =
659 (TransportElementDescriptor_T *) DataPointer; */
660 memcpy(&TEBuf, DataPointer,
661 (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
662 TransportElementDescriptorLength :
664 TransportElementDescriptor = &TEBuf;
666 DataPointer += TransportElementDescriptorLength;
667 BytesAvailable -= TransportElementDescriptorLength;
669 switch (ElementStatusPage->ElementTypeCode)
671 case MediumTransportElement:
672 ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
674 fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress);
677 /* we treat ImportExport elements as if they were normal
678 * storage elements now, sigh...
680 case ImportExportElement:
681 ElementStatus->ImportExportCount++;
683 fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
685 ElementStatus->StorageElementIsImportExport[ElementStatus->StorageElementCount] = 1;
686 /* WARNING: DO *NOT* PUT A 'break' STATEMENT HERE, it is supposed
687 * to run on into the case for a Storage Element!
691 fprintf(stderr,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
693 ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
694 BigEndian16(TransportElementDescriptor->ElementAddress);
695 ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
696 TransportElementDescriptor->Full;
698 fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
700 if (!TransportElementDescriptor->Full)
702 EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
703 ElementStatus->StorageElementCount; /* slot idx. */
704 /* ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
706 if ( (TransportElementDescriptorLength > 11) &&
707 (ElementStatusPage->VolBits & E2_AVOLTAG)) {
708 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
709 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
711 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */;
713 if ( (TransportElementDescriptorLength > 11) &&
714 (ElementStatusPage->VolBits & E2_PVOLTAG)) {
715 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
716 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
718 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
721 ElementStatus->StorageElementCount++;
722 /* Note that the original mtx had no check here for
723 buffer overflow, though some drives might mistakingly
726 if (ElementStatus->StorageElementCount > mode_sense->NumStorage) {
727 fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
728 mode_sense->NumStorage,
729 ElementStatus->StorageElementCount);
731 return; /* we're done :-(. */
738 case DataTransferElement:
739 /* tape drive not installed, go back to top of loop */
741 /* if (TransportElementDescriptor->Except) continue ; */
743 /* Note: This is for Exabyte tape libraries that improperly
744 report that they have a 2nd tape drive when they don't. We
745 could generalize this in an ideal world, but my attempt to
746 do so failed with dual-drive Exabyte tape libraries that
747 *DID* have the second drive. Sigh.
749 /* No longer need this test due to code restructuring? */
750 /* if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
751 TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
754 /* generalize it. Does it work? Let's try it! */
755 /* No, dammit, following does not work on dual-drive Exabyte
756 'cause if a tape is in the drive, it sets the AdditionalSense
757 code to something (sigh).
759 /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
765 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
766 BigEndian16(TransportElementDescriptor->ElementAddress);
767 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] =
768 TransportElementDescriptor->Full;
769 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
770 BigEndian16(TransportElementDescriptor
771 ->SourceStorageElementAddress);
773 if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
774 FatalError("Too many Data Transfer Elements Reported\n");
776 if (ElementStatusPage->VolBits & E2_PVOLTAG) {
777 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
778 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
780 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
782 if (ElementStatusPage->VolBits & E2_AVOLTAG) {
783 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
784 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
786 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
788 ElementStatus->DataTransferElementCount++;
789 /* 0 actually is a usable element address */
790 /* if (DataTransferElementAddress == 0) */
792 /* "illegal Data Transfer Element Address %d reported\n", */
793 /* DataTransferElementAddress); */
796 FatalError("illegal Element Type Code %d reported\n",
797 ElementStatusPage->ElementTypeCode);
803 /********************* Real ReadElementStatus ********************* */
806 * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
807 * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
808 * under Linux, which gets around the @#%@ 4k buffer size in Linux.
809 * We still have the restriction that Linux cuts off the last two
810 * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
811 * verbose widget won't work :-(.
813 * We now look for that "attached" bit in the inquiry_info to see whether
814 * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
815 * look at the device type in the inquiry_info to see whether it is a media
816 * changer or tape device, and if it's a media changer device, we ignore the
817 * attached bit (one beta tester found an old 4-tape DAT changer that set
818 * the attached bit for both the tape device AND the media changer device).
822 ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
823 ElementStatus_T *ElementStatus;
825 unsigned char *DataBuffer; /* size of data... */
827 int EmptyStorageElementCount=0;
828 int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
831 int invalid_sources=0;
832 boolean is_attached = false;
835 ElementModeSense_T *mode_sense = NULL;
837 if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
840 if (flags->no_attached) { /* override, sigh */
844 mode_sense=ReadAssignmentPage(MediumChangerFD);
847 mode_sense=(ElementModeSense_T *) xmalloc(sizeof(ElementModeSense_T));
848 mode_sense->NumMediumTransport=MAX_TRANSPORT_ELEMENTS;
849 mode_sense->NumStorage=MAX_STORAGE_ELEMENTS;
850 mode_sense->NumDataTransfer=MAX_TRANSFER_ELEMENTS;
851 mode_sense->MaxReadElementStatusData= (sizeof(ElementStatusDataHeader_T)
852 + 3 * sizeof(ElementStatusPage_T)
853 + (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS)
854 * sizeof(TransportElementDescriptor_T));
856 /* don't care about the others anyhow at the moment... */
859 ElementStatus=AllocateElementData(mode_sense);
861 /* Now to initialize it (sigh). */
862 ElementStatus->StorageElementCount = 0;
863 ElementStatus->DataTransferElementCount=0;
866 /* first, allocate some empty storage stuff: Note that we pass this
867 * down to ParseElementStatus (sigh!)
870 EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
871 for (i=0;i<mode_sense->NumStorage;i++) {
872 EmptyStorageElementAddress[i]=-1;
875 /* Okay, now to send some requests for the various types of stuff: */
877 /* -----------STORAGE ELEMENTS---------------- */
878 /* Let's start with storage elements: */
880 for (i=0;i<mode_sense->NumDataTransfer;i++) {
881 /* initialize them to an illegal # so that we can fix later... */
882 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
886 flags->elementtype=StorageElement; /* sigh! */
887 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
889 mode_sense->StorageStart,
890 /* adjust for import/export. */
891 mode_sense->NumStorage-mode_sense->NumImportExport,
892 mode_sense->MaxReadElementStatusData);
894 /* darn. Free up stuff and return. */
895 /****FIXME**** do a free on element data! */
896 FreeElementData(ElementStatus);
900 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
901 DataBuffer,ElementStatus,mode_sense);
903 free(DataBuffer); /* sigh! */
905 /* --------------IMPORT/EXPORT--------------- */
906 /* Next let's see if we need to do Import/Export: */
907 if (mode_sense->NumImportExport > 0) {
908 flags->elementtype=ImportExportElement;
909 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
911 mode_sense->ImportExportStart,
912 mode_sense->NumImportExport,
913 mode_sense->MaxReadElementStatusData);
916 /* darn. Free up stuff and return. */
917 /****FIXME**** do a free on element data! */
918 FreeElementData(ElementStatus);
921 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
922 DataBuffer,ElementStatus,mode_sense);
928 /* ----------------- DRIVES ---------------------- */
931 flags->elementtype=DataTransferElement; /* sigh! */
932 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
934 mode_sense->DataTransferStart,
935 mode_sense->NumDataTransfer,
936 mode_sense->MaxReadElementStatusData);
938 /* darn. Free up stuff and return. */
939 /****FIXME**** do a free on element data! */
940 FreeElementData(ElementStatus);
944 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
945 DataBuffer,ElementStatus,mode_sense);
947 free(DataBuffer); /* sigh! */
950 /* ----------------- Robot Arm(s) -------------------------- */
952 /* grr, damned brain dead HP doesn't report that it has any! */
953 if (!mode_sense->NumMediumTransport) {
954 ElementStatus->TransportElementAddress=0; /* default it sensibly :-(. */
956 flags->elementtype=MediumTransportElement; /* sigh! */
957 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
959 mode_sense->MediumTransportStart,
960 1, /* only get 1, sigh. */
961 mode_sense->MaxReadElementStatusData);
963 /* darn. Free up stuff and return. */
964 /****FIXME**** do a free on element data! */
965 FreeElementData(ElementStatus);
969 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
970 DataBuffer,ElementStatus,mode_sense);
972 free(DataBuffer); /* sigh! */
975 /*---------------------- Sanity Checking ------------------- */
977 if (!ElementStatus->DataTransferElementCount)
978 FatalError("no Data Transfer Element reported\n");
979 if (ElementStatus->StorageElementCount == 0)
980 FatalError("no Storage Elements reported\n");
983 /* ---------------------- Reset SourceStorageElementNumbers ------- */
985 /* okay, re-write the SourceStorageElementNumber code *AGAIN*.
987 * Translate from raw element # to our translated # (if possible).
988 * First, check the SourceStorageElementNumbers against the list of
989 * filled slots. If the slots indicated are empty, we accept that list as
990 * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
994 * If we had some invalid (or unknown) SourceStorageElementNumbers
995 * then we must search for free slots, and assign SourceStorageElementNumbers
996 * to those free slots. We happen to already built a list of free
997 * slots as part of the process of reading the storage element numbers
998 * from the tape. So that's easy enough to do!
1001 #ifdef DEBUG_TAPELIST
1002 fprintf(stderr,"empty slots: %d\n",EmptyStorageElementCount);
1003 if (EmptyStorageElementCount) {
1004 for (i=0; i<EmptyStorageElementCount; i++) {
1005 fprintf(stderr,"empty: %d\n",EmptyStorageElementAddress[i]);
1010 /* Okay, now we re-assign origin slots if the "real" origin slot
1011 * is obviously defective:
1014 invalid_sources=0; /* no invalid sources yet! */
1015 for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1017 int translated_elnum = -1;
1018 /* if we have an element, then ... */
1019 if (ElementStatus->DataTransferElementFull[i]) {
1020 elnum=ElementStatus->DataTransferElementSourceStorageElementNumber[i];
1021 /* if we have an element number, then ... */
1023 /* Now to translate the elnum: */
1024 for (j=0; j<ElementStatus->StorageElementCount; j++) {
1025 if (elnum == ElementStatus->StorageElementAddress[j]) {
1029 /* now see if the element # is already occupied: */
1030 if (ElementStatus->StorageElementFull[translated_elnum]) {
1032 break; /* break out of the loop! */
1034 /* properly set the source... */
1035 ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1040 /* if element # was not >=0, then we had an invalid source anyhow! */
1046 #ifdef DEBUG_TAPELIST
1047 fprintf(stderr,"invalid_sources=%d\n",invalid_sources);
1051 /* okay, we have invalid sources, so let's see what they should be: */
1052 /* Note: If EmptyStorageElementCount is < # of drives, the leftover
1053 * drives will be assigned a -1 (see the initialization loop for
1054 * EmptyStorageElementAddress above), which will be reported as "slot 0"
1055 * by the user interface. This is an invalid value, but more useful for us
1056 * to have than just crapping out here :-(.
1058 if (invalid_sources) {
1060 for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1061 if (ElementStatus->DataTransferElementFull[i]) {
1062 #ifdef DEBUG_TAPELIST
1063 fprintf(stderr,"for drive %d, changing source %d to %d (empty slot #%d)\n",
1065 ElementStatus->DataTransferElementSourceStorageElementNumber[i],
1066 EmptyStorageElementAddress[empty_idx],
1070 ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1071 EmptyStorageElementAddress[empty_idx++];
1078 free(EmptyStorageElementAddress);
1079 return ElementStatus;
1083 /*************************************************************************/
1087 /* Now the actual media movement routine! */
1088 RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
1089 int DestinationAddress,
1090 ElementStatus_T *ElementStatus,
1091 Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1093 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1095 if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) { /* if using the ATTACHED API */
1096 CDB[0]=0xA7; /* MOVE_MEDIUM_ATTACHED */
1098 CDB[0] = 0xA5; /* MOVE MEDIUM */
1100 CDB[1] = 0; /* Reserved */
1101 CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF; /* Transport Element Address MSB */
1102 CDB[3] = (ElementStatus->TransportElementAddress) & 0xff; /* Transport Element Address LSB */
1103 CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
1104 CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
1105 CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
1106 CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
1107 CDB[8] = 0; /* Reserved */
1108 CDB[9] = 0; /* Reserved */
1109 if (flags->invert) {
1110 CDB[10] = 1; /* Reserved */
1114 /* eepos controls the tray for import/export elements, sometimes. */
1115 CDB[11] = 0 | (flags->eepos <<6); /* Control */
1117 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1118 NULL, 0, RequestSense) != 0) {
1119 return RequestSense;
1122 return NULL; /* success! */
1125 /* for Linux, this creates a way to do a short erase... the @#$%@ st.c
1126 * driver defaults to doing a long erase!
1129 RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD) {
1130 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1134 CDB[1]=0; /* Short! */
1135 CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
1137 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
1138 NULL, 0, RequestSense) != 0) {
1139 return RequestSense;
1142 return NULL; /* Success! */
1146 #ifdef LONG_PRINT_REQUEST_SENSE
1148 static char *sense_keys[] = {
1149 "No Sense", /* 00 */
1150 "Recovered Error", /* 01 */
1151 "Not Ready", /* 02 */
1152 "Medium Error", /* 03 */
1153 "Hardware Error", /* 04 */
1154 "Illegal Request", /* 05 */
1155 "Unit Attention", /* 06 */
1156 "Data Protect", /* 07 */
1157 "Blank Check", /* 08 */
1160 "Aborted Command", /* 0b */
1162 "Volume Overflow", /* 0d */
1163 "Miscompare", /* 0e */
1167 static char Yes[]="yes";
1168 static char No[]="no";
1170 void PrintRequestSense(RequestSense_T *RequestSense)
1174 fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
1175 fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
1176 if (RequestSense->ErrorCode==0x70) {
1178 } else if (RequestSense->ErrorCode==0x71) {
1183 fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n",RequestSense->ErrorCode,msg);
1184 fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n",sense_keys[RequestSense->SenseKey]);
1185 fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
1186 fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
1187 fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
1188 if (RequestSense->Valid) {
1189 fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
1191 fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
1192 fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
1193 if (RequestSense->SKSV) {
1194 fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n",RequestSense->BitPointer);
1196 fprintf(stderr,"mtx: Request Sense: BPV=%s\n",RequestSense->BPV ? Yes : No);
1197 fprintf(stderr,"mtx: Request Sense: Error in CDB=%s\n",RequestSense->CommandData ? Yes : No);
1198 fprintf(stderr,"mtx: Request Sense: SKSV=%s\n",RequestSense->SKSV ? Yes : No);
1200 if (RequestSense->BPV || RequestSense -> SKSV) {
1201 fprintf(stderr,"mtx: Request Sense: Field Pointer = %02X %02X\n",RequestSense->FieldData[0],RequestSense->FieldData[1]);
1207 void PrintRequestSense(RequestSense_T *RequestSense)
1210 fprintf(stderr, "mtx: Request Sense: %02X",
1211 ((unsigned char *) RequestSense)[0]);
1212 for (i = 1; i < sizeof(RequestSense_T); i++)
1213 fprintf(stderr, " %02X", ((unsigned char *) RequestSense)[i]);
1214 fprintf(stderr, "\n");
1219 /* $Date: 2002/02/05 16:51:11 $
1221 * Revision 1.8.2.3 2002/02/05 16:51:11 elgreen
1224 * Revision 1.8.2.2 2002/01/22 16:27:47 elgreen
1225 * Handle too-big transport element descriptor lengths
1227 * Revision 1.8.2.1 2002/01/17 22:24:35 elgreen
1228 * Handle ADIC silliness of saying that it has 1 import/export element whose
1229 * descriptor is 0 bytes in length
1231 * Revision 1.8 2001/06/25 23:06:22 elgreen
1232 * Readying this for 1.2.13 release
1234 * Revision 1.7 2001/06/25 04:56:35 elgreen
1235 * Kai to the rescue *again* :-)
1237 * Revision 1.6 2001/06/24 07:02:01 elgreen
1238 * Kai's fix was better than mine.
1240 * Revision 1.5 2001/06/24 06:59:19 elgreen
1241 * Kai found bug in the barcode backoff.
1243 * Revision 1.4 2001/06/15 18:56:54 elgreen
1244 * Arg, it doesn't help to check for 0 robot arms if you force it to 1!
1246 * Revision 1.3 2001/06/15 14:26:09 elgreen
1247 * Fixed brain-dead case of HP loaders that report they have no robot arms.
1249 * Revision 1.2 2001/06/09 17:26:26 elgreen
1250 * Added 'nobarcode' command to mtx (to skip the initial request asking for
1251 * barcodes for mtx status purposes).
1253 * Revision 1.1.1.1 2001/06/05 17:10:25 elgreen
1254 * Initial import into SourceForge
1256 * Revision 1.29 2001/05/01 01:39:23 eric
1257 * Remove the Exabyte special case code, which seemed to be barfing me :-(.
1259 * Revision 1.28 2001/04/28 00:03:10 eric
1260 * update for 1.2.12.
1262 * Revision 1.27 2001/04/18 19:27:39 eric
1263 * whoops, move ImportExport parser into the $#%!@ ImportExport test :-(.
1265 * Revision 1.26 2001/04/18 18:55:44 eric
1266 * turn too many storage elements into a warning rather than a fatal error (sigh!)
1268 * Revision 1.25 2001/04/18 18:46:54 eric
1269 * $%!@# "C" precedence rules.
1271 * Revision 1.24 2001/04/18 17:58:04 eric
1272 * initialize EmptyStorageElementCount (sigh)
1274 * Revision 1.23 2001/04/18 16:55:03 eric
1275 * hopefully get the initialization for element status straigtened out.
1277 * Revision 1.22 2001/04/18 16:32:59 eric
1278 * Cleaned up all -Wall messages.
1280 * Revision 1.21 2001/04/18 16:08:08 eric
1281 * after re-arranging & fixing bugs
1283 * Revision 1.20 2001/04/17 21:28:43 eric
1286 * Revision 1.19 2001/04/02 20:12:48 eric
1287 * checkin, mtx 1.2.11pre6
1289 * Revision 1.18 2001/03/06 01:37:05 eric
1292 * Revision 1.17 2001/02/26 20:56:12 eric
1293 * added debugging, removed Makefile
1295 * Revision 1.16 2001/02/19 23:22:28 eric
1298 * Revision 1.15 2001/02/19 23:02:04 eric
1299 * Apply SPARC patch.
1301 * Revision 1.14 2001/01/26 15:58:10 eric
1302 * Make work on solaris?
1304 * Revision 1.13 2001/01/17 19:39:01 eric
1305 * Changed mtx.h and mtxl.c to use autoconf stuff to figure out which SCSI
1306 * routines to use (this way, if we ever port to an OS that has an identical
1307 * SCSI interface to one of the supported OS's, we don't have to do anything
1310 * Revision 1.12 2001/01/17 16:36:26 eric
1311 * added date & revision strings as needed
1313 * Revision 1.11 2000/11/30 20:35:18 eric
1314 * Added Erase command.