f2350a49da04196c45da2872d8f2dd2f8e930fb5
[debian/mtx] / mtxl.c
1 /*  MTX -- SCSI Tape Attached Medium Changer Control Program
2
3   Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
4
5 $Date: 2002/02/05 16:51:11 $
6 $Revision: 1.8.2.3 $
7
8   This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
9   extracted from mtx.c, plus some additional routines. 
10
11   This program is free software; you may redistribute and/or modify it under
12   the terms of the GNU General Public License Version 2 as published by the
13   Free Software Foundation.
14
15   This program is distributed in the hope that it will be useful, but
16   WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
17   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18   for complete details.
19 */
20
21
22 /* FatalError: changed Feb. 2000 elg@estinc.com to eliminate a buffer
23    overflow :-(. That could be important if mtxl is SUID for some reason. 
24 */
25
26 #include "mtx.h"
27 #include "mtxl.h"
28
29 /* #define DEBUG_MODE_SENSE 1 */
30 /* #define DEBUG */
31 /* #define DEBUG_SCSI */
32
33 /* zap the following define when we finally add real import/export support */
34 #define IMPORT_EXPORT_HACK 1 /* for the moment, import/export == storage */
35
36 /* First, do some SCSI routines: */
37
38 /* the camlib is used on FreeBSD. */
39 #if HAVE_CAMLIB_H
40 #  include "scsi_freebsd.c"
41 #endif
42
43 /* the scsi_ctl interface is used on HP/UX. */
44 #if HAVE_SYS_SCSI_CTL_H
45 #  include "scsi_hpux.c"
46 #endif
47
48 /* the 'sg' interface is used on Linux. */
49 #if HAVE_SCSI_SG_H
50 #  include "scsi_linux.c"
51 #endif
52
53 /* The 'uscsi' interface is used on Solaris. */
54 #if HAVE_SYS_SCSI_IMPL_USCSI_H
55 #  include "scsi_sun.c"
56 #endif
57
58 /* The 'tm_buf' interface, as used on AIX. */
59 #ifdef HAVE_SYS_SCSI_H
60 #  include "scsi_aix.c"
61 #endif
62
63 /* The 'dslib' interface is used on SGI. */
64 #if HAVE_DSLIB_H
65 #  include "scsi_sgi.c"
66 #endif
67
68 /* Hmm, dunno what to do about Digital Unix at the moment. */
69 #ifdef DIGITAL_UNIX
70 #include "du/scsi.c"
71 #endif
72
73
74 #ifdef VMS
75 #include "[.vms]scsi.c"
76 #endif
77
78 extern char *argv0; /* something to let us do good error messages. */
79
80 /* Now for some low-level SCSI stuff: */
81
82 Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense) {
83   Inquiry_T *Inquiry;
84   CDB_T CDB;
85
86   Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));  
87   
88   CDB[0] = 0x12;                /* INQUIRY */
89   CDB[1] = 0;                   /* EVPD = 0 */
90   CDB[2] = 0;                   /* Page Code */
91   CDB[3] = 0;                   /* Reserved */
92   CDB[4] = sizeof(Inquiry_T);   /* Allocation Length */
93   CDB[5] = 0;                   /* Control */
94
95   /* set us a very short timeout, sigh... */
96   SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
97
98   if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
99                           Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
100     {
101       free(Inquiry);
102       return NULL;  /* sorry! */
103     }
104   return Inquiry;
105 }
106
107
108 int BigEndian16(unsigned char *BigEndianData)
109 {
110   return (BigEndianData[0] << 8) + BigEndianData[1];
111 }
112
113
114 int BigEndian24(unsigned char *BigEndianData)
115 {
116   return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
117 }
118
119
120
121
122
123 void FatalError(char *ErrorMessage, ...)
124 {
125 #define FORMAT_BUF_LEN 1024
126   char FormatBuffer[FORMAT_BUF_LEN];
127   char *SourcePointer;
128   char *TargetPointer = FormatBuffer;
129   int Character, LastCharacter = '\0';
130   int numchars = 0;
131
132   va_list ArgumentPointer;
133   va_start(ArgumentPointer, ErrorMessage);
134   /*  SourcePointer = "mtx: "; */
135   sprintf(TargetPointer,"%s: ",argv0);
136   numchars=strlen(TargetPointer);
137   
138   /* while ((Character = *SourcePointer++) != '\0') { */
139   /*  *TargetPointer++ = Character;                   */
140   /*  numchars++;                                     */
141   /*   }                                              */
142  
143   
144   while ((Character = *ErrorMessage++) != '\0')
145     if (LastCharacter == '%')
146       {
147         if (Character == 'm')
148           {
149             SourcePointer = strerror(errno);
150             while ((Character = *SourcePointer++) != '\0') {
151               *TargetPointer++ = Character;
152               numchars++;
153               if (numchars==FORMAT_BUF_LEN-1) break;  
154             }
155             if (numchars==FORMAT_BUF_LEN-1) break;  /* break outer loop... */
156           }
157         else
158           {
159             *TargetPointer++ = '%';
160             *TargetPointer++ = Character;
161             numchars++;
162             if (numchars==FORMAT_BUF_LEN-1) break;
163           }
164         LastCharacter = '\0';
165       }
166     else
167       {
168         if (Character != '%') {
169           *TargetPointer++ = Character;
170           numchars++;
171           if (numchars==FORMAT_BUF_LEN-1) break;
172         }
173         LastCharacter = Character;
174       }
175   *TargetPointer = '\0';  /* works even if we had to break from above... */
176   vfprintf(stderr, FormatBuffer, ArgumentPointer);
177   va_end(ArgumentPointer);
178 #ifndef VMS
179   exit(1);
180 #else
181   sys$exit(VMS_ExitCode);
182 #endif
183 }
184
185 /* This is a really slow and stupid 'bzero' implementation'... */
186 void slow_bzero(char *buffer, int numchars) {
187   while (numchars--) *buffer++ = 0;
188 }
189
190
191 /* malloc some memory while checking for out-of-memory conditions. */
192 void *xmalloc(size_t Size)
193 {
194   void *Result = (void *) malloc(Size);
195   if (Result == NULL)
196     FatalError("cannot allocate %d bytes of memory\n", Size);
197   return Result;
198 }
199
200 /* malloc some memory, zeroing it too: */
201 void *xzmalloc(size_t Size)
202 {
203   void *Result=(void *)xmalloc(Size);
204   
205   slow_bzero(Result,Size);
206   return Result;
207 }
208
209
210 int min(int x, int y)
211 {
212   return (x < y ? x : y);
213 }
214
215
216 int max(int x, int y)
217 {
218   return (x > y ? x : y);
219 }
220
221
222 /* Routine to inventory the library. Needed by, e.g., some Breece Hill
223  * loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
224  * has no parameters, such as a range to scan :-(. 
225  */
226
227 int Inventory(DEVICE_TYPE MediumChangerFD) {
228   CDB_T CDB;
229   RequestSense_T RequestSense;
230  
231   /* okay, now for the command: */
232   CDB[0]=0x07; 
233   CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
234
235   /* set us a very long timeout, sigh... */
236   SCSI_Set_Timeout(30*60); /* 30 minutes, sigh! */
237   
238   if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
239     return -1;  /* could not do! */
240   }
241   return 0; /* did do! */
242 }
243
244 /* Routine to send an UNLOAD from the SSC spec to a device. This can be
245  * used either to eject a tape (when sent to a tape drive), or to eject
246  * a magzine (on some Seagate changers, when sent to LUN 1 ). 
247  */
248
249 int Eject(DEVICE_TYPE fd) {
250   CDB_T CDB;
251   RequestSense_T RequestSense;
252   /* okay, now for the command: */
253   
254   CDB[0]=0x1b;
255   CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
256   
257   if (SCSI_ExecuteCommand(fd,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
258     return -1;  /* could not do! */
259   }
260   return 0; /* did do! */
261 }  
262
263 /* Routine to read the Mode Sense Element Address Assignment Page */
264 /* We try to read the page. If we can't read the page, we return NULL.
265  * Our caller really isn't too worried about why we could not read the
266  * page, it will simply default to some kind of default values. 
267  */ 
268 ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD) {
269   CDB_T CDB;
270   ElementModeSense_T *retval;  /* processed SCSI. */
271   unsigned char input_buffer[136];
272   ElementModeSensePage_T *sense_page; /* raw SCSI. */
273   RequestSense_T RequestSense;       /* see what retval of raw SCSI was... */
274   
275   /* okay, now for the command: */
276   CDB[0]=0x1a; /* Mode Sense(6) */
277   CDB[1]=0; 
278   CDB[2]=0x1d; /* Mode Sense Element Address Assignment Page */
279   CDB[3]=0;
280   CDB[4]=136; /* allocation_length... */
281   CDB[5]=0;
282
283   /* clear the data buffer: */
284   slow_bzero((char *)&RequestSense,sizeof(RequestSense_T));
285   slow_bzero((char *)input_buffer,sizeof(input_buffer));
286
287   if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,
288                           &input_buffer,sizeof(input_buffer),&RequestSense) != 0) {
289 #ifdef DEBUG_MODE_SENSE
290     fprintf(stderr,"Could not execute mode sense...\n");
291     fflush(stderr);
292 #endif
293     return NULL; /* sorry, couldn't do it. */
294   }
295
296   /* Could do it, now build return value: */
297
298 #ifdef DEBUG_MODE_SENSE
299   {
300     int i;
301     for (i=0;i<30;i+=3) {
302       fprintf(stderr,"ib[%d]=%d ib[%d]=%d ib[%d]=%d\n",
303               i,input_buffer[i],i+1,input_buffer[i+1],
304               i+2,input_buffer[i+2]);
305       /*  fprintf(stderr,"input_buffer[0]=%d input_buffer[3]=%d\n",
306        *          input_buffer[0], input_buffer[3]);
307        */
308     }
309     fflush(stderr);
310   }
311 #endif
312   /* first, skip past: mode data header, and block descriptor header if any */
313   sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
314
315 #ifdef DEBUG_MODE_SENSE
316   fprintf(stderr,"*sense_page=%x  %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
317   fflush(stderr);
318 #endif  
319
320   retval=(ElementModeSense_T *) xzmalloc(sizeof(ElementModeSense_T));
321
322   /* Remember that all SCSI values are big-endian: */
323   retval->MediumTransportStart=(((int)sense_page->MediumTransportStartHi)<<8) +
324     sense_page->MediumTransportStartLo;
325   retval->NumMediumTransport=(((int)(sense_page->NumMediumTransportHi))<<8) +
326     sense_page->NumMediumTransportLo;
327
328   /* HACK! Some HP autochangers don't report NumMediumTransport right! */
329   /* ARG! TAKE OUT THE %#@!# HACK! */
330 #ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
331   if (!retval->NumMediumTransport) {
332     retval->NumMediumTransport=1;
333   }
334 #endif
335
336 #ifdef DEBUG_MODE_SENSE
337   fprintf(stderr,"rawNumStorage= %d %d    rawNumImportExport= %d %d\n",
338           sense_page->NumStorageHi,sense_page->NumStorageLo,
339           sense_page->NumImportExportHi, sense_page->NumImportExportLo);
340   fprintf(stderr,"rawNumTransport=%d %d  rawNumDataTransfer=%d %d\n",
341           sense_page->NumMediumTransportHi,sense_page->NumMediumTransportLo,
342           sense_page->NumDataTransferHi,sense_page->NumDataTransferLo);
343   fflush(stderr);
344 #endif
345
346   retval->StorageStart=(((int)(sense_page->StorageStartHi))<<8) +
347     sense_page->StorageStartLo;
348   retval->NumStorage=(((int)(sense_page->NumStorageHi))<<8) + 
349     sense_page->NumStorageLo;
350   retval->ImportExportStart=(((int)(sense_page->ImportExportStartHi))<<8)+
351     sense_page->ImportExportStartLo; 
352   retval->NumImportExport=(((int)(sense_page->NumImportExportHi))<<8)+
353     sense_page->NumImportExportLo;
354   retval->DataTransferStart=(((int)(sense_page->DataTransferStartHi))<<8)+
355     sense_page->DataTransferStartLo;
356   retval->NumDataTransfer=(((int)(sense_page->NumDataTransferHi))<<8)+
357     sense_page->NumDataTransferLo;
358
359   /* allocate a couple spares 'cause some HP autochangers and maybe others
360    * don't properly report the robotics arm(s) count here... 
361    */
362   retval->NumElements=retval->NumStorage+retval->NumImportExport+
363     retval->NumDataTransfer+retval->NumMediumTransport;
364
365   retval->MaxReadElementStatusData=(sizeof(ElementStatusDataHeader_T) +
366                                     4 * sizeof(ElementStatusPage_T) +
367                                     (retval->NumElements) *
368                                       sizeof(TransportElementDescriptor_T));
369
370
371 #ifdef IMPORT_EXPORT_HACK
372   retval->NumStorage=retval->NumStorage+retval->NumImportExport;
373 #endif
374
375 #ifdef DEBUG_MODE_SENSE
376   fprintf(stderr,"NumMediumTransport=%d\n",retval->NumMediumTransport);
377   fprintf(stderr,"NumStorage=%d\n",retval->NumStorage);
378   fprintf(stderr,"NumImportExport=%d\n",retval->NumImportExport);
379   fprintf(stderr,"NumDataTransfer=%d\n",retval->NumDataTransfer);
380   fprintf(stderr,"MaxReadElementStatusData=%d\n",retval->MaxReadElementStatusData);
381   fprintf(stderr,"NumElements=%d\n",retval->NumElements);
382   fflush(stderr);
383 #endif
384   
385   return retval;
386 }
387
388 static void FreeElementData(ElementStatus_T *data)
389 {
390   free(data->DataTransferElementAddress);
391   free(data->DataTransferElementSourceStorageElementNumber);
392   free(data->DataTransferPrimaryVolumeTag);
393   free(data->DataTransferAlternateVolumeTag);
394   free(data->PrimaryVolumeTag);
395   free(data->AlternateVolumeTag);
396   free(data->StorageElementAddress);
397   free(data->StorageElementIsImportExport);
398   free(data->StorageElementFull);
399   free(data->DataTransferElementFull);
400 }
401   
402        
403
404 /* allocate data  */
405
406 static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense) {
407   ElementStatus_T *retval;
408
409   retval=(ElementStatus_T *) xzmalloc(sizeof(ElementStatus_T));
410
411   /* now for the invidual component arrays.... */
412
413   retval->DataTransferElementAddress = (int *) xzmalloc(sizeof(int)*
414                                     (mode_sense->NumDataTransfer + 1));
415   retval->DataTransferElementSourceStorageElementNumber = 
416     (int *) xzmalloc(sizeof(int)*(mode_sense->NumDataTransfer+1));
417   retval->DataTransferPrimaryVolumeTag=
418     (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
419   retval->DataTransferAlternateVolumeTag=
420     (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
421   retval->PrimaryVolumeTag=
422     (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
423   retval->AlternateVolumeTag=
424     (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
425   retval->StorageElementAddress=
426     (int *)xzmalloc(sizeof(int)*(mode_sense->NumStorage+1));
427   retval->StorageElementIsImportExport=
428     (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));    
429   retval->StorageElementFull=
430     (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));    
431   retval->DataTransferElementFull=
432     (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumDataTransfer+1));
433   
434   return retval; /* sigh! */
435 }
436
437
438 void copy_barcode(unsigned char *src, unsigned char *dest) {
439   int i;
440   
441   for (i=0; i < 36; i++) {
442     *dest++ = *src++;
443   }
444   *dest=0; /* null-terminate, sigh. */ 
445 }
446
447
448 /* This #%!@# routine has more parameters than I can count! */
449 unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
450                                         RequestSense_T *RequestSense,
451                                         Inquiry_T *inquiry_info, 
452                                         SCSI_Flags_T *flags,
453                                         int ElementStart,
454                                         int NumElements,
455                                         int NumBytes
456                                         ) {
457
458   CDB_T CDB;
459   boolean is_attached = false;
460
461   unsigned char *DataBuffer; /* size of data... */
462
463 #ifdef HAVE_GET_ID_LUN
464   scsi_id_t *scsi_id;
465 #endif
466   if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
467     is_attached=true;
468   }
469   if (flags->no_attached) { /* override, sigh */ 
470     is_attached=false;
471   }
472
473   DataBuffer=(unsigned char *) xmalloc(NumBytes+1);
474
475   slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
476 #ifdef HAVE_GET_ID_LUN
477   scsi_id = SCSI_GetIDLun(MediumChangerFD);
478 #endif
479
480
481   CDB[0] = 0xB8;                /* READ ELEMENT STATUS */
482   if (is_attached) {
483     CDB[0] = 0xB4;  /* whoops, READ_ELEMENT_STATUS_ATTACHED! */ 
484   }
485 #ifdef HAVE_GET_ID_LUN
486   CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype;  /* Lun + VolTag + Type code */
487   free(scsi_id);
488 #else
489   CDB[1] = ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype;              /* Element Type Code = 0, VolTag = 1 */
490 #endif
491   CDB[2] = (ElementStart >> 8) & 0xff;  /* Starting Element Address MSB */
492   CDB[3] = ElementStart & 0xff;         /* Starting Element Address LSB */
493
494
495   CDB[4]= (NumElements >> 8) & 0xff;      /* Number Of Elements MSB */
496   CDB[5]= NumElements & 0xff ;        /* Number of elements LSB */
497
498   /*  CDB[5]=127; */ /* test */
499 #ifdef DEBUG
500   fprintf(stderr,"CDB[5]=%d\n" , CDB[5]);
501 #endif
502   CDB[6] = 0;                   /* Reserved */
503
504   CDB[7]= (NumBytes >> 16) & 0xff; /* allocation length MSB */
505   CDB[8]= (NumBytes >> 8) & 0xff;
506   CDB[9]= NumBytes & 0xff;   /* allocation length LSB */
507
508   CDB[10] = 0;                  /* Reserved */
509   CDB[11] = 0;                  /* Control */
510
511 #ifdef DEBUG_BARCODE
512   {
513     int i;
514     fprintf(stderr,"CDB= ");
515     for (i=0;i<12;i++) {
516       fprintf(stderr,"%x ",CDB[i]);
517     }
518     fprintf(stderr,"\n");
519     fflush(stderr);
520   }
521 #endif
522
523   if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
524                           DataBuffer,NumBytes, RequestSense) != 0){
525     /* okay, first see if we have sense key of 'illegal request',
526        additional sense code of '24', additional sense qualfier of 
527        '0', and field in error of '4'. This means that we issued a request
528        w/bar code reader and did not have one, thus must re-issue the request
529        w/out barcode :-(.
530     */
531     
532     /* Okay, most autochangers and tape libraries set a sense key here if
533      * they do not have a bar code reader. For some reason, the ADIC DAT
534      * uses a different sense key? Let's retry w/o bar codes for *ALL*
535      * sense keys, sigh sigh sigh. 
536      */
537     
538     if ( RequestSense->SenseKey > 1 ) {
539       /* we issued a code requesting bar codes, there is no bar code reader? */
540       /* clear out our sense buffer first... */
541       slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
542
543       CDB[1] &= ~0x10; /* clear bar code flag! */
544
545 #ifdef DEBUG_BARCODE
546       {
547         int i;
548         fprintf(stderr,"CDB= ");
549         for (i=0;i<12;i++) {
550           fprintf(stderr,"%x ",CDB[i]);
551         }
552         fprintf(stderr,"\n");
553         fflush(stderr);
554       }
555 #endif
556       
557       if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
558                               DataBuffer, NumBytes, RequestSense) != 0){
559         free(DataBuffer);
560         return NULL;
561       }
562     } else {
563       free(DataBuffer);
564       return NULL;
565     }
566   }
567
568 #ifdef DEBUG_BARCODE
569   /* print a bunch of extra debug data :-(.  */
570   PrintRequestSense(RequestSense); /* see what it sez :-(. */
571   {
572     int i;
573     fprintf(stderr,"Data:");
574     for (i=0;i<40;i++) {
575       fprint(stderr,"%02x ",DataBuffer[i]);
576     }
577     fprintf(stderr,"\n");
578   }
579 #endif  
580   return DataBuffer; /* we succeeded! */
581 }
582
583 /******************* ParseElementStatus ***********************************/
584 /* This does the actual grunt work of parsing element status data. It fills
585  * in appropriate pieces of its input data. It may be called multiple times
586  * while we are gathering element status.
587  */
588
589 static void ParseElementStatus(int *EmptyStorageElementAddress,
590                                int *EmptyStorageElementCount,
591                                unsigned char *DataBuffer,
592                                ElementStatus_T *ElementStatus,
593                                ElementModeSense_T *mode_sense
594
595                                )   {
596     
597     unsigned char *DataPointer=DataBuffer;
598     TransportElementDescriptor_T TEBuf;
599     TransportElementDescriptor_T *TransportElementDescriptor;
600     ElementStatusDataHeader_T *ElementStatusDataHeader;
601     Element2StatusPage_T *ElementStatusPage;
602     Element2StatusPage_T ESBuf;
603     int ElementCount;
604     int TransportElementDescriptorLength;
605     int BytesAvailable; 
606     
607     ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataBuffer;
608
609     
610
611   ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
612   DataPointer += sizeof(ElementStatusDataHeader_T);
613   ElementCount =
614     BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
615 #ifdef DEBUG
616       fprintf(stderr,"ElementCount=%d\n",ElementCount);
617       fflush(stderr);
618 #endif
619   while (ElementCount > 0)
620     {
621 #ifdef DEBUG
622       int got_element_num=0;
623       
624       fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
625       got_element_num++;
626 #endif
627
628       memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
629       ElementStatusPage = &ESBuf;
630       DataPointer += sizeof(ElementStatusPage_T);
631       TransportElementDescriptorLength =
632         BigEndian16(ElementStatusPage->ElementDescriptorLength);
633       if (TransportElementDescriptorLength <
634           sizeof(TransportElementDescriptorShort_T)) {
635
636         /* Foo, Storage Element Descriptors can be 4 bytes?! */
637         if ( (ElementStatusPage->ElementTypeCode != MediumTransportElement &&
638               ElementStatusPage->ElementTypeCode != StorageElement &&
639               ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
640              TransportElementDescriptorLength < 4) {
641 #ifdef DEBUG
642           fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
643 #endif
644           FatalError("Transport Element Descriptor Length too short: %d\n",TransportElementDescriptorLength);
645         } 
646         
647       }
648 #ifdef DEBUG
649       fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
650 #endif
651       BytesAvailable =
652         BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
653       if (BytesAvailable <= 0) {
654               ElementCount--;
655       }
656       while (BytesAvailable > 0)
657         {
658           /* TransportElementDescriptor =
659              (TransportElementDescriptor_T *) DataPointer; */
660           memcpy(&TEBuf, DataPointer, 
661                         (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
662                                 TransportElementDescriptorLength  :
663                                 sizeof(TEBuf));
664           TransportElementDescriptor = &TEBuf;
665
666           DataPointer += TransportElementDescriptorLength;
667           BytesAvailable -= TransportElementDescriptorLength;
668           ElementCount--;
669           switch (ElementStatusPage->ElementTypeCode)
670             {
671             case MediumTransportElement:
672               ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
673 #ifdef DEBUG
674               fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress); 
675 #endif
676               break;
677               /* we treat ImportExport elements as if they were normal
678                * storage elements now, sigh...
679                */
680             case ImportExportElement:
681               ElementStatus->ImportExportCount++;
682 #ifdef DEBUG
683               fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
684 #endif
685               ElementStatus->StorageElementIsImportExport[ElementStatus->StorageElementCount] = 1;
686               /* WARNING: DO *NOT* PUT A 'break' STATEMENT HERE, it is supposed
687                * to run on into the case for a Storage Element! 
688                */
689             case StorageElement:
690 #ifdef DEBUG
691               fprintf(stderr,"StorageElementCount=%d  ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
692 #endif
693               ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
694                 BigEndian16(TransportElementDescriptor->ElementAddress);
695               ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
696                 TransportElementDescriptor->Full;
697 #ifdef DEBUG
698               fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
699 #endif
700               if (!TransportElementDescriptor->Full)
701                 {
702                   EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
703                     ElementStatus->StorageElementCount; /* slot idx. */
704                     /*   ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
705                 }
706               if ( (TransportElementDescriptorLength >  11) && 
707                    (ElementStatusPage->VolBits & E2_AVOLTAG)) {
708                 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
709                              ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
710               } else {
711                 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0;  /* null string. */;
712               } 
713               if ( (TransportElementDescriptorLength > 11) && 
714                    (ElementStatusPage->VolBits & E2_PVOLTAG)) {
715                 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
716                              ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
717               } else {
718                 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
719               }
720               
721               ElementStatus->StorageElementCount++;
722               /* Note that the original mtx had no check here for 
723                  buffer overflow, though some drives might mistakingly
724                  do one... 
725               */ 
726               if (ElementStatus->StorageElementCount > mode_sense->NumStorage) {
727                 fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
728                         mode_sense->NumStorage,
729                         ElementStatus->StorageElementCount);
730                 fflush(stderr);
731                 return; /* we're done :-(. */
732               }
733
734
735               break;
736
737
738             case DataTransferElement:
739               /* tape drive not installed, go back to top of loop */
740
741               /* if (TransportElementDescriptor->Except) continue ; */
742
743               /* Note: This is for Exabyte tape libraries that improperly
744                  report that they have a 2nd tape drive when they don't. We
745                  could generalize this in an ideal world, but my attempt to
746                  do so failed with dual-drive Exabyte tape libraries that
747                  *DID* have the second drive. Sigh. 
748               */
749               /* No longer need this test due to code restructuring? */
750               /* if (TransportElementDescriptor->AdditionalSenseCode==0x83 && 
751                   TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04) 
752                 continue;
753               */ 
754               /* generalize it. Does it work? Let's try it! */
755               /* No, dammit, following does not work on dual-drive Exabyte
756                  'cause if a tape is in the drive, it sets the AdditionalSense
757                   code to something (sigh).
758               */
759               /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
760                 continue;
761               */ 
762               
763
764                   
765               ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
766                 BigEndian16(TransportElementDescriptor->ElementAddress);
767               ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] = 
768                 TransportElementDescriptor->Full;
769               ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
770                 BigEndian16(TransportElementDescriptor
771                             ->SourceStorageElementAddress);
772
773               if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
774                 FatalError("Too many Data Transfer Elements Reported\n");
775               }
776               if (ElementStatusPage->VolBits & E2_PVOLTAG) {
777                 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
778                              ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
779               } else {
780                 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
781               }
782               if (ElementStatusPage->VolBits & E2_AVOLTAG) {
783                 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
784                              ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
785               } else {
786                 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
787               }
788               ElementStatus->DataTransferElementCount++;
789               /* 0 actually is a usable element address */
790               /* if (DataTransferElementAddress == 0) */
791               /*        FatalError( */
792               /*  "illegal Data Transfer Element Address %d reported\n", */
793               /* DataTransferElementAddress); */
794               break;
795             default:
796               FatalError("illegal Element Type Code %d reported\n",
797                          ElementStatusPage->ElementTypeCode);
798             }
799         }
800     }
801 }
802
803 /********************* Real ReadElementStatus ********************* */
804
805 /*
806  * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
807  * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
808  * under Linux, which gets around the @#%@ 4k buffer size in Linux. 
809  * We still have the restriction that Linux cuts off the last two
810  * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
811  * verbose widget won't work :-(. 
812  
813  * We now look for that "attached" bit in the inquiry_info to see whether
814  * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
815  * look at the device type in the inquiry_info to see whether it is a media
816  * changer or tape device, and if it's a media changer device, we ignore the
817  * attached bit (one beta tester found an old 4-tape DAT changer that set
818  * the attached bit for both the tape device AND the media changer device). 
819
820 */
821
822 ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
823   ElementStatus_T *ElementStatus;
824   
825   unsigned char *DataBuffer; /* size of data... */
826   
827   int EmptyStorageElementCount=0;
828   int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
829   
830   int empty_idx=0;
831   int invalid_sources=0;
832   boolean is_attached = false;
833   int i,j;
834   
835   ElementModeSense_T *mode_sense = NULL;
836   
837   if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
838     is_attached=true;
839   }
840   if (flags->no_attached) { /* override, sigh */ 
841     is_attached=false;
842   }
843   if (!is_attached) {
844     mode_sense=ReadAssignmentPage(MediumChangerFD);
845   }
846   if (!mode_sense) {
847     mode_sense=(ElementModeSense_T *) xmalloc(sizeof(ElementModeSense_T));
848     mode_sense->NumMediumTransport=MAX_TRANSPORT_ELEMENTS;
849     mode_sense->NumStorage=MAX_STORAGE_ELEMENTS;
850     mode_sense->NumDataTransfer=MAX_TRANSFER_ELEMENTS;
851     mode_sense->MaxReadElementStatusData=  (sizeof(ElementStatusDataHeader_T)
852                                             + 3 * sizeof(ElementStatusPage_T)
853                                             + (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS)
854                                             * sizeof(TransportElementDescriptor_T));
855     
856     /* don't care about the others anyhow at the moment... */
857   }
858   
859   ElementStatus=AllocateElementData(mode_sense);
860
861   /* Now to initialize it (sigh).  */
862   ElementStatus->StorageElementCount = 0;
863   ElementStatus->DataTransferElementCount=0;
864     
865   
866   /* first, allocate some empty storage stuff: Note that we pass this
867    * down to ParseElementStatus (sigh!) 
868    */
869   
870   EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
871   for (i=0;i<mode_sense->NumStorage;i++) {
872     EmptyStorageElementAddress[i]=-1;
873   }
874   
875   /* Okay, now to send some requests for the various types of stuff: */
876
877   /* -----------STORAGE ELEMENTS---------------- */
878   /* Let's start with storage elements: */
879
880   for (i=0;i<mode_sense->NumDataTransfer;i++) {
881     /* initialize them to an illegal # so that we can fix later... */
882     ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1; 
883   }
884   
885
886   flags->elementtype=StorageElement; /* sigh! */
887   DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
888                                       inquiry_info,flags,
889                                       mode_sense->StorageStart,
890                                       /* adjust for import/export. */
891                                       mode_sense->NumStorage-mode_sense->NumImportExport,
892                                       mode_sense->MaxReadElementStatusData);
893   if (!DataBuffer) {
894     /* darn. Free up stuff and return. */
895     /****FIXME**** do a free on element data! */
896     FreeElementData(ElementStatus);
897     return NULL; 
898   } 
899
900   ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
901                      DataBuffer,ElementStatus,mode_sense);
902
903   free(DataBuffer); /* sigh! */
904
905   /* --------------IMPORT/EXPORT--------------- */
906   /* Next let's see if we need to do Import/Export: */
907   if (mode_sense->NumImportExport > 0) {
908     flags->elementtype=ImportExportElement;
909     DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
910                                         inquiry_info,flags,
911                                         mode_sense->ImportExportStart,
912                                         mode_sense->NumImportExport,
913                                         mode_sense->MaxReadElementStatusData);
914     
915     if (!DataBuffer) {
916       /* darn. Free up stuff and return. */
917       /****FIXME**** do a free on element data! */
918       FreeElementData(ElementStatus);
919       return NULL; 
920     } 
921     ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
922                      DataBuffer,ElementStatus,mode_sense);
923
924   }
925   
926
927
928   /* ----------------- DRIVES ---------------------- */
929
930
931   flags->elementtype=DataTransferElement; /* sigh! */
932   DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
933                                       inquiry_info,flags,
934                                       mode_sense->DataTransferStart,
935                                       mode_sense->NumDataTransfer,
936                                       mode_sense->MaxReadElementStatusData);
937   if (!DataBuffer) {
938     /* darn. Free up stuff and return. */
939     /****FIXME**** do a free on element data! */
940     FreeElementData(ElementStatus);
941     return NULL; 
942   } 
943
944   ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
945                      DataBuffer,ElementStatus,mode_sense);
946
947   free(DataBuffer); /* sigh! */
948
949
950   /* ----------------- Robot Arm(s) -------------------------- */
951
952     /* grr, damned brain dead HP doesn't report that it has any! */
953   if (!mode_sense->NumMediumTransport) { 
954     ElementStatus->TransportElementAddress=0; /* default it sensibly :-(. */
955   } else {
956      flags->elementtype=MediumTransportElement; /* sigh! */
957      DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
958                                       inquiry_info,flags,
959                                       mode_sense->MediumTransportStart,
960                                       1, /* only get 1, sigh. */
961                                       mode_sense->MaxReadElementStatusData);
962      if (!DataBuffer) {
963        /* darn. Free up stuff and return. */
964        /****FIXME**** do a free on element data! */
965        FreeElementData(ElementStatus);
966        return NULL; 
967      } 
968    
969      ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
970                      DataBuffer,ElementStatus,mode_sense);
971
972      free(DataBuffer); /* sigh! */
973   }
974
975   /*---------------------- Sanity Checking ------------------- */
976
977   if (!ElementStatus->DataTransferElementCount)
978     FatalError("no Data Transfer Element reported\n");
979   if (ElementStatus->StorageElementCount == 0)
980     FatalError("no Storage Elements reported\n");
981
982
983   /* ---------------------- Reset SourceStorageElementNumbers ------- */
984
985   /* okay, re-write the SourceStorageElementNumber code  *AGAIN*.
986    * Pass1:
987    *   Translate from raw element # to our translated # (if possible).
988    * First, check the SourceStorageElementNumbers against the list of 
989    * filled slots. If the slots indicated are empty, we accept that list as
990    * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
991    *
992    * Pass2:
993    *
994    * If we had some invalid (or unknown) SourceStorageElementNumbers
995    * then we must search for free slots, and assign SourceStorageElementNumbers
996    * to those free slots. We happen to already built a list of free
997    * slots as part of the process of reading the storage element numbers
998    * from the tape. So that's easy enough to do! 
999    */
1000
1001 #ifdef DEBUG_TAPELIST
1002   fprintf(stderr,"empty slots: %d\n",EmptyStorageElementCount);
1003   if (EmptyStorageElementCount) {
1004      for (i=0; i<EmptyStorageElementCount; i++) {
1005          fprintf(stderr,"empty: %d\n",EmptyStorageElementAddress[i]);
1006      }
1007   }
1008 #endif
1009
1010   /* Okay, now we re-assign origin slots if the "real" origin slot
1011    * is obviously defective: 
1012    */
1013   /* pass one: */
1014   invalid_sources=0; /* no invalid sources yet! */
1015   for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1016     int elnum;
1017     int translated_elnum = -1;
1018     /* if we have an element, then ... */
1019     if (ElementStatus->DataTransferElementFull[i]) {
1020       elnum=ElementStatus->DataTransferElementSourceStorageElementNumber[i];
1021       /* if we have an element number, then ... */
1022       if (elnum >= 0) {
1023          /* Now to translate the elnum: */
1024          for (j=0; j<ElementStatus->StorageElementCount; j++) {
1025              if (elnum == ElementStatus->StorageElementAddress[j]) {
1026                  translated_elnum=j; 
1027              }
1028          }
1029       /* now see if the element # is already occupied: */
1030         if (ElementStatus->StorageElementFull[translated_elnum]) {
1031           invalid_sources=1;
1032           break; /* break out of the loop! */
1033         } else {
1034            /* properly set the source... */
1035         ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1036                 translated_elnum;
1037         }
1038                  
1039       } else {
1040         /* if element # was not >=0, then we had an invalid source anyhow! */
1041         invalid_sources=1;
1042       }
1043     }
1044   }
1045
1046 #ifdef DEBUG_TAPELIST
1047    fprintf(stderr,"invalid_sources=%d\n",invalid_sources);
1048 #endif
1049
1050   /* Pass2: */
1051   /* okay, we have invalid sources, so let's see what they should be: */
1052   /* Note: If EmptyStorageElementCount is < # of drives, the leftover
1053    * drives will be assigned a -1 (see the initialization loop for
1054    * EmptyStorageElementAddress above), which will be reported as "slot 0"
1055    * by the user interface. This is an invalid value, but more useful for us
1056    * to have than just crapping out here :-(. 
1057    */ 
1058   if (invalid_sources) {
1059     empty_idx=0;
1060     for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1061       if (ElementStatus->DataTransferElementFull[i]) {
1062 #ifdef DEBUG_TAPELIST
1063         fprintf(stderr,"for drive %d, changing source %d to %d (empty slot #%d)\n",
1064              i,
1065              ElementStatus->DataTransferElementSourceStorageElementNumber[i],
1066              EmptyStorageElementAddress[empty_idx],
1067              empty_idx);
1068 #endif
1069               
1070         ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1071           EmptyStorageElementAddress[empty_idx++];
1072       }
1073     }
1074   }
1075
1076   /* and done! */      
1077   free(mode_sense);
1078   free(EmptyStorageElementAddress);
1079   return ElementStatus;
1080   
1081 }
1082
1083 /*************************************************************************/
1084
1085
1086
1087 /* Now the actual media movement routine! */
1088 RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
1089                        int DestinationAddress, 
1090                        ElementStatus_T *ElementStatus,
1091                        Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1092 {
1093   RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1094   CDB_T CDB;
1095   if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {  /* if using the ATTACHED API */
1096     CDB[0]=0xA7;   /* MOVE_MEDIUM_ATTACHED */
1097   } else {
1098     CDB[0] = 0xA5;              /* MOVE MEDIUM */
1099   }
1100   CDB[1] = 0;                   /* Reserved */
1101   CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF;  /* Transport Element Address MSB */
1102   CDB[3] = (ElementStatus->TransportElementAddress) & 0xff;   /* Transport Element Address LSB */
1103   CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
1104   CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
1105   CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
1106   CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
1107   CDB[8] = 0;                   /* Reserved */
1108   CDB[9] = 0;                   /* Reserved */
1109   if (flags->invert) {
1110     CDB[10] = 1;                        /* Reserved */
1111   } else {
1112     CDB[10] = 0;
1113   }
1114   /* eepos controls the tray for import/export elements, sometimes. */
1115   CDB[11] = 0 | (flags->eepos <<6);                     /* Control */
1116   
1117   if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1118                           NULL, 0, RequestSense) != 0) {
1119     return RequestSense;
1120   }
1121   free(RequestSense);
1122   return NULL; /* success! */
1123 }
1124
1125 /* for Linux, this creates a way to do a short erase... the @#$%@ st.c
1126  * driver defaults to doing a long erase!
1127  */
1128
1129 RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD) {
1130   RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1131   CDB_T CDB;
1132
1133   CDB[0]=0x19;
1134   CDB[1]=0;  /* Short! */
1135   CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
1136
1137   if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
1138                           NULL, 0, RequestSense) != 0) {
1139     return RequestSense;
1140   }
1141   free(RequestSense);
1142   return NULL;  /* Success! */
1143 }  
1144
1145
1146 #ifdef LONG_PRINT_REQUEST_SENSE
1147
1148 static char *sense_keys[] = {
1149   "No Sense",  /* 00 */
1150   "Recovered Error", /* 01 */
1151   "Not Ready",  /* 02 */
1152   "Medium Error",  /* 03 */
1153   "Hardware Error",  /* 04 */
1154   "Illegal Request",  /* 05 */
1155   "Unit Attention", /* 06 */
1156   "Data Protect",   /* 07 */
1157   "Blank Check", /* 08 */
1158   "0x09",   /* 09 */
1159   "0x0a",   /* 0a */
1160   "Aborted Command", /* 0b */
1161   "0x0c",  /* 0c */
1162   "Volume Overflow", /* 0d */
1163   "Miscompare",   /* 0e */
1164   "0x0f"  /* 0f */
1165 };
1166
1167 static char Yes[]="yes";
1168 static char No[]="no";
1169
1170 void PrintRequestSense(RequestSense_T *RequestSense)
1171 {
1172   char *msg;
1173
1174   fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
1175   fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
1176   if (RequestSense->ErrorCode==0x70) { 
1177     msg = "Current" ;
1178   } else if (RequestSense->ErrorCode==0x71) { 
1179     msg = "Deferred" ;
1180   } else {
1181     msg = "Unknown?!" ;
1182   }
1183   fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n",RequestSense->ErrorCode,msg);
1184   fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n",sense_keys[RequestSense->SenseKey]);
1185   fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
1186   fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
1187   fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
1188   if (RequestSense->Valid) {
1189     fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
1190   } 
1191   fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
1192   fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
1193   if (RequestSense->SKSV) {
1194     fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n",RequestSense->BitPointer);
1195   }
1196   fprintf(stderr,"mtx: Request Sense: BPV=%s\n",RequestSense->BPV ? Yes : No);
1197   fprintf(stderr,"mtx: Request Sense: Error in CDB=%s\n",RequestSense->CommandData ? Yes : No);
1198   fprintf(stderr,"mtx: Request Sense: SKSV=%s\n",RequestSense->SKSV ? Yes : No);
1199   fflush(stderr);
1200   if (RequestSense->BPV || RequestSense -> SKSV) {
1201     fprintf(stderr,"mtx: Request Sense: Field Pointer = %02X %02X\n",RequestSense->FieldData[0],RequestSense->FieldData[1]);
1202   }
1203 }
1204
1205
1206 #else
1207 void PrintRequestSense(RequestSense_T *RequestSense)
1208 {
1209   int i;
1210   fprintf(stderr, "mtx: Request Sense: %02X",
1211           ((unsigned char *) RequestSense)[0]);
1212   for (i = 1; i < sizeof(RequestSense_T); i++)
1213     fprintf(stderr, " %02X", ((unsigned char *) RequestSense)[i]);
1214   fprintf(stderr, "\n");
1215 }
1216
1217 #endif
1218
1219 /* $Date: 2002/02/05 16:51:11 $
1220  * $Log: mtxl.c,v $
1221  * Revision 1.8.2.3  2002/02/05 16:51:11  elgreen
1222  * mtx 1.2.16pre3
1223  *
1224  * Revision 1.8.2.2  2002/01/22 16:27:47  elgreen
1225  * Handle too-big transport element descriptor lengths
1226  *
1227  * Revision 1.8.2.1  2002/01/17 22:24:35  elgreen
1228  * Handle ADIC silliness of saying that it has 1 import/export element whose
1229  * descriptor is 0 bytes in length
1230  *
1231  * Revision 1.8  2001/06/25 23:06:22  elgreen
1232  * Readying this for 1.2.13 release
1233  *
1234  * Revision 1.7  2001/06/25 04:56:35  elgreen
1235  * Kai to the rescue *again* :-)
1236  *
1237  * Revision 1.6  2001/06/24 07:02:01  elgreen
1238  * Kai's fix was better than mine.
1239  *
1240  * Revision 1.5  2001/06/24 06:59:19  elgreen
1241  * Kai found bug in the barcode backoff.
1242  *
1243  * Revision 1.4  2001/06/15 18:56:54  elgreen
1244  * Arg, it doesn't help to check for 0 robot arms if you force it to 1!
1245  *
1246  * Revision 1.3  2001/06/15 14:26:09  elgreen
1247  * Fixed brain-dead case of HP loaders that report they have no robot arms.
1248  *
1249  * Revision 1.2  2001/06/09 17:26:26  elgreen
1250  * Added 'nobarcode' command to mtx (to skip the initial request asking for
1251  * barcodes for mtx status purposes).
1252  *
1253  * Revision 1.1.1.1  2001/06/05 17:10:25  elgreen
1254  * Initial import into SourceForge
1255  *
1256  * Revision 1.29  2001/05/01 01:39:23  eric
1257  * Remove the Exabyte special case code, which seemed to be barfing me :-(.
1258  *
1259  * Revision 1.28  2001/04/28 00:03:10  eric
1260  * update for 1.2.12.
1261  *
1262  * Revision 1.27  2001/04/18 19:27:39  eric
1263  * whoops, move ImportExport parser into the $#%!@ ImportExport test :-(.
1264  *
1265  * Revision 1.26  2001/04/18 18:55:44  eric
1266  * turn too many storage elements into a warning rather than a fatal error (sigh!)
1267  *
1268  * Revision 1.25  2001/04/18 18:46:54  eric
1269  * $%!@# "C" precedence rules.
1270  *
1271  * Revision 1.24  2001/04/18 17:58:04  eric
1272  * initialize EmptyStorageElementCount (sigh)
1273  *
1274  * Revision 1.23  2001/04/18 16:55:03  eric
1275  * hopefully get the initialization for element status straigtened out.
1276  *
1277  * Revision 1.22  2001/04/18 16:32:59  eric
1278  * Cleaned up all -Wall messages.
1279  *
1280  * Revision 1.21  2001/04/18 16:08:08  eric
1281  * after re-arranging & fixing bugs
1282  *
1283  * Revision 1.20  2001/04/17 21:28:43  eric
1284  * mtx 1.2.11
1285  *
1286  * Revision 1.19  2001/04/02 20:12:48  eric
1287  * checkin, mtx 1.2.11pre6
1288  *
1289  * Revision 1.18  2001/03/06 01:37:05  eric
1290  * Solaris changes
1291  *
1292  * Revision 1.17  2001/02/26 20:56:12  eric
1293  * added debugging, removed Makefile
1294  *
1295  * Revision 1.16  2001/02/19 23:22:28  eric
1296  * Add E2 bits
1297  *
1298  * Revision 1.15  2001/02/19 23:02:04  eric
1299  * Apply SPARC patch.
1300  *
1301  * Revision 1.14  2001/01/26 15:58:10  eric
1302  * Make work on solaris?
1303  *
1304  * Revision 1.13  2001/01/17 19:39:01  eric
1305  * Changed mtx.h and mtxl.c to use autoconf stuff to figure out which SCSI
1306  * routines to use (this way, if we ever port to an OS that has an identical
1307  * SCSI interface to one of the supported OS's, we don't have to do anything
1308  * special!).
1309  *
1310  * Revision 1.12  2001/01/17 16:36:26  eric
1311  * added date & revision strings as needed
1312  *
1313  * Revision 1.11  2000/11/30 20:35:18  eric
1314  * Added Erase command.
1315  *
1316  */