1 /* MTX -- SCSI Tape Attached Medium Changer Control Program
3 Copyright 1997-1998 by Leonard N. Zubkoff.
4 Copyright 1999-2006 by Eric Lee Green.
5 Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
7 $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
10 This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
11 extracted from mtx.c, plus a near total re-write of most of the beast.
13 This program is free software; you may redistribute and/or modify it under
14 the terms of the GNU General Public License Version 2 as published by the
15 Free Software Foundation.
17 This program is distributed in the hope that it will be useful, but
18 WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
19 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 * FatalError: changed Feb. 2000 elg@badtux.org to eliminate a buffer
26 * overflow :-(. That could be important if mtxl is SUID for some reason.
32 /* #define DEBUG_NSM 1 */
34 /* #define DEBUG_MODE_SENSE 1 */
36 /* #define DEBUG_SCSI */
37 #define __WEIRD_CHAR_SUPPRESS 1
39 /* zap the following define when we finally add real import/export support */
40 #define IMPORT_EXPORT_HACK 1 /* for the moment, import/export == storage */
42 /* First, do some SCSI routines: */
44 /* the camlib is used on FreeBSD. */
46 # include "scsi_freebsd.c"
49 /* the scsi_ctl interface is used on HP/UX. */
50 #if HAVE_SYS_SCSI_CTL_H
51 # include "scsi_hpux.c"
54 /* the 'sg' interface is used on Linux. */
56 # include "scsi_linux.c"
59 /* the IOCTL_SCSI_PASSTHROUGH interface is used on Windows. */
60 #if HAVE_DDK_NTDDSCSI_H || defined(_MSC_VER)
61 # include "scsi_win32.c"
64 /* The 'uscsi' interface is used on Solaris. */
65 #if HAVE_SYS_SCSI_IMPL_USCSI_H
66 # include "scsi_sun.c"
69 /* The 'gsc' interface, is used on AIX. */
71 # include "scsi_aix.c"
74 /* The 'dslib' interface is used on SGI. */
76 # include "scsi_sgi.c"
79 /* Hmm, dunno what to do about Digital Unix at the moment. */
85 # include "[.vms]scsi.c"
88 extern char *argv0; /* something to let us do good error messages. */
90 /* create a global RequestSenseT value. */
91 RequestSense_T scsi_error_sense;
93 /* Now for some low-level SCSI stuff: */
95 Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense)
100 Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));
102 CDB[0] = 0x12; /* INQUIRY */
103 CDB[1] = 0; /* EVPD = 0 */
104 CDB[2] = 0; /* Page Code */
105 CDB[3] = 0; /* Reserved */
106 CDB[4] = sizeof(Inquiry_T); /* Allocation Length */
107 CDB[5] = 0; /* Control */
109 /* set us a very short timeout, sigh... */
110 SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
112 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
115 fprintf(stderr, "SCSI Inquiry Command failed\n");
118 return NULL; /* sorry! */
124 #if defined(DEBUG_NSM) || defined(DEBUG_EXCHANGE)
126 static void dump_cdb(unsigned char *CDB, int len)
128 fprintf(stderr,"CDB:");
129 PrintHex(1, CDB, len);
134 #if defined(DEBUG_NSM) || defined(DEBUG_ADIC)
136 static void dump_data(unsigned char *data, int len)
140 fprintf(stderr,"**NO DATA**\n");
144 fprintf(stderr,"DATA:");
145 PrintHex(1, data, len);
150 int BigEndian16(unsigned char *BigEndianData)
152 return (BigEndianData[0] << 8) + BigEndianData[1];
156 int BigEndian24(unsigned char *BigEndianData)
158 return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
162 void FatalError(char *ErrorMessage, ...)
164 #define FORMAT_BUF_LEN 1024
166 char FormatBuffer[FORMAT_BUF_LEN];
168 char *TargetPointer = FormatBuffer;
169 char Character, LastCharacter = '\0';
172 va_list ArgumentPointer;
173 va_start(ArgumentPointer, ErrorMessage);
174 /* SourcePointer = "mtx: "; */
175 sprintf(TargetPointer,"%s: ",argv0);
176 numchars=strlen(TargetPointer);
178 while ((Character = *ErrorMessage++) != '\0')
180 if (LastCharacter == '%')
182 if (Character == 'm')
184 SourcePointer = strerror(errno);
185 while ((Character = *SourcePointer++) != '\0')
187 *TargetPointer++ = Character;
189 if (numchars == FORMAT_BUF_LEN - 1)
194 if (numchars == FORMAT_BUF_LEN - 1)
196 break; /* break outer loop... */
201 *TargetPointer++ = '%';
202 *TargetPointer++ = Character;
204 if (numchars == FORMAT_BUF_LEN - 1)
209 LastCharacter = '\0';
213 if (Character != '%')
215 *TargetPointer++ = Character;
217 if (numchars == FORMAT_BUF_LEN-1)
222 LastCharacter = Character;
226 *TargetPointer = '\0'; /* works even if we had to break from above... */
227 vfprintf(stderr, FormatBuffer, ArgumentPointer);
228 va_end(ArgumentPointer);
233 sys$exit(VMS_ExitCode);
237 /* This is a really slow and stupid 'bzero' implementation'... */
238 void slow_bzero(char *buffer, int numchars)
246 /* malloc some memory while checking for out-of-memory conditions. */
247 void *xmalloc(size_t Size)
249 void *Result = (void *) malloc(Size);
252 FatalError("cannot allocate %d bytes of memory\n", Size);
257 /* malloc some memory, zeroing it too: */
258 void *xzmalloc(size_t Size)
260 void *Result = (void *)xmalloc(Size);
262 slow_bzero(Result, Size);
267 int min(int x, int y)
269 return (x < y ? x : y);
273 int max(int x, int y)
275 return (x > y ? x : y);
279 /* Okay, this is a hack for the NSM modular jukebox series, which
280 * uses the "SEND DIAGNOSTIC" command to do shit.
283 int SendNSMHack(DEVICE_TYPE MediumChangerFD, NSM_Param_T *nsm_command,
284 int param_len, int timeout)
287 int list_len = param_len + sizeof(NSM_Param_T) - 2048;
289 /* Okay, now for the command: */
293 CDB[3] = (unsigned char)(list_len >> 8);
294 CDB[4] = (unsigned char)list_len;
299 dump_data(nsm_command,list_len);
302 /* Don't set us any timeout unless timeout is > 0 */
305 SCSI_Set_Timeout(timeout); /* 30 minutes, sigh! */
308 /* Now send the command: */
309 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, nsm_command, list_len, &scsi_error_sense))
311 return -1; /* we failed */
313 return 0; /* Did it! */
316 NSM_Result_T *RecNSMHack( DEVICE_TYPE MediumChangerFD,
317 int param_len, int timeout)
321 NSM_Result_T *retval = (NSM_Result_T *)xzmalloc(sizeof(NSM_Result_T));
323 int list_len = param_len + sizeof(NSM_Result_T) - 0xffff;
325 /* Okay, now for the command: */
329 CDB[3] = (unsigned char)(list_len >> 8);
330 CDB[4] = (unsigned char)list_len;
337 /* Don't set us any timeout unless timeout is > 0 */
340 SCSI_Set_Timeout(timeout);
343 /* Now send the command: */
344 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, retval, list_len, &scsi_error_sense))
346 return NULL; /* we failed */
351 "page_code=%02x page_len=%d command_code=%s\n",
353 (int) ((retval->page_len_msb << 8) + retval->page_len_lsb),
354 retval->command_code);
357 return retval; /* Did it! (maybe!)*/
360 /* Routine to inventory the library. Needed by, e.g., some Breece Hill
361 * loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
362 * has no parameters, such as a range to scan :-(.
365 int Inventory(DEVICE_TYPE MediumChangerFD)
369 /* okay, now for the command: */
371 CDB[1] = CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
373 /* set us a very long timeout, sigh... */
374 SCSI_Set_Timeout(30 * 60); /* 30 minutes, sigh! */
376 if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0)
378 /* If error is UNIT ATTENTION then retry the request */
379 if (scsi_error_sense.ErrorCode != 0x70 || scsi_error_sense.SenseKey != 6 ||
380 ClearUnitAttention(MediumChangerFD, &scsi_error_sense) != 0 ||
381 SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0)
383 PrintRequestSense(&scsi_error_sense);
384 fprintf(stderr, "Initialize Element Status (0x07) failed\n");
385 return -1; /* could not do! */
388 return 0; /* did do! */
391 /* Routine to read the Mode Sense Element Address Assignment Page */
392 /* We try to read the page. If we can't read the page, we return NULL.
393 * Our caller really isn't too worried about why we could not read the
394 * page, it will simply default to some kind of default values.
396 ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD)
399 ElementModeSense_T *retval; /* processed SCSI. */
400 unsigned char input_buffer[136];
401 ElementModeSensePage_T *sense_page; /* raw SCSI. */
403 /* okay, now for the command: */
404 CDB[0] = 0x1A; /* Mode Sense(6) */
406 CDB[2] = 0x1D; /* Mode Sense Element Address Assignment Page */
408 CDB[4] = 136; /* allocation_length... */
411 /* clear the data buffer: */
412 slow_bzero((char *)&scsi_error_sense, sizeof(RequestSense_T));
413 slow_bzero((char *)input_buffer, sizeof(input_buffer));
415 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6,
416 &input_buffer, sizeof(input_buffer), &scsi_error_sense) != 0)
418 /* If error is UNIT ATTENTION then retry the request */
419 if (scsi_error_sense.ErrorCode != 0x70 || scsi_error_sense.SenseKey != 6 ||
420 ClearUnitAttention(MediumChangerFD, &scsi_error_sense) != 0 ||
421 SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6,
422 &input_buffer, sizeof(input_buffer), &scsi_error_sense) != 0)
424 PrintRequestSense(&scsi_error_sense);
425 fprintf(stderr,"Mode sense (0x1A) for Page 0x1D failed\n");
427 return NULL; /* sorry, couldn't do it. */
431 /* Could do it, now build return value: */
433 #ifdef DEBUG_MODE_SENSE
434 PrintHex(0, input_buffer, 30);
437 /* first, skip past: mode data header, and block descriptor header if any */
438 sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
440 #ifdef DEBUG_MODE_SENSE
441 fprintf(stderr,"*sense_page=%x %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
445 retval = (ElementModeSense_T *)xzmalloc(sizeof(ElementModeSense_T));
447 /* Remember that all SCSI values are big-endian: */
448 retval->MediumTransportStart =
449 (((int)sense_page->MediumTransportStartHi) << 8) +
450 sense_page->MediumTransportStartLo;
452 retval->NumMediumTransport =
453 (((int)(sense_page->NumMediumTransportHi))<<8) +
454 sense_page->NumMediumTransportLo;
456 /* HACK! Some HP autochangers don't report NumMediumTransport right! */
457 /* ARG! TAKE OUT THE %#@!# HACK! */
458 #ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
459 if (!retval->NumMediumTransport)
461 retval->NumMediumTransport = 1;
465 #ifdef DEBUG_MODE_SENSE
466 fprintf(stderr, "rawNumStorage= %d %d rawNumImportExport= %d %d\n",
467 sense_page->NumStorageHi, sense_page->NumStorageLo,
468 sense_page->NumImportExportHi, sense_page->NumImportExportLo);
469 fprintf(stderr, "rawNumTransport=%d %d rawNumDataTransfer=%d %d\n",
470 sense_page->NumMediumTransportHi, sense_page->NumMediumTransportLo,
471 sense_page->NumDataTransferHi, sense_page->NumDataTransferLo);
475 retval->StorageStart =
476 ((int)sense_page->StorageStartHi << 8) + sense_page->StorageStartLo;
479 ((int)sense_page->NumStorageHi << 8) + sense_page->NumStorageLo;
481 retval->ImportExportStart =
482 ((int)sense_page->ImportExportStartHi << 8) + sense_page->ImportExportStartLo;
484 retval->NumImportExport =
485 ((int)sense_page->NumImportExportHi << 8) + sense_page->NumImportExportLo;
487 retval->DataTransferStart =
488 ((int)sense_page->DataTransferStartHi << 8) + sense_page->DataTransferStartLo;
490 retval->NumDataTransfer =
491 ((int)sense_page->NumDataTransferHi << 8) + sense_page->NumDataTransferLo;
493 /* allocate a couple spares 'cause some HP autochangers and maybe others
494 * don't properly report the robotics arm(s) count here...
496 retval->NumElements =
497 retval->NumStorage+retval->NumImportExport +
498 retval->NumDataTransfer+retval->NumMediumTransport;
500 retval->MaxReadElementStatusData =
501 (sizeof(ElementStatusDataHeader_T) +
502 4 * sizeof(ElementStatusPage_T) +
503 retval->NumElements * sizeof(TransportElementDescriptor_T));
505 #ifdef IMPORT_EXPORT_HACK
506 retval->NumStorage = retval->NumStorage+retval->NumImportExport;
509 #ifdef DEBUG_MODE_SENSE
510 fprintf(stderr, "NumMediumTransport=%d\n", retval->NumMediumTransport);
511 fprintf(stderr, "NumStorage=%d\n", retval->NumStorage);
512 fprintf(stderr, "NumImportExport=%d\n", retval->NumImportExport);
513 fprintf(stderr, "NumDataTransfer=%d\n", retval->NumDataTransfer);
514 fprintf(stderr, "MaxReadElementStatusData=%d\n", retval->MaxReadElementStatusData);
515 fprintf(stderr, "NumElements=%d\n", retval->NumElements);
522 static void FreeElementData(ElementStatus_T *data)
524 free(data->DataTransferElementAddress);
525 free(data->DataTransferElementSourceStorageElementNumber);
526 free(data->DataTransferPrimaryVolumeTag);
527 free(data->DataTransferAlternateVolumeTag);
528 free(data->PrimaryVolumeTag);
529 free(data->AlternateVolumeTag);
530 free(data->StorageElementAddress);
531 free(data->StorageElementIsImportExport);
532 free(data->StorageElementFull);
533 free(data->DataTransferElementFull);
539 static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense)
541 ElementStatus_T *retval;
543 retval = (ElementStatus_T *)xzmalloc(sizeof(ElementStatus_T));
545 /* now for the invidual component arrays.... */
547 retval->DataTransferElementAddress =
548 (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
549 retval->DataTransferElementSourceStorageElementNumber =
550 (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
551 retval->DataTransferPrimaryVolumeTag =
552 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
553 retval->DataTransferAlternateVolumeTag =
554 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
555 retval->PrimaryVolumeTag =
556 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
557 retval->AlternateVolumeTag =
558 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
559 retval->StorageElementAddress =
560 (int *)xzmalloc(sizeof(int) * (mode_sense->NumStorage + 1));
561 retval->StorageElementIsImportExport =
562 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
563 retval->StorageElementFull =
564 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
565 retval->DataTransferElementFull =
566 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumDataTransfer + 1));
568 return retval; /* sigh! */
572 void copy_barcode(unsigned char *src, unsigned char *dest)
576 for (i=0; i < 36; i++)
580 if ((*dest < 32) || (*dest > 127))
587 *dest = 0; /* null-terminate */
590 /* This #%!@# routine has more parameters than I can count! */
591 static unsigned char *SendElementStatusRequestActual(
592 DEVICE_TYPE MediumChangerFD,
593 RequestSense_T *RequestSense,
594 Inquiry_T *inquiry_info,
602 boolean is_attached = false;
604 unsigned char *DataBuffer; /* size of data... */
606 #ifdef HAVE_GET_ID_LUN
609 if (inquiry_info->MChngr &&
610 inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
615 if (flags->no_attached)
621 DataBuffer = (unsigned char *)xzmalloc(NumBytes + 1);
623 slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
625 #ifdef HAVE_GET_ID_LUN
626 scsi_id = SCSI_GetIDLun(MediumChangerFD);
629 CDB[0] = 0xB8; /* READ ELEMENT STATUS */
633 CDB[0] = 0xB4; /* whoops, READ_ELEMENT_STATUS_ATTACHED! */
636 #ifdef HAVE_GET_ID_LUN
637 CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ?
638 0 : 0x10) | flags->elementtype; /* Lun + VolTag + Type code */
641 /* Element Type Code = 0, VolTag = 1 */
642 CDB[1] = (unsigned char)((flags->no_barcodes ? 0 : 0x10) | flags->elementtype);
644 /* Starting Element Address */
645 CDB[2] = (unsigned char)(ElementStart >> 8);
646 CDB[3] = (unsigned char)ElementStart;
648 /* Number Of Elements */
649 CDB[4]= (unsigned char)(NumElements >> 8);
650 CDB[5]= (unsigned char)NumElements;
652 CDB[6] = 0; /* Reserved */
654 /* allocation length */
655 CDB[7]= (unsigned char)(NumBytes >> 16);
656 CDB[8]= (unsigned char)(NumBytes >> 8);
657 CDB[9]= (unsigned char)NumBytes;
659 CDB[10] = 0; /* Reserved */
660 CDB[11] = 0; /* Control */
663 fprintf(stderr,"CDB:\n");
664 PrintHex(2, CDB, 12);
667 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
668 DataBuffer,NumBytes, RequestSense) != 0)
672 fprintf(stderr, "Read Element Status (0x%02X) failed\n", CDB[0]);
676 First see if we have sense key of 'illegal request',
677 additional sense code of '24', additional sense qualfier of
678 '0', and field in error of '4'. This means that we issued a request
679 w/bar code reader and did not have one, thus must re-issue the request
684 Most autochangers and tape libraries set a sense key here if
685 they do not have a bar code reader. For some reason, the ADIC DAT
686 uses a different sense key? Let's retry w/o bar codes for *ALL*
690 if (RequestSense->SenseKey > 1)
692 /* we issued a code requesting bar codes, there is no bar code reader? */
693 /* clear out our sense buffer first... */
694 slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
696 CDB[1] &= ~0x10; /* clear bar code flag! */
699 fprintf(stderr,"CDB:\n");
700 PrintHex(2, CDB, 12);
703 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
704 DataBuffer, NumBytes, RequestSense) != 0)
718 /* print a bunch of extra debug data :-(. */
719 PrintRequestSense(RequestSense); /* see what it sez :-(. */
720 fprintf(stderr,"Data:\n");
721 PrintHex(2, DataBuffer, 40);
723 return DataBuffer; /* we succeeded! */
727 unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
728 RequestSense_T *RequestSense,
729 Inquiry_T *inquiry_info,
736 unsigned char *DataBuffer; /* size of data... */
739 DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
748 One weird loader wants either 8 or BYTE_COUNT_OF_REPORT
749 values for the ALLOCATION_LENGTH. Give it what it wants
750 if we get an Sense Key of 05 Illegal Request with a
751 CDB position of 7 as the field in error.
754 if (DataBuffer == NULL &&
755 RequestSense->SenseKey == 5 &&
756 RequestSense->CommandData &&
757 RequestSense->BitPointer == 7)
759 NumBytes=8; /* send an 8 byte request */
760 DataBuffer=SendElementStatusRequestActual( MediumChangerFD,
770 /* the above code falls thru into this: */
771 if (DataBuffer != NULL)
773 /* see if we need to retry with a bigger NumBytes: */
774 real_numbytes = ((int)DataBuffer[5] << 16) +
775 ((int)DataBuffer[6] << 8) +
776 (int)DataBuffer[7] + 8;
778 if (real_numbytes > NumBytes)
781 free(DataBuffer); /* solve memory leak */
782 DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
798 /******************* ParseElementStatus ***********************************/
799 /* This does the actual grunt work of parsing element status data. It fills
800 * in appropriate pieces of its input data. It may be called multiple times
801 * while we are gathering element status.
804 static void ParseElementStatus( int *EmptyStorageElementAddress,
805 int *EmptyStorageElementCount,
806 unsigned char *DataBuffer,
807 ElementStatus_T *ElementStatus,
808 ElementModeSense_T *mode_sense,
812 unsigned char *DataPointer = DataBuffer;
813 TransportElementDescriptor_T TEBuf;
814 TransportElementDescriptor_T *TransportElementDescriptor;
815 ElementStatusDataHeader_T *ElementStatusDataHeader;
816 Element2StatusPage_T *ElementStatusPage;
817 Element2StatusPage_T ESBuf;
819 int TransportElementDescriptorLength;
821 int ImportExportIndex;
823 ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
824 DataPointer += sizeof(ElementStatusDataHeader_T);
826 BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
829 fprintf(stderr,"ElementCount=%d\n",ElementCount);
833 while (ElementCount > 0)
836 int got_element_num=0;
838 fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
842 memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
843 ElementStatusPage = &ESBuf;
844 DataPointer += sizeof(ElementStatusPage_T);
846 TransportElementDescriptorLength =
847 BigEndian16(ElementStatusPage->ElementDescriptorLength);
849 if (TransportElementDescriptorLength <
850 sizeof(TransportElementDescriptorShort_T))
852 /* Foo, Storage Element Descriptors can be 4 bytes?! */
853 if ((ElementStatusPage->ElementTypeCode != MediumTransportElement &&
854 ElementStatusPage->ElementTypeCode != StorageElement &&
855 ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
856 TransportElementDescriptorLength < 4)
859 fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
861 FatalError("Transport Element Descriptor Length too short: %d\n", TransportElementDescriptorLength);
865 fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
868 BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
870 fprintf(stderr,"%d bytes of descriptor data available in descriptor\n",
873 /* work around a bug in ADIC DAT loaders */
874 if (BytesAvailable <= 0)
876 ElementCount--; /* sorry :-( */
878 while (BytesAvailable > 0)
880 /* TransportElementDescriptor =
881 (TransportElementDescriptor_T *) DataPointer; */
882 memcpy(&TEBuf, DataPointer,
883 (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
884 TransportElementDescriptorLength :
886 TransportElementDescriptor = &TEBuf;
888 if (pNextElement != NULL)
890 if (BigEndian16(TransportElementDescriptor->ElementAddress) != 0 || *pNextElement == 0)
892 (*pNextElement) = BigEndian16(TransportElementDescriptor->ElementAddress) + 1;
900 DataPointer += TransportElementDescriptorLength;
901 BytesAvailable -= TransportElementDescriptorLength;
904 switch (ElementStatusPage->ElementTypeCode)
906 case MediumTransportElement:
907 ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
909 fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress);
913 /* we treat ImportExport elements as if they were normal
914 * storage elements now, sigh...
916 case ImportExportElement:
918 fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
920 if (ElementStatus->ImportExportCount >= mode_sense->NumImportExport)
922 fprintf(stderr,"Warning:Too Many Import/Export Elements Reported (expected %d, now have %d\n",
923 mode_sense->NumImportExport,
924 ElementStatus->ImportExportCount + 1);
926 return; /* we're done :-(. */
929 ImportExportIndex = mode_sense->NumStorage - mode_sense->NumImportExport + ElementStatus->ImportExportCount;
931 ElementStatus->StorageElementAddress[ImportExportIndex] =
932 BigEndian16(TransportElementDescriptor->ElementAddress);
933 ElementStatus->StorageElementFull[ImportExportIndex] =
934 TransportElementDescriptor->Full;
936 if ( (TransportElementDescriptorLength > 11) &&
937 (ElementStatusPage->VolBits & E2_AVOLTAG))
939 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
940 ElementStatus->AlternateVolumeTag[ImportExportIndex]);
944 ElementStatus->AlternateVolumeTag[ImportExportIndex][0] = 0; /* null string. */;
946 if ((TransportElementDescriptorLength > 11) &&
947 (ElementStatusPage->VolBits & E2_PVOLTAG))
949 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
950 ElementStatus->PrimaryVolumeTag[ImportExportIndex]);
954 ElementStatus->PrimaryVolumeTag[ImportExportIndex][0]=0; /* null string. */
957 ElementStatus->StorageElementIsImportExport[ImportExportIndex] = 1;
959 ElementStatus->ImportExportCount++;
964 fprintf(stderr,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
966 /* ATL/Exabyte kludge -- skip slots that aren't installed :-( */
967 if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
968 TransportElementDescriptor->AdditionalSenseCodeQualifier==0x02)
971 ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
972 BigEndian16(TransportElementDescriptor->ElementAddress);
973 ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
974 TransportElementDescriptor->Full;
976 if (TransportElementDescriptor->Except)
977 fprintf(stderr,"ASC,ASCQ = 0x%x,0x%x ",TransportElementDescriptor->AdditionalSenseCode,TransportElementDescriptor->AdditionalSenseCodeQualifier);
978 fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
980 if (!TransportElementDescriptor->Full)
982 EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
983 ElementStatus->StorageElementCount; /* slot idx. */
984 /* ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
986 if ((TransportElementDescriptorLength > 11) &&
987 (ElementStatusPage->VolBits & E2_AVOLTAG))
989 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
990 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
994 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */;
996 if ((TransportElementDescriptorLength > 11) &&
997 (ElementStatusPage->VolBits & E2_PVOLTAG))
999 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
1000 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
1004 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
1007 ElementStatus->StorageElementCount++;
1009 Note that the original mtx had no check here for
1010 buffer overflow, though some drives might mistakingly
1014 if (ElementStatus->StorageElementCount > mode_sense->NumStorage)
1016 fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
1017 mode_sense->NumStorage,
1018 ElementStatus->StorageElementCount);
1020 return; /* we're done :-(. */
1024 case DataTransferElement:
1025 /* tape drive not installed, go back to top of loop */
1027 /* if (TransportElementDescriptor->Except) continue ; */
1029 /* Note: This is for Exabyte tape libraries that improperly
1030 report that they have a 2nd tape drive when they don't. We
1031 could generalize this in an ideal world, but my attempt to
1032 do so failed with dual-drive Exabyte tape libraries that
1033 *DID* have the second drive. Sigh.
1035 if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
1036 TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
1041 /* generalize it. Does it work? Let's try it! */
1043 No, dammit, following does not work on dual-drive Exabyte
1044 'cause if a tape is in the drive, it sets the AdditionalSense
1045 code to something (sigh).
1047 /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
1051 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
1052 BigEndian16(TransportElementDescriptor->ElementAddress);
1053 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] =
1054 TransportElementDescriptor->Full;
1055 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
1056 BigEndian16(TransportElementDescriptor->SourceStorageElementAddress);
1059 fprintf(stderr, "%d: ElementAddress = %d, Full = %d, SourceElement = %d\n",
1060 ElementStatus->DataTransferElementCount,
1061 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount],
1062 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount],
1063 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount]);
1065 if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer)
1067 FatalError("Too many Data Transfer Elements Reported\n");
1070 if (ElementStatusPage->VolBits & E2_PVOLTAG)
1072 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
1073 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
1077 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
1080 if (ElementStatusPage->VolBits & E2_AVOLTAG)
1082 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
1083 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
1087 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
1090 ElementStatus->DataTransferElementCount++;
1092 /* 0 actually is a usable element address */
1093 /* if (DataTransferElementAddress == 0) */
1095 /* "illegal Data Transfer Element Address %d reported\n", */
1096 /* DataTransferElementAddress); */
1100 FatalError("illegal Element Type Code %d reported\n",
1101 ElementStatusPage->ElementTypeCode);
1108 fprintf(stderr,"Next start element will be %d\n",*pNextElement);
1113 /********************* Real ReadElementStatus ********************* */
1116 * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
1117 * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
1118 * under Linux, which gets around the @#%@ 4k buffer size in Linux.
1119 * We still have the restriction that Linux cuts off the last two
1120 * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
1121 * verbose widget won't work :-(.
1123 * We now look for that "attached" bit in the inquiry_info to see whether
1124 * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
1125 * look at the device type in the inquiry_info to see whether it is a media
1126 * changer or tape device, and if it's a media changer device, we ignore the
1127 * attached bit (one beta tester found an old 4-tape DAT changer that set
1128 * the attached bit for both the tape device AND the media changer device).
1132 ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1134 ElementStatus_T *ElementStatus;
1136 unsigned char *DataBuffer; /* size of data... */
1138 int EmptyStorageElementCount=0;
1139 int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
1142 boolean is_attached = false;
1145 ElementModeSense_T *mode_sense = NULL;
1147 if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
1152 if (flags->no_attached)
1154 /* override, sigh */
1155 is_attached = false;
1160 mode_sense = ReadAssignmentPage(MediumChangerFD);
1165 mode_sense = (ElementModeSense_T *)xmalloc(sizeof(ElementModeSense_T));
1166 mode_sense->NumMediumTransport = MAX_TRANSPORT_ELEMENTS;
1167 mode_sense->NumStorage = MAX_STORAGE_ELEMENTS;
1168 mode_sense->NumDataTransfer = MAX_TRANSFER_ELEMENTS;
1169 mode_sense->MaxReadElementStatusData =
1170 (sizeof(ElementStatusDataHeader_T) + 3 * sizeof(ElementStatusPage_T) +
1171 (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS) *
1172 sizeof(TransportElementDescriptor_T));
1174 /* don't care about the others anyhow at the moment... */
1177 ElementStatus = AllocateElementData(mode_sense);
1179 /* Now to initialize it (sigh). */
1180 ElementStatus->StorageElementCount = 0;
1181 ElementStatus->DataTransferElementCount = 0;
1183 /* first, allocate some empty storage stuff: Note that we pass this
1184 * down to ParseElementStatus (sigh!)
1187 EmptyStorageElementAddress = (int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
1188 for (i = 0; i < mode_sense->NumStorage; i++)
1190 EmptyStorageElementAddress[i] = -1;
1193 /* Okay, now to send some requests for the various types of stuff: */
1195 /* -----------STORAGE ELEMENTS---------------- */
1196 /* Let's start with storage elements: */
1198 for (i = 0; i < mode_sense->NumDataTransfer; i++)
1200 /* initialize them to an illegal # so that we can fix later... */
1201 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
1204 if (flags->querytype == MTX_ELEMENTSTATUS_ORIGINAL)
1207 fprintf(stderr,"Using original element status polling method (storage, import/export, drivers etc independantly)\n");
1209 flags->elementtype = StorageElement; /* sigh! */
1210 DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
1211 inquiry_info, flags,
1212 mode_sense->StorageStart,
1213 /* adjust for import/export. */
1214 mode_sense->NumStorage - mode_sense->NumImportExport,
1215 mode_sense->MaxReadElementStatusData);
1220 fprintf(stderr,"Had no elements!\n");
1222 /* darn. Free up stuff and return. */
1223 #ifdef DEBUG_MODE_SENSE
1224 PrintRequestSense(RequestSense);
1226 FreeElementData(ElementStatus);
1231 fprintf(stderr, "Parsing storage elements\n");
1233 ParseElementStatus(EmptyStorageElementAddress, &EmptyStorageElementCount,
1234 DataBuffer,ElementStatus,mode_sense,NULL);
1236 free(DataBuffer); /* sigh! */
1238 /* --------------IMPORT/EXPORT--------------- */
1239 /* Next let's see if we need to do Import/Export: */
1240 if (mode_sense->NumImportExport > 0)
1243 fprintf(stderr,"Sending request for Import/Export status\n");
1245 flags->elementtype = ImportExportElement;
1246 DataBuffer = SendElementStatusRequest( MediumChangerFD,RequestSense,
1247 inquiry_info, flags,
1248 mode_sense->ImportExportStart,
1249 mode_sense->NumImportExport,
1250 mode_sense->MaxReadElementStatusData);
1255 fprintf(stderr,"Had no input/export element!\n");
1257 /* darn. Free up stuff and return. */
1258 #ifdef DEBUG_MODE_SENSE
1259 PrintRequestSense(RequestSense);
1261 FreeElementData(ElementStatus);
1265 fprintf(stderr,"Parsing inport/export element status\n");
1268 dump_data(DataBuffer, 100); /* dump some data :-(. */
1270 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1271 DataBuffer, ElementStatus, mode_sense, NULL);
1273 ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
1276 /* ----------------- DRIVES ---------------------- */
1279 fprintf(stderr,"Sending request for data transfer element (drive) status\n");
1281 flags->elementtype = DataTransferElement; /* sigh! */
1282 DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
1283 inquiry_info, flags,
1284 mode_sense->DataTransferStart,
1285 mode_sense->NumDataTransfer,
1286 mode_sense->MaxReadElementStatusData);
1290 fprintf(stderr,"No data transfer element status.");
1292 /* darn. Free up stuff and return. */
1293 #ifdef DEBUG_MODE_SENSE
1294 PrintRequestSense(RequestSense);
1296 FreeElementData(ElementStatus);
1301 fprintf(stderr,"Parsing data for data transfer element (drive) status\n");
1303 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1304 DataBuffer,ElementStatus, mode_sense, NULL);
1306 free(DataBuffer); /* sigh! */
1308 /* ----------------- Robot Arm(s) -------------------------- */
1310 /* grr, damned brain dead HP doesn't report that it has any! */
1311 if (!mode_sense->NumMediumTransport)
1313 ElementStatus->TransportElementAddress = 0; /* default it sensibly :-(. */
1318 fprintf(stderr,"Sending request for robot arm status\n");
1320 flags->elementtype = MediumTransportElement; /* sigh! */
1321 DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
1322 inquiry_info, flags,
1323 mode_sense->MediumTransportStart,
1324 1, /* only get 1, sigh. */
1325 mode_sense->MaxReadElementStatusData);
1329 fprintf(stderr,"Loader reports no robot arm!\n");
1331 /* darn. Free up stuff and return. */
1332 #ifdef DEBUG_MODE_SENSE
1333 PrintRequestSense(RequestSense);
1335 FreeElementData(ElementStatus);
1339 fprintf(stderr,"Parsing robot arm data\n");
1341 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1342 DataBuffer, ElementStatus, mode_sense, NULL);
1349 int nLastEl=-1, nNextEl=0;
1352 fprintf(stderr,"Using alternative element status polling method (all elements)\n");
1354 /* ----------------- ALL Elements ---------------------- */
1355 /* Just keep asking for elements till no more are returned
1356 - increment our starting address as we go acording to the
1357 number of elements returned from the last call
1360 while(nLastEl!=nNextEl)
1362 flags->elementtype = AllElementTypes;//StorageElement; /* sigh! */ /*XL1B2 firewire changer does not seem to respond to specific types so just use all elements*/
1363 DataBuffer = SendElementStatusRequest( MediumChangerFD,
1367 nNextEl,//mode_sense->StorageStart,
1368 /* adjust for import/export. */
1369 mode_sense->NumStorage - mode_sense->NumImportExport,//FIX ME:this should be a more sensible value
1370 mode_sense->MaxReadElementStatusData);
1373 if (RequestSense->AdditionalSenseCode == 0x21 &&
1374 RequestSense->AdditionalSenseCodeQualifier == 0x01)
1376 /* Error is invalid element address, we've probably just hit the end */
1380 /* darn. Free up stuff and return. */
1381 FreeElementData(ElementStatus);
1387 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1388 DataBuffer, ElementStatus, mode_sense, &nNextEl);
1390 free(DataBuffer); /* sigh! */
1393 ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
1396 /*---------------------- Sanity Checking ------------------- */
1398 if (ElementStatus->DataTransferElementCount == 0)
1399 FatalError("no Data Transfer Element reported\n");
1401 if (ElementStatus->StorageElementCount == 0)
1402 FatalError("no Storage Elements reported\n");
1405 /* ---------------------- Reset SourceStorageElementNumbers ------- */
1408 * Re-write the SourceStorageElementNumber code *AGAIN*.
1411 * Translate from raw element # to our translated # (if possible).
1412 * First, check the SourceStorageElementNumbers against the list of
1413 * filled slots. If the slots indicated are empty, we accept that list as
1414 * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
1417 * If we had some invalid (or unknown) SourceStorageElementNumbers
1418 * then we must search for free slots, and assign SourceStorageElementNumbers
1419 * to those free slots. We happen to already built a list of free
1420 * slots as part of the process of reading the storage element numbers
1421 * from the tape. So that's easy enough to do!
1424 #ifdef DEBUG_TAPELIST
1425 fprintf(stderr, "empty slots: %d\n", EmptyStorageElementCount);
1426 if (EmptyStorageElementCount)
1428 for (i = 0; i < EmptyStorageElementCount; i++)
1430 fprintf(stderr, "empty: %d\n", EmptyStorageElementAddress[i]);
1436 * Now we re-assign origin slots if the "real" origin slot
1437 * is obviously defective:
1440 for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
1444 /* if we have an element, then ... */
1445 if (ElementStatus->DataTransferElementFull[i])
1447 elnum = ElementStatus->DataTransferElementSourceStorageElementNumber[i];
1448 /* if we have an element number, then ... */
1451 /* Now to translate the elnum: */
1452 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
1453 for (j = 0; j < ElementStatus->StorageElementCount; j++)
1455 if (elnum == ElementStatus->StorageElementAddress[j])
1457 /* now see if the element # is already occupied:*/
1458 if (!ElementStatus->StorageElementFull[j])
1460 /* properly set the source... */
1461 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = j;
1470 /* We have invalid sources, so let's see what they should be: */
1471 /* Note: If EmptyStorageElementCount is < # of drives, the leftover
1472 * drives will be assigned a -1 (see the initialization loop for
1473 * EmptyStorageElementAddress above), which will be reported as "slot 0"
1474 * by the user interface. This is an invalid value, but more useful for us
1475 * to have than just crapping out here :-(.
1478 for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
1480 if (ElementStatus->DataTransferElementFull[i] &&
1481 ElementStatus->DataTransferElementSourceStorageElementNumber[i] < 0)
1483 #ifdef DEBUG_TAPELIST
1484 fprintf(stderr,"for drive %d, changing to %d (empty slot #%d)\n",
1486 EmptyStorageElementAddress[empty_idx],
1489 ElementStatus->DataTransferElementSourceStorageElementNumber[i] =
1490 EmptyStorageElementAddress[empty_idx++];
1496 free(EmptyStorageElementAddress);
1497 return ElementStatus;
1500 /*************************************************************************/
1502 RequestSense_T *PositionElement(DEVICE_TYPE MediumChangerFD,
1503 int DestinationAddress,
1504 ElementStatus_T *ElementStatus)
1506 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1511 CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1512 CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1513 CDB[4] = (unsigned char)(DestinationAddress >> 8);
1514 CDB[5] = (unsigned char)DestinationAddress;
1520 if(SCSI_ExecuteCommand( MediumChangerFD, Output, &CDB, 10,
1521 NULL, 0, RequestSense) != 0)
1523 return RequestSense;
1526 return NULL; /* success */
1530 /* Now the actual media movement routine! */
1531 RequestSense_T *MoveMedium( DEVICE_TYPE MediumChangerFD, int SourceAddress,
1532 int DestinationAddress,
1533 ElementStatus_T *ElementStatus,
1534 Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1536 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1539 if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
1541 /* if using the ATTACHED API */
1542 CDB[0] = 0xA7; /* MOVE_MEDIUM_ATTACHED */
1546 CDB[0] = 0xA5; /* MOVE MEDIUM */
1549 CDB[1] = 0; /* Reserved */
1551 /* Transport Element Address */
1552 CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1553 CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1555 /* Source Address */
1556 CDB[4] = (unsigned char)(SourceAddress >> 8);
1557 CDB[5] = (unsigned char)SourceAddress;
1559 /* Destination Address */
1560 CDB[6] = (unsigned char)(DestinationAddress >> 8);
1561 CDB[7] = (unsigned char)DestinationAddress;
1563 CDB[8] = 0; /* Reserved */
1564 CDB[9] = 0; /* Reserved */
1568 CDB[10] = 1; /* Reserved */
1574 /* eepos controls the tray for import/export elements, sometimes. */
1575 CDB[11] = flags->eepos << 6; /* Control */
1577 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1578 NULL, 0, RequestSense) != 0)
1581 fprintf(stderr, "Move Medium (0x%02X) failed\n", CDB[0]);
1583 return RequestSense;
1587 return NULL; /* success! */
1591 /* Now the actual Exchange Medium routine! */
1592 RequestSense_T *ExchangeMedium( DEVICE_TYPE MediumChangerFD, int SourceAddress,
1593 int DestinationAddress, int Dest2Address,
1594 ElementStatus_T *ElementStatus,
1595 SCSI_Flags_T *flags)
1597 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1600 CDB[0] = 0xA6; /* EXCHANGE MEDIUM */
1601 CDB[1] = 0; /* Reserved */
1603 /* Transport Element Address */
1604 CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1605 CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1607 /* Source Address */
1608 CDB[4] = (unsigned char)(SourceAddress >> 8);
1609 CDB[5] = (unsigned char)SourceAddress;
1611 /* Destination Address */
1612 CDB[6] = (unsigned char)(DestinationAddress >> 8);
1613 CDB[7] = (unsigned char)DestinationAddress;
1615 /* move destination back to source? */
1616 CDB[8] = (unsigned char)(Dest2Address >> 8);
1617 CDB[9] = (unsigned char)Dest2Address;
1622 CDB[10] |= 2; /* INV2 */
1627 CDB[1] |= 1; /* INV1 */
1630 /* eepos controls the tray for import/export elements, sometimes. */
1631 CDB[11] = flags->eepos << 6; /* Control */
1633 #ifdef DEBUG_EXCHANGE
1637 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1638 NULL, 0, RequestSense) != 0)
1640 return RequestSense;
1643 return NULL; /* success! */
1648 * for Linux, this creates a way to do a short erase... the @#$%@ st.c
1649 * driver defaults to doing a long erase!
1652 RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD)
1654 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1658 CDB[1] = 0; /* Short! */
1659 CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
1661 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, NULL, 0, RequestSense) != 0)
1663 /* If error is UNIT ATTENTION then retry the request */
1664 if (RequestSense->ErrorCode != 0x70 || RequestSense->SenseKey != 6 ||
1665 ClearUnitAttention(MediumChangerFD, RequestSense) != 0 ||
1666 SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, NULL, 0, RequestSense) != 0)
1669 fprintf(stderr, "Erase (0x19) failed\n");
1671 return RequestSense;
1676 return NULL; /* Success! */
1679 /* Routine to send an LOAD/UNLOAD from the MMC/SSC spec to a device.
1680 * For tapes and changers this can be used either to eject a tape
1681 * or to eject a magazine (on some Seagate changers, when sent to LUN 1 ).
1682 * For CD/DVDs this is used to Load or Unload a disc which is required by
1683 * some media changers.
1686 int LoadUnload(DEVICE_TYPE fd, int bLoad)
1689 /* okay, now for the command: */
1692 CDB[4] = bLoad ? 3 : 2;
1693 CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1695 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
1697 /* If error is UNIT ATTENTION then retry the request */
1698 if (scsi_error_sense.ErrorCode != 0x70 || scsi_error_sense.SenseKey != 6 ||
1699 ClearUnitAttention(fd, &scsi_error_sense) != 0 ||
1700 SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
1702 PrintRequestSense(&scsi_error_sense);
1703 fprintf(stderr, "Eject (0x1B) failed\n");
1704 return -1; /* could not do! */
1707 return 0; /* did do! */
1710 /* Routine to send an START/STOP from the MMC/SSC spec to a device.
1711 * For tape drives this may be required prior to using the changer
1712 * Load or Unload commands.
1713 * For CD/DVD drives this is used to Load or Unload a disc which may be
1714 * required by some media changers.
1717 int StartStop(DEVICE_TYPE fd, int bStart)
1720 /* okay, now for the command: */
1723 CDB[4] = bStart ? 1 : 0;
1724 CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1726 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,NULL, 0, &scsi_error_sense) != 0)
1728 PrintRequestSense(&scsi_error_sense);
1729 fprintf(stderr, "Eject (0x1B) failed\n");
1730 return -1; /* could not do! */
1732 return 0; /* did do! */
1735 /* Routine to send a LOCK/UNLOCK from the SSC/MMC spec to a device.
1736 * This can be used to prevent or allow the Tape or CD/DVD from being
1740 int LockUnlock(DEVICE_TYPE fd, int bLock)
1743 /* okay, now for the command: */
1746 CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1747 CDB[4] = (char)bLock;
1749 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
1751 PrintRequestSense(&scsi_error_sense);
1752 fprintf(stderr, "Lock/Unlock (0x1E) failed\n");
1753 return -1; /* could not do! */
1755 return 0; /* did do! */
1758 int ClearUnitAttention(DEVICE_TYPE fd, RequestSense_T *RequestSense)
1761 int RetryCount = 10; /* Unit Attentions may be stacked */
1762 RequestSense_T unit_attention_sense;
1764 CDB[0] = 0x03; /* Request Sense */
1765 CDB[4] = (char)sizeof(*RequestSense);
1766 CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1768 while (RetryCount-- > 0)
1770 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
1771 &unit_attention_sense, sizeof(unit_attention_sense),
1774 fprintf(stderr, "RequestSense (0x03) failed\n");
1775 return -1; /* could not do! */
1778 if (unit_attention_sense.SenseKey == 0)
1780 /* If SenseKey is NO SENSE then we are done. */
1784 return -1; /* did do! */
1788 static char Spaces[] = " ";
1790 void PrintHex(int Indent, unsigned char *Buffer, int Length)
1797 for (idxBuffer = 0; idxBuffer < Length; idxBuffer++)
1799 if ((idxBuffer % 16) == 0)
1803 fputc('\'', stderr);
1805 for (idxAscii = idxBuffer - 16; idxAscii < idxBuffer; idxAscii++)
1807 cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x7F ? Buffer[idxAscii] : '.';
1808 fputc(cAscii, stderr);
1810 fputs("'\n", stderr);
1812 fprintf(stderr, "%.*s%04X: ", Indent, Spaces, idxBuffer);
1814 fprintf(stderr, "%02X ", (unsigned char)Buffer[idxBuffer]);
1817 PadLength = 16 - (Length % 16);
1821 fprintf(stderr, "%.*s'", 3 * PadLength, Spaces);
1823 for (idxAscii = idxBuffer - (16 - PadLength); idxAscii < idxBuffer; idxAscii++)
1825 cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x80 ? Buffer[idxAscii] : '.';
1826 fputc(cAscii, stderr);
1828 fputs("'\n", stderr);
1834 static char *sense_keys[] =
1836 "No Sense", /* 00 */
1837 "Recovered Error", /* 01 */
1838 "Not Ready", /* 02 */
1839 "Medium Error", /* 03 */
1840 "Hardware Error", /* 04 */
1841 "Illegal Request", /* 05 */
1842 "Unit Attention", /* 06 */
1843 "Data Protect", /* 07 */
1844 "Blank Check", /* 08 */
1847 "Aborted Command", /* 0b */
1849 "Volume Overflow", /* 0d */
1850 "Miscompare", /* 0e */
1854 static char Yes[] = "yes";
1855 static char No[] = "no";
1857 void PrintRequestSense(RequestSense_T *RequestSense)
1861 fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
1862 fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
1864 if (RequestSense->ErrorCode == 0x70)
1868 else if (RequestSense->ErrorCode == 0x71)
1877 fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n", RequestSense->ErrorCode, msg);
1878 fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n", sense_keys[RequestSense->SenseKey]);
1879 fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
1880 fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
1881 fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
1883 if (RequestSense->Valid)
1885 fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
1888 fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
1889 fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
1891 if (RequestSense->SKSV)
1893 fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n", RequestSense->BitPointer);
1896 fprintf(stderr, "mtx: Request Sense: BPV=%s\n", RequestSense->BPV ? Yes : No);
1897 fprintf(stderr, "mtx: Request Sense: Error in CDB=%s\n", RequestSense->CommandData ? Yes : No);
1898 fprintf(stderr, "mtx: Request Sense: SKSV=%s\n", RequestSense->SKSV ? Yes : No);
1900 if (RequestSense->BPV || RequestSense -> SKSV)
1902 fprintf(stderr, "mtx: Request Sense: Field Pointer = %02X %02X\n",
1903 RequestSense->FieldData[0], RequestSense->FieldData[1]);