1 /* MTX -- SCSI Tape Attached Medium Changer Control Program
3 Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
5 $Date: 2002/09/27 17:23:28 $
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++) {
443 #ifdef __WEIRD_CHAR_SUPPRESS
444 if ((*dest < 32) || (*dest > 127))
449 dest=0; /* null-terminate, sigh. */
453 /* This #%!@# routine has more parameters than I can count! */
454 unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
455 RequestSense_T *RequestSense,
456 Inquiry_T *inquiry_info,
464 boolean is_attached = false;
466 unsigned char *DataBuffer; /* size of data... */
468 #ifdef HAVE_GET_ID_LUN
471 if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
474 if (flags->no_attached) { /* override, sigh */
478 DataBuffer=(unsigned char *) xmalloc(NumBytes+1);
480 slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
481 #ifdef HAVE_GET_ID_LUN
482 scsi_id = SCSI_GetIDLun(MediumChangerFD);
486 CDB[0] = 0xB8; /* READ ELEMENT STATUS */
488 CDB[0] = 0xB4; /* whoops, READ_ELEMENT_STATUS_ATTACHED! */
490 #ifdef HAVE_GET_ID_LUN
491 CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Lun + VolTag + Type code */
494 CDB[1] = ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Element Type Code = 0, VolTag = 1 */
496 CDB[2] = (ElementStart >> 8) & 0xff; /* Starting Element Address MSB */
497 CDB[3] = ElementStart & 0xff; /* Starting Element Address LSB */
500 CDB[4]= (NumElements >> 8) & 0xff; /* Number Of Elements MSB */
501 CDB[5]= NumElements & 0xff ; /* Number of elements LSB */
503 /* CDB[5]=127; */ /* test */
505 fprintf(stderr,"CDB[5]=%d\n" , CDB[5]);
507 CDB[6] = 0; /* Reserved */
509 CDB[7]= (NumBytes >> 16) & 0xff; /* allocation length MSB */
510 CDB[8]= (NumBytes >> 8) & 0xff;
511 CDB[9]= NumBytes & 0xff; /* allocation length LSB */
513 CDB[10] = 0; /* Reserved */
514 CDB[11] = 0; /* Control */
519 fprintf(stderr,"CDB= ");
521 fprintf(stderr,"%x ",CDB[i]);
523 fprintf(stderr,"\n");
528 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
529 DataBuffer,NumBytes, RequestSense) != 0){
530 /* okay, first see if we have sense key of 'illegal request',
531 additional sense code of '24', additional sense qualfier of
532 '0', and field in error of '4'. This means that we issued a request
533 w/bar code reader and did not have one, thus must re-issue the request
537 /* Okay, most autochangers and tape libraries set a sense key here if
538 * they do not have a bar code reader. For some reason, the ADIC DAT
539 * uses a different sense key? Let's retry w/o bar codes for *ALL*
540 * sense keys, sigh sigh sigh.
543 if ( RequestSense->SenseKey > 1 ) {
544 /* we issued a code requesting bar codes, there is no bar code reader? */
545 /* clear out our sense buffer first... */
546 slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
548 CDB[1] &= ~0x10; /* clear bar code flag! */
553 fprintf(stderr,"CDB= ");
555 fprintf(stderr,"%x ",CDB[i]);
557 fprintf(stderr,"\n");
562 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
563 DataBuffer, NumBytes, RequestSense) != 0){
574 /* print a bunch of extra debug data :-(. */
575 PrintRequestSense(RequestSense); /* see what it sez :-(. */
578 fprintf(stderr,"Data:");
580 fprint(stderr,"%02x ",DataBuffer[i]);
582 fprintf(stderr,"\n");
585 return DataBuffer; /* we succeeded! */
588 /******************* ParseElementStatus ***********************************/
589 /* This does the actual grunt work of parsing element status data. It fills
590 * in appropriate pieces of its input data. It may be called multiple times
591 * while we are gathering element status.
594 static void ParseElementStatus(int *EmptyStorageElementAddress,
595 int *EmptyStorageElementCount,
596 unsigned char *DataBuffer,
597 ElementStatus_T *ElementStatus,
598 ElementModeSense_T *mode_sense
602 unsigned char *DataPointer=DataBuffer;
603 TransportElementDescriptor_T TEBuf;
604 TransportElementDescriptor_T *TransportElementDescriptor;
605 ElementStatusDataHeader_T *ElementStatusDataHeader;
606 Element2StatusPage_T *ElementStatusPage;
607 Element2StatusPage_T ESBuf;
609 int TransportElementDescriptorLength;
612 ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataBuffer;
616 ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
617 DataPointer += sizeof(ElementStatusDataHeader_T);
619 BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
621 fprintf(stderr,"ElementCount=%d\n",ElementCount);
624 while (ElementCount > 0)
627 int got_element_num=0;
629 fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
633 memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
634 ElementStatusPage = &ESBuf;
635 DataPointer += sizeof(ElementStatusPage_T);
636 TransportElementDescriptorLength =
637 BigEndian16(ElementStatusPage->ElementDescriptorLength);
638 if (TransportElementDescriptorLength <
639 sizeof(TransportElementDescriptorShort_T)) {
641 /* Foo, Storage Element Descriptors can be 4 bytes?! */
642 if ( (ElementStatusPage->ElementTypeCode != MediumTransportElement &&
643 ElementStatusPage->ElementTypeCode != StorageElement &&
644 ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
645 TransportElementDescriptorLength < 4) {
647 fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
649 FatalError("Transport Element Descriptor Length too short: %d\n",TransportElementDescriptorLength);
654 fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
657 BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
658 if (BytesAvailable <= 0) {
661 while (BytesAvailable > 0)
663 /* TransportElementDescriptor =
664 (TransportElementDescriptor_T *) DataPointer; */
665 memcpy(&TEBuf, DataPointer,
666 (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
667 TransportElementDescriptorLength :
669 TransportElementDescriptor = &TEBuf;
671 DataPointer += TransportElementDescriptorLength;
672 BytesAvailable -= TransportElementDescriptorLength;
674 switch (ElementStatusPage->ElementTypeCode)
676 case MediumTransportElement:
677 ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
679 fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress);
682 /* we treat ImportExport elements as if they were normal
683 * storage elements now, sigh...
685 case ImportExportElement:
686 ElementStatus->ImportExportCount++;
688 fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
690 ElementStatus->StorageElementIsImportExport[ElementStatus->StorageElementCount] = 1;
691 /* WARNING: DO *NOT* PUT A 'break' STATEMENT HERE, it is supposed
692 * to run on into the case for a Storage Element!
696 fprintf(stderr,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
698 ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
699 BigEndian16(TransportElementDescriptor->ElementAddress);
700 ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
701 TransportElementDescriptor->Full;
703 fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
705 if (!TransportElementDescriptor->Full)
707 EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
708 ElementStatus->StorageElementCount; /* slot idx. */
709 /* ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
711 if ( (TransportElementDescriptorLength > 11) &&
712 (ElementStatusPage->VolBits & E2_AVOLTAG)) {
713 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
714 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
716 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */;
718 if ( (TransportElementDescriptorLength > 11) &&
719 (ElementStatusPage->VolBits & E2_PVOLTAG)) {
720 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
721 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
723 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
726 ElementStatus->StorageElementCount++;
727 /* Note that the original mtx had no check here for
728 buffer overflow, though some drives might mistakingly
731 if (ElementStatus->StorageElementCount > mode_sense->NumStorage) {
732 fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
733 mode_sense->NumStorage,
734 ElementStatus->StorageElementCount);
736 return; /* we're done :-(. */
743 case DataTransferElement:
744 /* tape drive not installed, go back to top of loop */
746 /* if (TransportElementDescriptor->Except) continue ; */
748 /* Note: This is for Exabyte tape libraries that improperly
749 report that they have a 2nd tape drive when they don't. We
750 could generalize this in an ideal world, but my attempt to
751 do so failed with dual-drive Exabyte tape libraries that
752 *DID* have the second drive. Sigh.
754 /* No longer need this test due to code restructuring? */
755 /* if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
756 TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
759 /* generalize it. Does it work? Let's try it! */
760 /* No, dammit, following does not work on dual-drive Exabyte
761 'cause if a tape is in the drive, it sets the AdditionalSense
762 code to something (sigh).
764 /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
770 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
771 BigEndian16(TransportElementDescriptor->ElementAddress);
772 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] =
773 TransportElementDescriptor->Full;
774 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
775 BigEndian16(TransportElementDescriptor
776 ->SourceStorageElementAddress);
778 if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
779 FatalError("Too many Data Transfer Elements Reported\n");
781 if (ElementStatusPage->VolBits & E2_PVOLTAG) {
782 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
783 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
785 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
787 if (ElementStatusPage->VolBits & E2_AVOLTAG) {
788 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
789 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
791 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
793 ElementStatus->DataTransferElementCount++;
794 /* 0 actually is a usable element address */
795 /* if (DataTransferElementAddress == 0) */
797 /* "illegal Data Transfer Element Address %d reported\n", */
798 /* DataTransferElementAddress); */
801 FatalError("illegal Element Type Code %d reported\n",
802 ElementStatusPage->ElementTypeCode);
808 /********************* Real ReadElementStatus ********************* */
811 * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
812 * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
813 * under Linux, which gets around the @#%@ 4k buffer size in Linux.
814 * We still have the restriction that Linux cuts off the last two
815 * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
816 * verbose widget won't work :-(.
818 * We now look for that "attached" bit in the inquiry_info to see whether
819 * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
820 * look at the device type in the inquiry_info to see whether it is a media
821 * changer or tape device, and if it's a media changer device, we ignore the
822 * attached bit (one beta tester found an old 4-tape DAT changer that set
823 * the attached bit for both the tape device AND the media changer device).
827 ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
828 ElementStatus_T *ElementStatus;
830 unsigned char *DataBuffer; /* size of data... */
832 int EmptyStorageElementCount=0;
833 int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
836 int invalid_sources=0;
837 boolean is_attached = false;
840 ElementModeSense_T *mode_sense = NULL;
842 if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
845 if (flags->no_attached) { /* override, sigh */
849 mode_sense=ReadAssignmentPage(MediumChangerFD);
852 mode_sense=(ElementModeSense_T *) xmalloc(sizeof(ElementModeSense_T));
853 mode_sense->NumMediumTransport=MAX_TRANSPORT_ELEMENTS;
854 mode_sense->NumStorage=MAX_STORAGE_ELEMENTS;
855 mode_sense->NumDataTransfer=MAX_TRANSFER_ELEMENTS;
856 mode_sense->MaxReadElementStatusData= (sizeof(ElementStatusDataHeader_T)
857 + 3 * sizeof(ElementStatusPage_T)
858 + (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS)
859 * sizeof(TransportElementDescriptor_T));
861 /* don't care about the others anyhow at the moment... */
864 ElementStatus=AllocateElementData(mode_sense);
866 /* Now to initialize it (sigh). */
867 ElementStatus->StorageElementCount = 0;
868 ElementStatus->DataTransferElementCount=0;
871 /* first, allocate some empty storage stuff: Note that we pass this
872 * down to ParseElementStatus (sigh!)
875 EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
876 for (i=0;i<mode_sense->NumStorage;i++) {
877 EmptyStorageElementAddress[i]=-1;
880 /* Okay, now to send some requests for the various types of stuff: */
882 /* -----------STORAGE ELEMENTS---------------- */
883 /* Let's start with storage elements: */
885 for (i=0;i<mode_sense->NumDataTransfer;i++) {
886 /* initialize them to an illegal # so that we can fix later... */
887 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
891 flags->elementtype=StorageElement; /* sigh! */
892 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
894 mode_sense->StorageStart,
895 /* adjust for import/export. */
896 mode_sense->NumStorage-mode_sense->NumImportExport,
897 mode_sense->MaxReadElementStatusData);
899 /* darn. Free up stuff and return. */
900 /****FIXME**** do a free on element data! */
901 FreeElementData(ElementStatus);
905 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
906 DataBuffer,ElementStatus,mode_sense);
908 free(DataBuffer); /* sigh! */
910 /* --------------IMPORT/EXPORT--------------- */
911 /* Next let's see if we need to do Import/Export: */
912 if (mode_sense->NumImportExport > 0) {
913 flags->elementtype=ImportExportElement;
914 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
916 mode_sense->ImportExportStart,
917 mode_sense->NumImportExport,
918 mode_sense->MaxReadElementStatusData);
921 /* darn. Free up stuff and return. */
922 /****FIXME**** do a free on element data! */
923 FreeElementData(ElementStatus);
926 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
927 DataBuffer,ElementStatus,mode_sense);
933 /* ----------------- DRIVES ---------------------- */
936 flags->elementtype=DataTransferElement; /* sigh! */
937 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
939 mode_sense->DataTransferStart,
940 mode_sense->NumDataTransfer,
941 mode_sense->MaxReadElementStatusData);
943 /* darn. Free up stuff and return. */
944 /****FIXME**** do a free on element data! */
945 FreeElementData(ElementStatus);
949 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
950 DataBuffer,ElementStatus,mode_sense);
952 free(DataBuffer); /* sigh! */
955 /* ----------------- Robot Arm(s) -------------------------- */
957 /* grr, damned brain dead HP doesn't report that it has any! */
958 if (!mode_sense->NumMediumTransport) {
959 ElementStatus->TransportElementAddress=0; /* default it sensibly :-(. */
961 flags->elementtype=MediumTransportElement; /* sigh! */
962 DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
964 mode_sense->MediumTransportStart,
965 1, /* only get 1, sigh. */
966 mode_sense->MaxReadElementStatusData);
968 /* darn. Free up stuff and return. */
969 /****FIXME**** do a free on element data! */
970 FreeElementData(ElementStatus);
974 ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
975 DataBuffer,ElementStatus,mode_sense);
977 free(DataBuffer); /* sigh! */
980 /*---------------------- Sanity Checking ------------------- */
982 if (!ElementStatus->DataTransferElementCount)
983 FatalError("no Data Transfer Element reported\n");
984 if (ElementStatus->StorageElementCount == 0)
985 FatalError("no Storage Elements reported\n");
988 /* ---------------------- Reset SourceStorageElementNumbers ------- */
990 /* okay, re-write the SourceStorageElementNumber code *AGAIN*.
992 * Translate from raw element # to our translated # (if possible).
993 * First, check the SourceStorageElementNumbers against the list of
994 * filled slots. If the slots indicated are empty, we accept that list as
995 * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
999 * If we had some invalid (or unknown) SourceStorageElementNumbers
1000 * then we must search for free slots, and assign SourceStorageElementNumbers
1001 * to those free slots. We happen to already built a list of free
1002 * slots as part of the process of reading the storage element numbers
1003 * from the tape. So that's easy enough to do!
1006 #ifdef DEBUG_TAPELIST
1007 fprintf(stderr,"empty slots: %d\n",EmptyStorageElementCount);
1008 if (EmptyStorageElementCount) {
1009 for (i=0; i<EmptyStorageElementCount; i++) {
1010 fprintf(stderr,"empty: %d\n",EmptyStorageElementAddress[i]);
1015 /* Okay, now we re-assign origin slots if the "real" origin slot
1016 * is obviously defective:
1019 invalid_sources=0; /* no invalid sources yet! */
1020 for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1022 int translated_elnum = -1;
1023 /* if we have an element, then ... */
1024 if (ElementStatus->DataTransferElementFull[i]) {
1025 elnum=ElementStatus->DataTransferElementSourceStorageElementNumber[i];
1026 /* if we have an element number, then ... */
1028 /* Now to translate the elnum: */
1029 for (j=0; j<ElementStatus->StorageElementCount; j++) {
1030 if (elnum == ElementStatus->StorageElementAddress[j]) {
1034 /* now see if the element # is already occupied: */
1035 if (ElementStatus->StorageElementFull[translated_elnum]) {
1037 break; /* break out of the loop! */
1039 /* properly set the source... */
1040 ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1045 /* if element # was not >=0, then we had an invalid source anyhow! */
1051 #ifdef DEBUG_TAPELIST
1052 fprintf(stderr,"invalid_sources=%d\n",invalid_sources);
1056 /* okay, we have invalid sources, so let's see what they should be: */
1057 /* Note: If EmptyStorageElementCount is < # of drives, the leftover
1058 * drives will be assigned a -1 (see the initialization loop for
1059 * EmptyStorageElementAddress above), which will be reported as "slot 0"
1060 * by the user interface. This is an invalid value, but more useful for us
1061 * to have than just crapping out here :-(.
1063 if (invalid_sources) {
1065 for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1066 if (ElementStatus->DataTransferElementFull[i]) {
1067 #ifdef DEBUG_TAPELIST
1068 fprintf(stderr,"for drive %d, changing source %d to %d (empty slot #%d)\n",
1070 ElementStatus->DataTransferElementSourceStorageElementNumber[i],
1071 EmptyStorageElementAddress[empty_idx],
1075 ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1076 EmptyStorageElementAddress[empty_idx++];
1083 free(EmptyStorageElementAddress);
1084 return ElementStatus;
1088 /*************************************************************************/
1092 /* Now the actual media movement routine! */
1093 RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
1094 int DestinationAddress,
1095 ElementStatus_T *ElementStatus,
1096 Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1098 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1100 if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) { /* if using the ATTACHED API */
1101 CDB[0]=0xA7; /* MOVE_MEDIUM_ATTACHED */
1103 CDB[0] = 0xA5; /* MOVE MEDIUM */
1105 CDB[1] = 0; /* Reserved */
1106 CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF; /* Transport Element Address MSB */
1107 CDB[3] = (ElementStatus->TransportElementAddress) & 0xff; /* Transport Element Address LSB */
1108 CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
1109 CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
1110 CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
1111 CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
1112 CDB[8] = 0; /* Reserved */
1113 CDB[9] = 0; /* Reserved */
1114 if (flags->invert) {
1115 CDB[10] = 1; /* Reserved */
1119 /* eepos controls the tray for import/export elements, sometimes. */
1120 CDB[11] = 0 | (flags->eepos <<6); /* Control */
1122 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1123 NULL, 0, RequestSense) != 0) {
1124 return RequestSense;
1127 return NULL; /* success! */
1130 /* for Linux, this creates a way to do a short erase... the @#$%@ st.c
1131 * driver defaults to doing a long erase!
1134 RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD) {
1135 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1139 CDB[1]=0; /* Short! */
1140 CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
1142 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
1143 NULL, 0, RequestSense) != 0) {
1144 return RequestSense;
1147 return NULL; /* Success! */
1151 #ifdef LONG_PRINT_REQUEST_SENSE
1153 static char *sense_keys[] = {
1154 "No Sense", /* 00 */
1155 "Recovered Error", /* 01 */
1156 "Not Ready", /* 02 */
1157 "Medium Error", /* 03 */
1158 "Hardware Error", /* 04 */
1159 "Illegal Request", /* 05 */
1160 "Unit Attention", /* 06 */
1161 "Data Protect", /* 07 */
1162 "Blank Check", /* 08 */
1165 "Aborted Command", /* 0b */
1167 "Volume Overflow", /* 0d */
1168 "Miscompare", /* 0e */
1172 static char Yes[]="yes";
1173 static char No[]="no";
1175 void PrintRequestSense(RequestSense_T *RequestSense)
1179 fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
1180 fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
1181 if (RequestSense->ErrorCode==0x70) {
1183 } else if (RequestSense->ErrorCode==0x71) {
1188 fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n",RequestSense->ErrorCode,msg);
1189 fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n",sense_keys[RequestSense->SenseKey]);
1190 fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
1191 fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
1192 fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
1193 if (RequestSense->Valid) {
1194 fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
1196 fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
1197 fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
1198 if (RequestSense->SKSV) {
1199 fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n",RequestSense->BitPointer);
1201 fprintf(stderr,"mtx: Request Sense: BPV=%s\n",RequestSense->BPV ? Yes : No);
1202 fprintf(stderr,"mtx: Request Sense: Error in CDB=%s\n",RequestSense->CommandData ? Yes : No);
1203 fprintf(stderr,"mtx: Request Sense: SKSV=%s\n",RequestSense->SKSV ? Yes : No);
1205 if (RequestSense->BPV || RequestSense -> SKSV) {
1206 fprintf(stderr,"mtx: Request Sense: Field Pointer = %02X %02X\n",RequestSense->FieldData[0],RequestSense->FieldData[1]);
1212 void PrintRequestSense(RequestSense_T *RequestSense)
1215 fprintf(stderr, "mtx: Request Sense: %02X",
1216 ((unsigned char *) RequestSense)[0]);
1217 for (i = 1; i < sizeof(RequestSense_T); i++)
1218 fprintf(stderr, " %02X", ((unsigned char *) RequestSense)[i]);
1219 fprintf(stderr, "\n");
1224 /* $Date: 2002/09/27 17:23:28 $
1226 * Revision 1.8.2.7 2002/09/27 17:23:28 elgreen
1227 * Don't dereference pointer in barcode stuff
1229 * Revision 1.8.2.6 2002/09/27 17:20:18 elgreen
1230 * doh, get rid of the + from the patch process!
1232 * Revision 1.8.2.5 2002/09/27 17:17:13 elgreen
1235 * Revision 1.8.2.4 2002/09/27 16:49:43 elgreen
1236 * copy_barcode was off by one (sigh!)
1238 * Revision 1.8.2.3 2002/02/05 16:51:11 elgreen
1241 * Revision 1.8.2.2 2002/01/22 16:27:47 elgreen
1242 * Handle too-big transport element descriptor lengths
1244 * Revision 1.8.2.1 2002/01/17 22:24:35 elgreen
1245 * Handle ADIC silliness of saying that it has 1 import/export element whose
1246 * descriptor is 0 bytes in length
1248 * Revision 1.8 2001/06/25 23:06:22 elgreen
1249 * Readying this for 1.2.13 release
1251 * Revision 1.7 2001/06/25 04:56:35 elgreen
1252 * Kai to the rescue *again* :-)
1254 * Revision 1.6 2001/06/24 07:02:01 elgreen
1255 * Kai's fix was better than mine.
1257 * Revision 1.5 2001/06/24 06:59:19 elgreen
1258 * Kai found bug in the barcode backoff.
1260 * Revision 1.4 2001/06/15 18:56:54 elgreen
1261 * Arg, it doesn't help to check for 0 robot arms if you force it to 1!
1263 * Revision 1.3 2001/06/15 14:26:09 elgreen
1264 * Fixed brain-dead case of HP loaders that report they have no robot arms.
1266 * Revision 1.2 2001/06/09 17:26:26 elgreen
1267 * Added 'nobarcode' command to mtx (to skip the initial request asking for
1268 * barcodes for mtx status purposes).
1270 * Revision 1.1.1.1 2001/06/05 17:10:25 elgreen
1271 * Initial import into SourceForge
1273 * Revision 1.29 2001/05/01 01:39:23 eric
1274 * Remove the Exabyte special case code, which seemed to be barfing me :-(.
1276 * Revision 1.28 2001/04/28 00:03:10 eric
1277 * update for 1.2.12.
1279 * Revision 1.27 2001/04/18 19:27:39 eric
1280 * whoops, move ImportExport parser into the $#%!@ ImportExport test :-(.
1282 * Revision 1.26 2001/04/18 18:55:44 eric
1283 * turn too many storage elements into a warning rather than a fatal error (sigh!)
1285 * Revision 1.25 2001/04/18 18:46:54 eric
1286 * $%!@# "C" precedence rules.
1288 * Revision 1.24 2001/04/18 17:58:04 eric
1289 * initialize EmptyStorageElementCount (sigh)
1291 * Revision 1.23 2001/04/18 16:55:03 eric
1292 * hopefully get the initialization for element status straigtened out.
1294 * Revision 1.22 2001/04/18 16:32:59 eric
1295 * Cleaned up all -Wall messages.
1297 * Revision 1.21 2001/04/18 16:08:08 eric
1298 * after re-arranging & fixing bugs
1300 * Revision 1.20 2001/04/17 21:28:43 eric
1303 * Revision 1.19 2001/04/02 20:12:48 eric
1304 * checkin, mtx 1.2.11pre6
1306 * Revision 1.18 2001/03/06 01:37:05 eric
1309 * Revision 1.17 2001/02/26 20:56:12 eric
1310 * added debugging, removed Makefile
1312 * Revision 1.16 2001/02/19 23:22:28 eric
1315 * Revision 1.15 2001/02/19 23:02:04 eric
1316 * Apply SPARC patch.
1318 * Revision 1.14 2001/01/26 15:58:10 eric
1319 * Make work on solaris?
1321 * Revision 1.13 2001/01/17 19:39:01 eric
1322 * Changed mtx.h and mtxl.c to use autoconf stuff to figure out which SCSI
1323 * routines to use (this way, if we ever port to an OS that has an identical
1324 * SCSI interface to one of the supported OS's, we don't have to do anything
1327 * Revision 1.12 2001/01/17 16:36:26 eric
1328 * added date & revision strings as needed
1330 * Revision 1.11 2000/11/30 20:35:18 eric
1331 * Added Erase command.