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 by Robert Nelson <robertn@the-nelsons.org>
7 $Date: 2007-03-24 18:14:01 -0700 (Sat, 24 Mar 2007) $
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,
113 Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
116 fprintf(stderr, "SCSI Inquiry Command failed\n");
119 return NULL; /* sorry! */
125 #if defined(DEBUG_NSM) || defined(DEBUG_EXCHANGE)
127 static void dump_cdb(unsigned char *CDB, int len)
129 fprintf(stderr,"CDB:");
130 PrintHex(1, CDB, len);
135 #if defined(DEBUG_NSM) || defined(DEBUG_ADIC)
137 static void dump_data(unsigned char *data, int len)
141 fprintf(stderr,"**NO DATA**\n");
145 fprintf(stderr,"DATA:");
146 PrintHex(1, data, len);
151 int BigEndian16(unsigned char *BigEndianData)
153 return (BigEndianData[0] << 8) + BigEndianData[1];
157 int BigEndian24(unsigned char *BigEndianData)
159 return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
163 void FatalError(char *ErrorMessage, ...)
165 #define FORMAT_BUF_LEN 1024
167 char FormatBuffer[FORMAT_BUF_LEN];
169 char *TargetPointer = FormatBuffer;
170 char Character, LastCharacter = '\0';
173 va_list ArgumentPointer;
174 va_start(ArgumentPointer, ErrorMessage);
175 /* SourcePointer = "mtx: "; */
176 sprintf(TargetPointer,"%s: ",argv0);
177 numchars=strlen(TargetPointer);
179 while ((Character = *ErrorMessage++) != '\0')
181 if (LastCharacter == '%')
183 if (Character == 'm')
185 SourcePointer = strerror(errno);
186 while ((Character = *SourcePointer++) != '\0')
188 *TargetPointer++ = Character;
190 if (numchars == FORMAT_BUF_LEN - 1)
195 if (numchars == FORMAT_BUF_LEN - 1)
197 break; /* break outer loop... */
202 *TargetPointer++ = '%';
203 *TargetPointer++ = Character;
205 if (numchars == FORMAT_BUF_LEN - 1)
210 LastCharacter = '\0';
214 if (Character != '%')
216 *TargetPointer++ = Character;
218 if (numchars == FORMAT_BUF_LEN-1)
223 LastCharacter = Character;
227 *TargetPointer = '\0'; /* works even if we had to break from above... */
228 vfprintf(stderr, FormatBuffer, ArgumentPointer);
229 va_end(ArgumentPointer);
234 sys$exit(VMS_ExitCode);
238 /* This is a really slow and stupid 'bzero' implementation'... */
239 void slow_bzero(char *buffer, int numchars)
247 /* malloc some memory while checking for out-of-memory conditions. */
248 void *xmalloc(size_t Size)
250 void *Result = (void *) malloc(Size);
253 FatalError("cannot allocate %d bytes of memory\n", Size);
258 /* malloc some memory, zeroing it too: */
259 void *xzmalloc(size_t Size)
261 void *Result = (void *)xmalloc(Size);
263 slow_bzero(Result, Size);
268 int min(int x, int y)
270 return (x < y ? x : y);
274 int max(int x, int y)
276 return (x > y ? x : y);
280 /* Okay, this is a hack for the NSM modular jukebox series, which
281 * uses the "SEND DIAGNOSTIC" command to do shit.
284 int SendNSMHack(DEVICE_TYPE MediumChangerFD, NSM_Param_T *nsm_command,
285 int param_len, int timeout)
288 int list_len = param_len + sizeof(NSM_Param_T) - 2048;
290 /* Okay, now for the command: */
294 CDB[3] = (unsigned char)(list_len >> 8);
295 CDB[4] = (unsigned char)list_len;
300 dump_data(nsm_command,list_len);
303 /* Don't set us any timeout unless timeout is > 0 */
306 SCSI_Set_Timeout(timeout); /* 30 minutes, sigh! */
309 /* Now send the command: */
310 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, nsm_command, list_len, &scsi_error_sense))
312 return -1; /* we failed */
314 return 0; /* Did it! */
317 NSM_Result_T *RecNSMHack( DEVICE_TYPE MediumChangerFD,
318 int param_len, int timeout)
322 NSM_Result_T *retval = (NSM_Result_T *)xzmalloc(sizeof(NSM_Result_T));
324 int list_len = param_len + sizeof(NSM_Result_T) - 0xffff;
326 /* Okay, now for the command: */
330 CDB[3] = (unsigned char)(list_len >> 8);
331 CDB[4] = (unsigned char)list_len;
338 /* Don't set us any timeout unless timeout is > 0 */
341 SCSI_Set_Timeout(timeout);
344 /* Now send the command: */
345 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, retval, list_len, &scsi_error_sense))
347 return NULL; /* we failed */
352 "page_code=%02x page_len=%d command_code=%s\n",
354 (int) ((retval->page_len_msb << 8) + retval->page_len_lsb),
355 retval->command_code);
358 return retval; /* Did it! (maybe!)*/
361 /* Routine to inventory the library. Needed by, e.g., some Breece Hill
362 * loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
363 * has no parameters, such as a range to scan :-(.
366 int Inventory(DEVICE_TYPE MediumChangerFD)
370 /* okay, now for the command: */
372 CDB[1] = CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
374 /* set us a very long timeout, sigh... */
375 SCSI_Set_Timeout(30 * 60); /* 30 minutes, sigh! */
377 if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0)
380 PrintRequestSense(&scsi_error_sense);
381 fprintf(stderr, "Initialize Element Status (0x07) failed\n");
383 return -1; /* could not do! */
385 return 0; /* did do! */
388 /* Routine to read the Mode Sense Element Address Assignment Page */
389 /* We try to read the page. If we can't read the page, we return NULL.
390 * Our caller really isn't too worried about why we could not read the
391 * page, it will simply default to some kind of default values.
393 ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD)
396 ElementModeSense_T *retval; /* processed SCSI. */
397 unsigned char input_buffer[136];
398 ElementModeSensePage_T *sense_page; /* raw SCSI. */
400 /* okay, now for the command: */
401 CDB[0] = 0x1A; /* Mode Sense(6) */
403 CDB[2] = 0x1D; /* Mode Sense Element Address Assignment Page */
405 CDB[4] = 136; /* allocation_length... */
408 /* clear the data buffer: */
409 slow_bzero((char *)&scsi_error_sense, sizeof(RequestSense_T));
410 slow_bzero((char *)input_buffer, sizeof(input_buffer));
412 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6,
413 &input_buffer, sizeof(input_buffer), &scsi_error_sense) != 0)
415 PrintRequestSense(&scsi_error_sense);
416 fprintf(stderr,"Mode sense (0x1A) for Page 0x1D failed\n");
418 return NULL; /* sorry, couldn't do it. */
421 /* Could do it, now build return value: */
423 #ifdef DEBUG_MODE_SENSE
424 PrintHex(0, input_buffer, 30);
427 /* first, skip past: mode data header, and block descriptor header if any */
428 sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
430 #ifdef DEBUG_MODE_SENSE
431 fprintf(stderr,"*sense_page=%x %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
435 retval = (ElementModeSense_T *)xzmalloc(sizeof(ElementModeSense_T));
437 /* Remember that all SCSI values are big-endian: */
438 retval->MediumTransportStart =
439 (((int)sense_page->MediumTransportStartHi) << 8) +
440 sense_page->MediumTransportStartLo;
442 retval->NumMediumTransport =
443 (((int)(sense_page->NumMediumTransportHi))<<8) +
444 sense_page->NumMediumTransportLo;
446 /* HACK! Some HP autochangers don't report NumMediumTransport right! */
447 /* ARG! TAKE OUT THE %#@!# HACK! */
448 #ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
449 if (!retval->NumMediumTransport)
451 retval->NumMediumTransport = 1;
455 #ifdef DEBUG_MODE_SENSE
456 fprintf(stderr, "rawNumStorage= %d %d rawNumImportExport= %d %d\n",
457 sense_page->NumStorageHi, sense_page->NumStorageLo,
458 sense_page->NumImportExportHi, sense_page->NumImportExportLo);
459 fprintf(stderr, "rawNumTransport=%d %d rawNumDataTransfer=%d %d\n",
460 sense_page->NumMediumTransportHi, sense_page->NumMediumTransportLo,
461 sense_page->NumDataTransferHi, sense_page->NumDataTransferLo);
465 retval->StorageStart =
466 ((int)sense_page->StorageStartHi << 8) + sense_page->StorageStartLo;
469 ((int)sense_page->NumStorageHi << 8) + sense_page->NumStorageLo;
471 retval->ImportExportStart =
472 ((int)sense_page->ImportExportStartHi << 8) + sense_page->ImportExportStartLo;
474 retval->NumImportExport =
475 ((int)sense_page->NumImportExportHi << 8) + sense_page->NumImportExportLo;
477 retval->DataTransferStart =
478 ((int)sense_page->DataTransferStartHi << 8) + sense_page->DataTransferStartLo;
480 retval->NumDataTransfer =
481 ((int)sense_page->NumDataTransferHi << 8) + sense_page->NumDataTransferLo;
483 /* allocate a couple spares 'cause some HP autochangers and maybe others
484 * don't properly report the robotics arm(s) count here...
486 retval->NumElements =
487 retval->NumStorage+retval->NumImportExport +
488 retval->NumDataTransfer+retval->NumMediumTransport;
490 retval->MaxReadElementStatusData =
491 (sizeof(ElementStatusDataHeader_T) +
492 4 * sizeof(ElementStatusPage_T) +
493 retval->NumElements * sizeof(TransportElementDescriptor_T));
495 #ifdef IMPORT_EXPORT_HACK
496 retval->NumStorage = retval->NumStorage+retval->NumImportExport;
499 #ifdef DEBUG_MODE_SENSE
500 fprintf(stderr, "NumMediumTransport=%d\n", retval->NumMediumTransport);
501 fprintf(stderr, "NumStorage=%d\n", retval->NumStorage);
502 fprintf(stderr, "NumImportExport=%d\n", retval->NumImportExport);
503 fprintf(stderr, "NumDataTransfer=%d\n", retval->NumDataTransfer);
504 fprintf(stderr, "MaxReadElementStatusData=%d\n", retval->MaxReadElementStatusData);
505 fprintf(stderr, "NumElements=%d\n", retval->NumElements);
512 static void FreeElementData(ElementStatus_T *data)
514 free(data->DataTransferElementAddress);
515 free(data->DataTransferElementSourceStorageElementNumber);
516 free(data->DataTransferPrimaryVolumeTag);
517 free(data->DataTransferAlternateVolumeTag);
518 free(data->PrimaryVolumeTag);
519 free(data->AlternateVolumeTag);
520 free(data->StorageElementAddress);
521 free(data->StorageElementIsImportExport);
522 free(data->StorageElementFull);
523 free(data->DataTransferElementFull);
529 static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense)
531 ElementStatus_T *retval;
533 retval = (ElementStatus_T *)xzmalloc(sizeof(ElementStatus_T));
535 /* now for the invidual component arrays.... */
537 retval->DataTransferElementAddress =
538 (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
539 retval->DataTransferElementSourceStorageElementNumber =
540 (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
541 retval->DataTransferPrimaryVolumeTag =
542 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
543 retval->DataTransferAlternateVolumeTag =
544 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
545 retval->PrimaryVolumeTag =
546 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
547 retval->AlternateVolumeTag =
548 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
549 retval->StorageElementAddress =
550 (int *)xzmalloc(sizeof(int) * (mode_sense->NumStorage + 1));
551 retval->StorageElementIsImportExport =
552 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
553 retval->StorageElementFull =
554 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
555 retval->DataTransferElementFull =
556 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumDataTransfer + 1));
558 return retval; /* sigh! */
562 void copy_barcode(unsigned char *src, unsigned char *dest)
566 for (i=0; i < 36; i++)
570 if ((*dest < 32) || (*dest > 127))
577 *dest = 0; /* null-terminate */
580 /* This #%!@# routine has more parameters than I can count! */
581 static unsigned char *SendElementStatusRequestActual(
582 DEVICE_TYPE MediumChangerFD,
583 RequestSense_T *RequestSense,
584 Inquiry_T *inquiry_info,
592 boolean is_attached = false;
594 unsigned char *DataBuffer; /* size of data... */
596 #ifdef HAVE_GET_ID_LUN
599 if (inquiry_info->MChngr &&
600 inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
605 if (flags->no_attached)
611 DataBuffer = (unsigned char *)xzmalloc(NumBytes + 1);
613 slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
615 #ifdef HAVE_GET_ID_LUN
616 scsi_id = SCSI_GetIDLun(MediumChangerFD);
619 CDB[0] = 0xB8; /* READ ELEMENT STATUS */
623 CDB[0] = 0xB4; /* whoops, READ_ELEMENT_STATUS_ATTACHED! */
626 #ifdef HAVE_GET_ID_LUN
627 CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ?
628 0 : 0x10) | flags->elementtype; /* Lun + VolTag + Type code */
631 /* Element Type Code = 0, VolTag = 1 */
632 CDB[1] = (unsigned char)((flags->no_barcodes ? 0 : 0x10) | flags->elementtype);
634 /* Starting Element Address */
635 CDB[2] = (unsigned char)(ElementStart >> 8);
636 CDB[3] = (unsigned char)ElementStart;
638 /* Number Of Elements */
639 CDB[4]= (unsigned char)(NumElements >> 8);
640 CDB[5]= (unsigned char)NumElements;
642 CDB[6] = 0; /* Reserved */
644 /* allocation length */
645 CDB[7]= (unsigned char)(NumBytes >> 16);
646 CDB[8]= (unsigned char)(NumBytes >> 8);
647 CDB[9]= (unsigned char)NumBytes;
649 CDB[10] = 0; /* Reserved */
650 CDB[11] = 0; /* Control */
653 fprintf(stderr,"CDB:\n");
654 PrintHex(2, CDB, 12);
657 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
658 DataBuffer,NumBytes, RequestSense) != 0)
662 fprintf(stderr, "Read Element Status (0x%02X) failed\n", CDB[0]);
666 First see if we have sense key of 'illegal request',
667 additional sense code of '24', additional sense qualfier of
668 '0', and field in error of '4'. This means that we issued a request
669 w/bar code reader and did not have one, thus must re-issue the request
674 Most autochangers and tape libraries set a sense key here if
675 they do not have a bar code reader. For some reason, the ADIC DAT
676 uses a different sense key? Let's retry w/o bar codes for *ALL*
680 if (RequestSense->SenseKey > 1)
682 /* we issued a code requesting bar codes, there is no bar code reader? */
683 /* clear out our sense buffer first... */
684 slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
686 CDB[1] &= ~0x10; /* clear bar code flag! */
689 fprintf(stderr,"CDB:\n");
690 PrintHex(2, CDB, 12);
693 if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
694 DataBuffer, NumBytes, RequestSense) != 0)
708 /* print a bunch of extra debug data :-(. */
709 PrintRequestSense(RequestSense); /* see what it sez :-(. */
710 fprintf(stderr,"Data:\n");
711 PrintHex(2, DataBuffer, 40);
713 return DataBuffer; /* we succeeded! */
717 unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
718 RequestSense_T *RequestSense,
719 Inquiry_T *inquiry_info,
726 unsigned char *DataBuffer; /* size of data... */
729 DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
738 One weird loader wants either 8 or BYTE_COUNT_OF_REPORT
739 values for the ALLOCATION_LENGTH. Give it what it wants
740 if we get an Sense Key of 05 Illegal Request with a
741 CDB position of 7 as the field in error.
744 if (DataBuffer == NULL &&
745 RequestSense->SenseKey == 5 &&
746 RequestSense->CommandData &&
747 RequestSense->BitPointer == 7)
749 NumBytes=8; /* send an 8 byte request */
750 DataBuffer=SendElementStatusRequestActual( MediumChangerFD,
760 /* the above code falls thru into this: */
761 if (DataBuffer != NULL)
763 /* see if we need to retry with a bigger NumBytes: */
764 real_numbytes = ((int)DataBuffer[5] << 16) +
765 ((int)DataBuffer[6] << 8) +
766 (int)DataBuffer[7] + 8;
768 if (real_numbytes > NumBytes)
771 free(DataBuffer); /* solve memory leak */
772 DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
788 /******************* ParseElementStatus ***********************************/
789 /* This does the actual grunt work of parsing element status data. It fills
790 * in appropriate pieces of its input data. It may be called multiple times
791 * while we are gathering element status.
794 static void ParseElementStatus( int *EmptyStorageElementAddress,
795 int *EmptyStorageElementCount,
796 unsigned char *DataBuffer,
797 ElementStatus_T *ElementStatus,
798 ElementModeSense_T *mode_sense,
802 unsigned char *DataPointer = DataBuffer;
803 TransportElementDescriptor_T TEBuf;
804 TransportElementDescriptor_T *TransportElementDescriptor;
805 ElementStatusDataHeader_T *ElementStatusDataHeader;
806 Element2StatusPage_T *ElementStatusPage;
807 Element2StatusPage_T ESBuf;
809 int TransportElementDescriptorLength;
811 int ImportExportIndex;
813 ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
814 DataPointer += sizeof(ElementStatusDataHeader_T);
816 BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
819 fprintf(stderr,"ElementCount=%d\n",ElementCount);
823 while (ElementCount > 0)
826 int got_element_num=0;
828 fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
832 memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
833 ElementStatusPage = &ESBuf;
834 DataPointer += sizeof(ElementStatusPage_T);
836 TransportElementDescriptorLength =
837 BigEndian16(ElementStatusPage->ElementDescriptorLength);
839 if (TransportElementDescriptorLength <
840 sizeof(TransportElementDescriptorShort_T))
842 /* Foo, Storage Element Descriptors can be 4 bytes?! */
843 if ((ElementStatusPage->ElementTypeCode != MediumTransportElement &&
844 ElementStatusPage->ElementTypeCode != StorageElement &&
845 ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
846 TransportElementDescriptorLength < 4)
849 fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
851 FatalError("Transport Element Descriptor Length too short: %d\n", TransportElementDescriptorLength);
855 fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
858 BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
860 fprintf(stderr,"%d bytes of descriptor data available in descriptor\n",
863 /* work around a bug in ADIC DAT loaders */
864 if (BytesAvailable <= 0)
866 ElementCount--; /* sorry :-( */
868 while (BytesAvailable > 0)
870 /* TransportElementDescriptor =
871 (TransportElementDescriptor_T *) DataPointer; */
872 memcpy(&TEBuf, DataPointer,
873 (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
874 TransportElementDescriptorLength :
876 TransportElementDescriptor = &TEBuf;
878 if (pNextElement != NULL)
880 if (BigEndian16(TransportElementDescriptor->ElementAddress) != 0 || *pNextElement == 0)
882 (*pNextElement) = BigEndian16(TransportElementDescriptor->ElementAddress) + 1;
890 DataPointer += TransportElementDescriptorLength;
891 BytesAvailable -= TransportElementDescriptorLength;
894 switch (ElementStatusPage->ElementTypeCode)
896 case MediumTransportElement:
897 ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
899 fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress);
903 /* we treat ImportExport elements as if they were normal
904 * storage elements now, sigh...
906 case ImportExportElement:
908 fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
910 if (ElementStatus->ImportExportCount >= mode_sense->NumImportExport)
912 fprintf(stderr,"Warning:Too Many Import/Export Elements Reported (expected %d, now have %d\n",
913 mode_sense->NumImportExport,
914 ElementStatus->ImportExportCount + 1);
916 return; /* we're done :-(. */
919 ImportExportIndex = mode_sense->NumStorage - mode_sense->NumImportExport + ElementStatus->ImportExportCount;
921 ElementStatus->StorageElementAddress[ImportExportIndex] =
922 BigEndian16(TransportElementDescriptor->ElementAddress);
923 ElementStatus->StorageElementFull[ImportExportIndex] =
924 TransportElementDescriptor->Full;
926 if ( (TransportElementDescriptorLength > 11) &&
927 (ElementStatusPage->VolBits & E2_AVOLTAG))
929 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
930 ElementStatus->AlternateVolumeTag[ImportExportIndex]);
934 ElementStatus->AlternateVolumeTag[ImportExportIndex][0] = 0; /* null string. */;
936 if ((TransportElementDescriptorLength > 11) &&
937 (ElementStatusPage->VolBits & E2_PVOLTAG))
939 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
940 ElementStatus->PrimaryVolumeTag[ImportExportIndex]);
944 ElementStatus->PrimaryVolumeTag[ImportExportIndex][0]=0; /* null string. */
947 ElementStatus->StorageElementIsImportExport[ImportExportIndex] = 1;
949 ElementStatus->ImportExportCount++;
954 fprintf(stderr,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
956 /* ATL/Exabyte kludge -- skip slots that aren't installed :-( */
957 if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
958 TransportElementDescriptor->AdditionalSenseCodeQualifier==0x02)
961 ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
962 BigEndian16(TransportElementDescriptor->ElementAddress);
963 ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
964 TransportElementDescriptor->Full;
966 if (TransportElementDescriptor->Except)
967 fprintf(stderr,"ASC,ASCQ = 0x%x,0x%x ",TransportElementDescriptor->AdditionalSenseCode,TransportElementDescriptor->AdditionalSenseCodeQualifier);
968 fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
970 if (!TransportElementDescriptor->Full)
972 EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
973 ElementStatus->StorageElementCount; /* slot idx. */
974 /* ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
976 if ((TransportElementDescriptorLength > 11) &&
977 (ElementStatusPage->VolBits & E2_AVOLTAG))
979 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
980 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
984 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */;
986 if ((TransportElementDescriptorLength > 11) &&
987 (ElementStatusPage->VolBits & E2_PVOLTAG))
989 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
990 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
994 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
997 ElementStatus->StorageElementCount++;
999 Note that the original mtx had no check here for
1000 buffer overflow, though some drives might mistakingly
1004 if (ElementStatus->StorageElementCount > mode_sense->NumStorage)
1006 fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
1007 mode_sense->NumStorage,
1008 ElementStatus->StorageElementCount);
1010 return; /* we're done :-(. */
1014 case DataTransferElement:
1015 /* tape drive not installed, go back to top of loop */
1017 /* if (TransportElementDescriptor->Except) continue ; */
1019 /* Note: This is for Exabyte tape libraries that improperly
1020 report that they have a 2nd tape drive when they don't. We
1021 could generalize this in an ideal world, but my attempt to
1022 do so failed with dual-drive Exabyte tape libraries that
1023 *DID* have the second drive. Sigh.
1025 if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
1026 TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
1031 /* generalize it. Does it work? Let's try it! */
1033 No, dammit, following does not work on dual-drive Exabyte
1034 'cause if a tape is in the drive, it sets the AdditionalSense
1035 code to something (sigh).
1037 /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
1041 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
1042 BigEndian16(TransportElementDescriptor->ElementAddress);
1043 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] =
1044 TransportElementDescriptor->Full;
1045 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
1046 BigEndian16(TransportElementDescriptor->SourceStorageElementAddress);
1049 fprintf(stderr, "%d: ElementAddress = %d, Full = %d, SourceElement = %d\n",
1050 ElementStatus->DataTransferElementCount,
1051 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount],
1052 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount],
1053 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount]);
1055 if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer)
1057 FatalError("Too many Data Transfer Elements Reported\n");
1060 if (ElementStatusPage->VolBits & E2_PVOLTAG)
1062 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
1063 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
1067 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
1070 if (ElementStatusPage->VolBits & E2_AVOLTAG)
1072 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
1073 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
1077 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
1080 ElementStatus->DataTransferElementCount++;
1082 /* 0 actually is a usable element address */
1083 /* if (DataTransferElementAddress == 0) */
1085 /* "illegal Data Transfer Element Address %d reported\n", */
1086 /* DataTransferElementAddress); */
1090 FatalError("illegal Element Type Code %d reported\n",
1091 ElementStatusPage->ElementTypeCode);
1098 fprintf(stderr,"Next start element will be %d\n",*pNextElement);
1103 /********************* Real ReadElementStatus ********************* */
1106 * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
1107 * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
1108 * under Linux, which gets around the @#%@ 4k buffer size in Linux.
1109 * We still have the restriction that Linux cuts off the last two
1110 * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
1111 * verbose widget won't work :-(.
1113 * We now look for that "attached" bit in the inquiry_info to see whether
1114 * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
1115 * look at the device type in the inquiry_info to see whether it is a media
1116 * changer or tape device, and if it's a media changer device, we ignore the
1117 * attached bit (one beta tester found an old 4-tape DAT changer that set
1118 * the attached bit for both the tape device AND the media changer device).
1122 ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1124 ElementStatus_T *ElementStatus;
1126 unsigned char *DataBuffer; /* size of data... */
1128 int EmptyStorageElementCount=0;
1129 int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
1132 boolean is_attached = false;
1135 ElementModeSense_T *mode_sense = NULL;
1137 if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
1142 if (flags->no_attached)
1144 /* override, sigh */
1145 is_attached = false;
1150 mode_sense = ReadAssignmentPage(MediumChangerFD);
1155 mode_sense = (ElementModeSense_T *)xmalloc(sizeof(ElementModeSense_T));
1156 mode_sense->NumMediumTransport = MAX_TRANSPORT_ELEMENTS;
1157 mode_sense->NumStorage = MAX_STORAGE_ELEMENTS;
1158 mode_sense->NumDataTransfer = MAX_TRANSFER_ELEMENTS;
1159 mode_sense->MaxReadElementStatusData =
1160 (sizeof(ElementStatusDataHeader_T) + 3 * sizeof(ElementStatusPage_T) +
1161 (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS) *
1162 sizeof(TransportElementDescriptor_T));
1164 /* don't care about the others anyhow at the moment... */
1167 ElementStatus = AllocateElementData(mode_sense);
1169 /* Now to initialize it (sigh). */
1170 ElementStatus->StorageElementCount = 0;
1171 ElementStatus->DataTransferElementCount = 0;
1173 /* first, allocate some empty storage stuff: Note that we pass this
1174 * down to ParseElementStatus (sigh!)
1177 EmptyStorageElementAddress = (int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
1178 for (i = 0; i < mode_sense->NumStorage; i++)
1180 EmptyStorageElementAddress[i] = -1;
1183 /* Okay, now to send some requests for the various types of stuff: */
1185 /* -----------STORAGE ELEMENTS---------------- */
1186 /* Let's start with storage elements: */
1188 for (i = 0; i < mode_sense->NumDataTransfer; i++)
1190 /* initialize them to an illegal # so that we can fix later... */
1191 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
1194 if (flags->querytype == MTX_ELEMENTSTATUS_ORIGINAL)
1197 fprintf(stderr,"Using original element status polling method (storage, import/export, drivers etc independantly)\n");
1199 flags->elementtype = StorageElement; /* sigh! */
1200 DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
1201 inquiry_info, flags,
1202 mode_sense->StorageStart,
1203 /* adjust for import/export. */
1204 mode_sense->NumStorage - mode_sense->NumImportExport,
1205 mode_sense->MaxReadElementStatusData);
1210 fprintf(stderr,"Had no elements!\n");
1212 /* darn. Free up stuff and return. */
1213 #ifdef DEBUG_MODE_SENSE
1214 PrintRequestSense(RequestSense);
1216 FreeElementData(ElementStatus);
1221 fprintf(stderr, "Parsing storage elements\n");
1223 ParseElementStatus(EmptyStorageElementAddress, &EmptyStorageElementCount,
1224 DataBuffer,ElementStatus,mode_sense,NULL);
1226 free(DataBuffer); /* sigh! */
1228 /* --------------IMPORT/EXPORT--------------- */
1229 /* Next let's see if we need to do Import/Export: */
1230 if (mode_sense->NumImportExport > 0)
1233 fprintf(stderr,"Sending request for Import/Export status\n");
1235 flags->elementtype = ImportExportElement;
1236 DataBuffer = SendElementStatusRequest( MediumChangerFD,RequestSense,
1237 inquiry_info, flags,
1238 mode_sense->ImportExportStart,
1239 mode_sense->NumImportExport,
1240 mode_sense->MaxReadElementStatusData);
1245 fprintf(stderr,"Had no input/export element!\n");
1247 /* darn. Free up stuff and return. */
1248 #ifdef DEBUG_MODE_SENSE
1249 PrintRequestSense(RequestSense);
1251 FreeElementData(ElementStatus);
1255 fprintf(stderr,"Parsing inport/export element status\n");
1258 dump_data(DataBuffer, 100); /* dump some data :-(. */
1260 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1261 DataBuffer, ElementStatus, mode_sense, NULL);
1263 ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
1266 /* ----------------- DRIVES ---------------------- */
1269 fprintf(stderr,"Sending request for data transfer element (drive) status\n");
1271 flags->elementtype = DataTransferElement; /* sigh! */
1272 DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
1273 inquiry_info, flags,
1274 mode_sense->DataTransferStart,
1275 mode_sense->NumDataTransfer,
1276 mode_sense->MaxReadElementStatusData);
1280 fprintf(stderr,"No data transfer element status.");
1282 /* darn. Free up stuff and return. */
1283 #ifdef DEBUG_MODE_SENSE
1284 PrintRequestSense(RequestSense);
1286 FreeElementData(ElementStatus);
1291 fprintf(stderr,"Parsing data for data transfer element (drive) status\n");
1293 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1294 DataBuffer,ElementStatus, mode_sense, NULL);
1296 free(DataBuffer); /* sigh! */
1298 /* ----------------- Robot Arm(s) -------------------------- */
1300 /* grr, damned brain dead HP doesn't report that it has any! */
1301 if (!mode_sense->NumMediumTransport)
1303 ElementStatus->TransportElementAddress = 0; /* default it sensibly :-(. */
1308 fprintf(stderr,"Sending request for robot arm status\n");
1310 flags->elementtype = MediumTransportElement; /* sigh! */
1311 DataBuffer = SendElementStatusRequest( MediumChangerFD, RequestSense,
1312 inquiry_info, flags,
1313 mode_sense->MediumTransportStart,
1314 1, /* only get 1, sigh. */
1315 mode_sense->MaxReadElementStatusData);
1319 fprintf(stderr,"Loader reports no robot arm!\n");
1321 /* darn. Free up stuff and return. */
1322 #ifdef DEBUG_MODE_SENSE
1323 PrintRequestSense(RequestSense);
1325 FreeElementData(ElementStatus);
1329 fprintf(stderr,"Parsing robot arm data\n");
1331 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1332 DataBuffer, ElementStatus, mode_sense, NULL);
1339 int nLastEl=-1, nNextEl=0;
1342 fprintf(stderr,"Using alternative element status polling method (all elements)\n");
1344 /* ----------------- ALL Elements ---------------------- */
1345 /* Just keep asking for elements till no more are returned
1346 - increment our starting address as we go acording to the
1347 number of elements returned from the last call
1350 while(nLastEl!=nNextEl)
1352 flags->elementtype = AllElementTypes;//StorageElement; /* sigh! */ /*XL1B2 firewire changer does not seem to respond to specific types so just use all elements*/
1353 DataBuffer = SendElementStatusRequest( MediumChangerFD,
1357 nNextEl,//mode_sense->StorageStart,
1358 /* adjust for import/export. */
1359 mode_sense->NumStorage - mode_sense->NumImportExport,//FIX ME:this should be a more sensible value
1360 mode_sense->MaxReadElementStatusData);
1363 if (RequestSense->AdditionalSenseCode == 0x21 &&
1364 RequestSense->AdditionalSenseCodeQualifier == 0x01)
1366 /* Error is invalid element address, we've probably just hit the end */
1370 /* darn. Free up stuff and return. */
1371 FreeElementData(ElementStatus);
1377 ParseElementStatus( EmptyStorageElementAddress, &EmptyStorageElementCount,
1378 DataBuffer, ElementStatus, mode_sense, &nNextEl);
1380 free(DataBuffer); /* sigh! */
1383 ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
1386 /*---------------------- Sanity Checking ------------------- */
1388 if (ElementStatus->DataTransferElementCount == 0)
1389 FatalError("no Data Transfer Element reported\n");
1391 if (ElementStatus->StorageElementCount == 0)
1392 FatalError("no Storage Elements reported\n");
1395 /* ---------------------- Reset SourceStorageElementNumbers ------- */
1398 * Re-write the SourceStorageElementNumber code *AGAIN*.
1401 * Translate from raw element # to our translated # (if possible).
1402 * First, check the SourceStorageElementNumbers against the list of
1403 * filled slots. If the slots indicated are empty, we accept that list as
1404 * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
1407 * If we had some invalid (or unknown) SourceStorageElementNumbers
1408 * then we must search for free slots, and assign SourceStorageElementNumbers
1409 * to those free slots. We happen to already built a list of free
1410 * slots as part of the process of reading the storage element numbers
1411 * from the tape. So that's easy enough to do!
1414 #ifdef DEBUG_TAPELIST
1415 fprintf(stderr, "empty slots: %d\n", EmptyStorageElementCount);
1416 if (EmptyStorageElementCount)
1418 for (i = 0; i < EmptyStorageElementCount; i++)
1420 fprintf(stderr, "empty: %d\n", EmptyStorageElementAddress[i]);
1426 * Now we re-assign origin slots if the "real" origin slot
1427 * is obviously defective:
1430 for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
1434 /* if we have an element, then ... */
1435 if (ElementStatus->DataTransferElementFull[i])
1437 elnum = ElementStatus->DataTransferElementSourceStorageElementNumber[i];
1438 /* if we have an element number, then ... */
1441 /* Now to translate the elnum: */
1442 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
1443 for (j = 0; j < ElementStatus->StorageElementCount; j++)
1445 if (elnum == ElementStatus->StorageElementAddress[j])
1447 /* now see if the element # is already occupied:*/
1448 if (!ElementStatus->StorageElementFull[j])
1450 /* properly set the source... */
1451 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = j;
1460 /* We have invalid sources, so let's see what they should be: */
1461 /* Note: If EmptyStorageElementCount is < # of drives, the leftover
1462 * drives will be assigned a -1 (see the initialization loop for
1463 * EmptyStorageElementAddress above), which will be reported as "slot 0"
1464 * by the user interface. This is an invalid value, but more useful for us
1465 * to have than just crapping out here :-(.
1468 for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
1470 if (ElementStatus->DataTransferElementFull[i] &&
1471 ElementStatus->DataTransferElementSourceStorageElementNumber[i] < 0)
1473 #ifdef DEBUG_TAPELIST
1474 fprintf(stderr,"for drive %d, changing to %d (empty slot #%d)\n",
1476 EmptyStorageElementAddress[empty_idx],
1479 ElementStatus->DataTransferElementSourceStorageElementNumber[i] =
1480 EmptyStorageElementAddress[empty_idx++];
1486 free(EmptyStorageElementAddress);
1487 return ElementStatus;
1490 /*************************************************************************/
1492 RequestSense_T *PositionElement(DEVICE_TYPE MediumChangerFD,
1493 int DestinationAddress,
1494 ElementStatus_T *ElementStatus)
1496 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1501 CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1502 CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1503 CDB[4] = (unsigned char)(DestinationAddress >> 8);
1504 CDB[5] = (unsigned char)DestinationAddress;
1510 if(SCSI_ExecuteCommand( MediumChangerFD, Output, &CDB, 10,
1511 NULL, 0, RequestSense) != 0)
1513 return RequestSense;
1516 return NULL; /* success */
1520 /* Now the actual media movement routine! */
1521 RequestSense_T *MoveMedium( DEVICE_TYPE MediumChangerFD, int SourceAddress,
1522 int DestinationAddress,
1523 ElementStatus_T *ElementStatus,
1524 Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1526 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1529 if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
1531 /* if using the ATTACHED API */
1532 CDB[0] = 0xA7; /* MOVE_MEDIUM_ATTACHED */
1536 CDB[0] = 0xA5; /* MOVE MEDIUM */
1539 CDB[1] = 0; /* Reserved */
1541 /* Transport Element Address */
1542 CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1543 CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1545 /* Source Address */
1546 CDB[4] = (unsigned char)(SourceAddress >> 8);
1547 CDB[5] = (unsigned char)SourceAddress;
1549 /* Destination Address */
1550 CDB[6] = (unsigned char)(DestinationAddress >> 8);
1551 CDB[7] = (unsigned char)DestinationAddress;
1553 CDB[8] = 0; /* Reserved */
1554 CDB[9] = 0; /* Reserved */
1558 CDB[10] = 1; /* Reserved */
1564 /* eepos controls the tray for import/export elements, sometimes. */
1565 CDB[11] = flags->eepos << 6; /* Control */
1567 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1568 NULL, 0, RequestSense) != 0)
1571 fprintf(stderr, "Move Medium (0x%02X) failed\n", CDB[0]);
1573 return RequestSense;
1577 return NULL; /* success! */
1581 /* Now the actual Exchange Medium routine! */
1582 RequestSense_T *ExchangeMedium( DEVICE_TYPE MediumChangerFD, int SourceAddress,
1583 int DestinationAddress, int Dest2Address,
1584 ElementStatus_T *ElementStatus,
1585 SCSI_Flags_T *flags)
1587 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1590 CDB[0] = 0xA6; /* EXCHANGE MEDIUM */
1591 CDB[1] = 0; /* Reserved */
1593 /* Transport Element Address */
1594 CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1595 CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1597 /* Source Address */
1598 CDB[4] = (unsigned char)(SourceAddress >> 8);
1599 CDB[5] = (unsigned char)SourceAddress;
1601 /* Destination Address */
1602 CDB[6] = (unsigned char)(DestinationAddress >> 8);
1603 CDB[7] = (unsigned char)DestinationAddress;
1605 /* move destination back to source? */
1606 CDB[8] = (unsigned char)(Dest2Address >> 8);
1607 CDB[9] = (unsigned char)Dest2Address;
1612 CDB[10] |= 2; /* INV2 */
1617 CDB[1] |= 1; /* INV1 */
1620 /* eepos controls the tray for import/export elements, sometimes. */
1621 CDB[11] = flags->eepos << 6; /* Control */
1623 #ifdef DEBUG_EXCHANGE
1627 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1628 NULL, 0, RequestSense) != 0)
1630 return RequestSense;
1633 return NULL; /* success! */
1638 * for Linux, this creates a way to do a short erase... the @#$%@ st.c
1639 * driver defaults to doing a long erase!
1642 RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD)
1644 RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1648 CDB[1] = 0; /* Short! */
1649 CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
1651 if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
1652 NULL, 0, RequestSense) != 0)
1655 fprintf(stderr, "Erase (0x19) failed\n");
1657 return RequestSense;
1661 return NULL; /* Success! */
1664 /* Routine to send an LOAD/UNLOAD from the MMC/SSC spec to a device.
1665 * For tapes and changers this can be used either to eject a tape
1666 * or to eject a magazine (on some Seagate changers, when sent to LUN 1 ).
1667 * For CD/DVDs this is used to Load or Unload a disc which is required by
1668 * some media changers.
1671 int LoadUnload(DEVICE_TYPE fd, int bLoad)
1674 /* okay, now for the command: */
1677 CDB[4] = bLoad ? 3 : 2;
1678 CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1680 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
1682 #ifdef DEBUG_MODE_SENSE
1683 PrintRequestSense(&scsi_error_sense);
1684 fprintf(stderr, "Eject (0x1B) failed\n");
1686 return -1; /* could not do! */
1688 return 0; /* did do! */
1691 /* Routine to send an START/STOP from the MMC/SSC spec to a device.
1692 * For tape drives this may be required prior to using the changer
1693 * Load or Unload commands.
1694 * For CD/DVD drives this is used to Load or Unload a disc which may be
1695 * required by some media changers.
1698 int StartStop(DEVICE_TYPE fd, int bStart)
1701 /* okay, now for the command: */
1704 CDB[4] = bStart ? 1 : 0;
1705 CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1707 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,NULL, 0, &scsi_error_sense) != 0)
1709 #ifdef DEBUG_MODE_SENSE
1710 PrintRequestSense(&scsi_error_sense);
1711 fprintf(stderr, "Eject (0x1B) failed\n");
1713 return -1; /* could not do! */
1715 return 0; /* did do! */
1718 /* Routine to send a LOCK/UNLOCK from the SSC/MMC spec to a device.
1719 * This can be used to prevent or allow the Tape or CD/DVD from being
1723 int LockUnlock(DEVICE_TYPE fd, int bLock)
1726 /* okay, now for the command: */
1729 CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1730 CDB[4] = (char)bLock;
1732 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
1734 #ifdef DEBUG_MODE_SENSE
1735 PrintRequestSense(&scsi_error_sense);
1736 fprintf(stderr, "Eject (0x1B) failed\n");
1738 return -1; /* could not do! */
1740 return 0; /* did do! */
1743 static char Spaces[] = " ";
1745 void PrintHex(int Indent, unsigned char *Buffer, int Length)
1752 for (idxBuffer = 0; idxBuffer < Length; idxBuffer++)
1754 if ((idxBuffer % 16) == 0)
1758 fputc('\'', stderr);
1760 for (idxAscii = idxBuffer - 16; idxAscii < idxBuffer; idxAscii++)
1762 cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x7F ? Buffer[idxAscii] : '.';
1763 fputc(cAscii, stderr);
1765 fputs("'\n", stderr);
1767 fprintf(stderr, "%.*s%04X: ", Indent, Spaces, idxBuffer);
1769 fprintf(stderr, "%02X ", (unsigned char)Buffer[idxBuffer]);
1772 PadLength = 16 - (Length % 16);
1776 fprintf(stderr, "%.*s'", 3 * PadLength, Spaces);
1778 for (idxAscii = idxBuffer - (16 - PadLength); idxAscii < idxBuffer; idxAscii++)
1780 cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x80 ? Buffer[idxAscii] : '.';
1781 fputc(cAscii, stderr);
1783 fputs("'\n", stderr);
1789 static char *sense_keys[] =
1791 "No Sense", /* 00 */
1792 "Recovered Error", /* 01 */
1793 "Not Ready", /* 02 */
1794 "Medium Error", /* 03 */
1795 "Hardware Error", /* 04 */
1796 "Illegal Request", /* 05 */
1797 "Unit Attention", /* 06 */
1798 "Data Protect", /* 07 */
1799 "Blank Check", /* 08 */
1802 "Aborted Command", /* 0b */
1804 "Volume Overflow", /* 0d */
1805 "Miscompare", /* 0e */
1809 static char Yes[] = "yes";
1810 static char No[] = "no";
1812 void PrintRequestSense(RequestSense_T *RequestSense)
1816 fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
1817 fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
1819 if (RequestSense->ErrorCode == 0x70)
1823 else if (RequestSense->ErrorCode == 0x71)
1832 fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n", RequestSense->ErrorCode, msg);
1833 fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n", sense_keys[RequestSense->SenseKey]);
1834 fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
1835 fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
1836 fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
1838 if (RequestSense->Valid)
1840 fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
1843 fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
1844 fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
1846 if (RequestSense->SKSV)
1848 fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n", RequestSense->BitPointer);
1851 fprintf(stderr, "mtx: Request Sense: BPV=%s\n", RequestSense->BPV ? Yes : No);
1852 fprintf(stderr, "mtx: Request Sense: Error in CDB=%s\n", RequestSense->CommandData ? Yes : No);
1853 fprintf(stderr, "mtx: Request Sense: SKSV=%s\n", RequestSense->SKSV ? Yes : No);
1855 if (RequestSense->BPV || RequestSense -> SKSV)
1857 fprintf(stderr, "mtx: Request Sense: Field Pointer = %02X %02X\n",
1858 RequestSense->FieldData[0], RequestSense->FieldData[1]);