Imported Upstream version 1.2.17rel
[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/09/27 17:23:28 $
6 $Revision: 1.8.2.7 $
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 #ifdef __WEIRD_CHAR_SUPPRESS
444     if ((*dest < 32) || (*dest > 127))
445        *dest = '\0';
446 #endif
447      dest++;
448   }
449   dest=0; /* null-terminate, sigh. */ 
450 }
451
452
453 /* This #%!@# routine has more parameters than I can count! */
454 unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
455                                         RequestSense_T *RequestSense,
456                                         Inquiry_T *inquiry_info, 
457                                         SCSI_Flags_T *flags,
458                                         int ElementStart,
459                                         int NumElements,
460                                         int NumBytes
461                                         ) {
462
463   CDB_T CDB;
464   boolean is_attached = false;
465
466   unsigned char *DataBuffer; /* size of data... */
467
468 #ifdef HAVE_GET_ID_LUN
469   scsi_id_t *scsi_id;
470 #endif
471   if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
472     is_attached=true;
473   }
474   if (flags->no_attached) { /* override, sigh */ 
475     is_attached=false;
476   }
477
478   DataBuffer=(unsigned char *) xmalloc(NumBytes+1);
479
480   slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
481 #ifdef HAVE_GET_ID_LUN
482   scsi_id = SCSI_GetIDLun(MediumChangerFD);
483 #endif
484
485
486   CDB[0] = 0xB8;                /* READ ELEMENT STATUS */
487   if (is_attached) {
488     CDB[0] = 0xB4;  /* whoops, READ_ELEMENT_STATUS_ATTACHED! */ 
489   }
490 #ifdef HAVE_GET_ID_LUN
491   CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype;  /* Lun + VolTag + Type code */
492   free(scsi_id);
493 #else
494   CDB[1] = ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype;              /* Element Type Code = 0, VolTag = 1 */
495 #endif
496   CDB[2] = (ElementStart >> 8) & 0xff;  /* Starting Element Address MSB */
497   CDB[3] = ElementStart & 0xff;         /* Starting Element Address LSB */
498
499
500   CDB[4]= (NumElements >> 8) & 0xff;      /* Number Of Elements MSB */
501   CDB[5]= NumElements & 0xff ;        /* Number of elements LSB */
502
503   /*  CDB[5]=127; */ /* test */
504 #ifdef DEBUG
505   fprintf(stderr,"CDB[5]=%d\n" , CDB[5]);
506 #endif
507   CDB[6] = 0;                   /* Reserved */
508
509   CDB[7]= (NumBytes >> 16) & 0xff; /* allocation length MSB */
510   CDB[8]= (NumBytes >> 8) & 0xff;
511   CDB[9]= NumBytes & 0xff;   /* allocation length LSB */
512
513   CDB[10] = 0;                  /* Reserved */
514   CDB[11] = 0;                  /* Control */
515
516 #ifdef DEBUG_BARCODE
517   {
518     int i;
519     fprintf(stderr,"CDB= ");
520     for (i=0;i<12;i++) {
521       fprintf(stderr,"%x ",CDB[i]);
522     }
523     fprintf(stderr,"\n");
524     fflush(stderr);
525   }
526 #endif
527
528   if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
529                           DataBuffer,NumBytes, RequestSense) != 0){
530     /* okay, first see if we have sense key of 'illegal request',
531        additional sense code of '24', additional sense qualfier of 
532        '0', and field in error of '4'. This means that we issued a request
533        w/bar code reader and did not have one, thus must re-issue the request
534        w/out barcode :-(.
535     */
536     
537     /* Okay, most autochangers and tape libraries set a sense key here if
538      * they do not have a bar code reader. For some reason, the ADIC DAT
539      * uses a different sense key? Let's retry w/o bar codes for *ALL*
540      * sense keys, sigh sigh sigh. 
541      */
542     
543     if ( RequestSense->SenseKey > 1 ) {
544       /* we issued a code requesting bar codes, there is no bar code reader? */
545       /* clear out our sense buffer first... */
546       slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
547
548       CDB[1] &= ~0x10; /* clear bar code flag! */
549
550 #ifdef DEBUG_BARCODE
551       {
552         int i;
553         fprintf(stderr,"CDB= ");
554         for (i=0;i<12;i++) {
555           fprintf(stderr,"%x ",CDB[i]);
556         }
557         fprintf(stderr,"\n");
558         fflush(stderr);
559       }
560 #endif
561       
562       if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
563                               DataBuffer, NumBytes, RequestSense) != 0){
564         free(DataBuffer);
565         return NULL;
566       }
567     } else {
568       free(DataBuffer);
569       return NULL;
570     }
571   }
572
573 #ifdef DEBUG_BARCODE
574   /* print a bunch of extra debug data :-(.  */
575   PrintRequestSense(RequestSense); /* see what it sez :-(. */
576   {
577     int i;
578     fprintf(stderr,"Data:");
579     for (i=0;i<40;i++) {
580       fprint(stderr,"%02x ",DataBuffer[i]);
581     }
582     fprintf(stderr,"\n");
583   }
584 #endif  
585   return DataBuffer; /* we succeeded! */
586 }
587
588 /******************* ParseElementStatus ***********************************/
589 /* This does the actual grunt work of parsing element status data. It fills
590  * in appropriate pieces of its input data. It may be called multiple times
591  * while we are gathering element status.
592  */
593
594 static void ParseElementStatus(int *EmptyStorageElementAddress,
595                                int *EmptyStorageElementCount,
596                                unsigned char *DataBuffer,
597                                ElementStatus_T *ElementStatus,
598                                ElementModeSense_T *mode_sense
599
600                                )   {
601     
602     unsigned char *DataPointer=DataBuffer;
603     TransportElementDescriptor_T TEBuf;
604     TransportElementDescriptor_T *TransportElementDescriptor;
605     ElementStatusDataHeader_T *ElementStatusDataHeader;
606     Element2StatusPage_T *ElementStatusPage;
607     Element2StatusPage_T ESBuf;
608     int ElementCount;
609     int TransportElementDescriptorLength;
610     int BytesAvailable; 
611     
612     ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataBuffer;
613
614     
615
616   ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
617   DataPointer += sizeof(ElementStatusDataHeader_T);
618   ElementCount =
619     BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
620 #ifdef DEBUG
621       fprintf(stderr,"ElementCount=%d\n",ElementCount);
622       fflush(stderr);
623 #endif
624   while (ElementCount > 0)
625     {
626 #ifdef DEBUG
627       int got_element_num=0;
628       
629       fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
630       got_element_num++;
631 #endif
632
633       memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
634       ElementStatusPage = &ESBuf;
635       DataPointer += sizeof(ElementStatusPage_T);
636       TransportElementDescriptorLength =
637         BigEndian16(ElementStatusPage->ElementDescriptorLength);
638       if (TransportElementDescriptorLength <
639           sizeof(TransportElementDescriptorShort_T)) {
640
641         /* Foo, Storage Element Descriptors can be 4 bytes?! */
642         if ( (ElementStatusPage->ElementTypeCode != MediumTransportElement &&
643               ElementStatusPage->ElementTypeCode != StorageElement &&
644               ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
645              TransportElementDescriptorLength < 4) {
646 #ifdef DEBUG
647           fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
648 #endif
649           FatalError("Transport Element Descriptor Length too short: %d\n",TransportElementDescriptorLength);
650         } 
651         
652       }
653 #ifdef DEBUG
654       fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
655 #endif
656       BytesAvailable =
657         BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
658       if (BytesAvailable <= 0) {
659               ElementCount--;
660       }
661       while (BytesAvailable > 0)
662         {
663           /* TransportElementDescriptor =
664              (TransportElementDescriptor_T *) DataPointer; */
665           memcpy(&TEBuf, DataPointer, 
666                         (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
667                                 TransportElementDescriptorLength  :
668                                 sizeof(TEBuf));
669           TransportElementDescriptor = &TEBuf;
670
671           DataPointer += TransportElementDescriptorLength;
672           BytesAvailable -= TransportElementDescriptorLength;
673           ElementCount--;
674           switch (ElementStatusPage->ElementTypeCode)
675             {
676             case MediumTransportElement:
677               ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
678 #ifdef DEBUG
679               fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress); 
680 #endif
681               break;
682               /* we treat ImportExport elements as if they were normal
683                * storage elements now, sigh...
684                */
685             case ImportExportElement:
686               ElementStatus->ImportExportCount++;
687 #ifdef DEBUG
688               fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
689 #endif
690               ElementStatus->StorageElementIsImportExport[ElementStatus->StorageElementCount] = 1;
691               /* WARNING: DO *NOT* PUT A 'break' STATEMENT HERE, it is supposed
692                * to run on into the case for a Storage Element! 
693                */
694             case StorageElement:
695 #ifdef DEBUG
696               fprintf(stderr,"StorageElementCount=%d  ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
697 #endif
698               ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
699                 BigEndian16(TransportElementDescriptor->ElementAddress);
700               ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
701                 TransportElementDescriptor->Full;
702 #ifdef DEBUG
703               fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
704 #endif
705               if (!TransportElementDescriptor->Full)
706                 {
707                   EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
708                     ElementStatus->StorageElementCount; /* slot idx. */
709                     /*   ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
710                 }
711               if ( (TransportElementDescriptorLength >  11) && 
712                    (ElementStatusPage->VolBits & E2_AVOLTAG)) {
713                 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
714                              ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
715               } else {
716                 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0;  /* null string. */;
717               } 
718               if ( (TransportElementDescriptorLength > 11) && 
719                    (ElementStatusPage->VolBits & E2_PVOLTAG)) {
720                 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
721                              ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
722               } else {
723                 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
724               }
725               
726               ElementStatus->StorageElementCount++;
727               /* Note that the original mtx had no check here for 
728                  buffer overflow, though some drives might mistakingly
729                  do one... 
730               */ 
731               if (ElementStatus->StorageElementCount > mode_sense->NumStorage) {
732                 fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
733                         mode_sense->NumStorage,
734                         ElementStatus->StorageElementCount);
735                 fflush(stderr);
736                 return; /* we're done :-(. */
737               }
738
739
740               break;
741
742
743             case DataTransferElement:
744               /* tape drive not installed, go back to top of loop */
745
746               /* if (TransportElementDescriptor->Except) continue ; */
747
748               /* Note: This is for Exabyte tape libraries that improperly
749                  report that they have a 2nd tape drive when they don't. We
750                  could generalize this in an ideal world, but my attempt to
751                  do so failed with dual-drive Exabyte tape libraries that
752                  *DID* have the second drive. Sigh. 
753               */
754               /* No longer need this test due to code restructuring? */
755               /* if (TransportElementDescriptor->AdditionalSenseCode==0x83 && 
756                   TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04) 
757                 continue;
758               */ 
759               /* generalize it. Does it work? Let's try it! */
760               /* No, dammit, following does not work on dual-drive Exabyte
761                  'cause if a tape is in the drive, it sets the AdditionalSense
762                   code to something (sigh).
763               */
764               /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
765                 continue;
766               */ 
767               
768
769                   
770               ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
771                 BigEndian16(TransportElementDescriptor->ElementAddress);
772               ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] = 
773                 TransportElementDescriptor->Full;
774               ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
775                 BigEndian16(TransportElementDescriptor
776                             ->SourceStorageElementAddress);
777
778               if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
779                 FatalError("Too many Data Transfer Elements Reported\n");
780               }
781               if (ElementStatusPage->VolBits & E2_PVOLTAG) {
782                 copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
783                              ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
784               } else {
785                 ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
786               }
787               if (ElementStatusPage->VolBits & E2_AVOLTAG) {
788                 copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
789                              ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
790               } else {
791                 ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
792               }
793               ElementStatus->DataTransferElementCount++;
794               /* 0 actually is a usable element address */
795               /* if (DataTransferElementAddress == 0) */
796               /*        FatalError( */
797               /*  "illegal Data Transfer Element Address %d reported\n", */
798               /* DataTransferElementAddress); */
799               break;
800             default:
801               FatalError("illegal Element Type Code %d reported\n",
802                          ElementStatusPage->ElementTypeCode);
803             }
804         }
805     }
806 }
807
808 /********************* Real ReadElementStatus ********************* */
809
810 /*
811  * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
812  * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
813  * under Linux, which gets around the @#%@ 4k buffer size in Linux. 
814  * We still have the restriction that Linux cuts off the last two
815  * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
816  * verbose widget won't work :-(. 
817  
818  * We now look for that "attached" bit in the inquiry_info to see whether
819  * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
820  * look at the device type in the inquiry_info to see whether it is a media
821  * changer or tape device, and if it's a media changer device, we ignore the
822  * attached bit (one beta tester found an old 4-tape DAT changer that set
823  * the attached bit for both the tape device AND the media changer device). 
824
825 */
826
827 ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
828   ElementStatus_T *ElementStatus;
829   
830   unsigned char *DataBuffer; /* size of data... */
831   
832   int EmptyStorageElementCount=0;
833   int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
834   
835   int empty_idx=0;
836   int invalid_sources=0;
837   boolean is_attached = false;
838   int i,j;
839   
840   ElementModeSense_T *mode_sense = NULL;
841   
842   if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
843     is_attached=true;
844   }
845   if (flags->no_attached) { /* override, sigh */ 
846     is_attached=false;
847   }
848   if (!is_attached) {
849     mode_sense=ReadAssignmentPage(MediumChangerFD);
850   }
851   if (!mode_sense) {
852     mode_sense=(ElementModeSense_T *) xmalloc(sizeof(ElementModeSense_T));
853     mode_sense->NumMediumTransport=MAX_TRANSPORT_ELEMENTS;
854     mode_sense->NumStorage=MAX_STORAGE_ELEMENTS;
855     mode_sense->NumDataTransfer=MAX_TRANSFER_ELEMENTS;
856     mode_sense->MaxReadElementStatusData=  (sizeof(ElementStatusDataHeader_T)
857                                             + 3 * sizeof(ElementStatusPage_T)
858                                             + (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS)
859                                             * sizeof(TransportElementDescriptor_T));
860     
861     /* don't care about the others anyhow at the moment... */
862   }
863   
864   ElementStatus=AllocateElementData(mode_sense);
865
866   /* Now to initialize it (sigh).  */
867   ElementStatus->StorageElementCount = 0;
868   ElementStatus->DataTransferElementCount=0;
869     
870   
871   /* first, allocate some empty storage stuff: Note that we pass this
872    * down to ParseElementStatus (sigh!) 
873    */
874   
875   EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
876   for (i=0;i<mode_sense->NumStorage;i++) {
877     EmptyStorageElementAddress[i]=-1;
878   }
879   
880   /* Okay, now to send some requests for the various types of stuff: */
881
882   /* -----------STORAGE ELEMENTS---------------- */
883   /* Let's start with storage elements: */
884
885   for (i=0;i<mode_sense->NumDataTransfer;i++) {
886     /* initialize them to an illegal # so that we can fix later... */
887     ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1; 
888   }
889   
890
891   flags->elementtype=StorageElement; /* sigh! */
892   DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
893                                       inquiry_info,flags,
894                                       mode_sense->StorageStart,
895                                       /* adjust for import/export. */
896                                       mode_sense->NumStorage-mode_sense->NumImportExport,
897                                       mode_sense->MaxReadElementStatusData);
898   if (!DataBuffer) {
899     /* darn. Free up stuff and return. */
900     /****FIXME**** do a free on element data! */
901     FreeElementData(ElementStatus);
902     return NULL; 
903   } 
904
905   ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
906                      DataBuffer,ElementStatus,mode_sense);
907
908   free(DataBuffer); /* sigh! */
909
910   /* --------------IMPORT/EXPORT--------------- */
911   /* Next let's see if we need to do Import/Export: */
912   if (mode_sense->NumImportExport > 0) {
913     flags->elementtype=ImportExportElement;
914     DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
915                                         inquiry_info,flags,
916                                         mode_sense->ImportExportStart,
917                                         mode_sense->NumImportExport,
918                                         mode_sense->MaxReadElementStatusData);
919     
920     if (!DataBuffer) {
921       /* darn. Free up stuff and return. */
922       /****FIXME**** do a free on element data! */
923       FreeElementData(ElementStatus);
924       return NULL; 
925     } 
926     ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
927                      DataBuffer,ElementStatus,mode_sense);
928
929   }
930   
931
932
933   /* ----------------- DRIVES ---------------------- */
934
935
936   flags->elementtype=DataTransferElement; /* sigh! */
937   DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
938                                       inquiry_info,flags,
939                                       mode_sense->DataTransferStart,
940                                       mode_sense->NumDataTransfer,
941                                       mode_sense->MaxReadElementStatusData);
942   if (!DataBuffer) {
943     /* darn. Free up stuff and return. */
944     /****FIXME**** do a free on element data! */
945     FreeElementData(ElementStatus);
946     return NULL; 
947   } 
948
949   ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
950                      DataBuffer,ElementStatus,mode_sense);
951
952   free(DataBuffer); /* sigh! */
953
954
955   /* ----------------- Robot Arm(s) -------------------------- */
956
957     /* grr, damned brain dead HP doesn't report that it has any! */
958   if (!mode_sense->NumMediumTransport) { 
959     ElementStatus->TransportElementAddress=0; /* default it sensibly :-(. */
960   } else {
961      flags->elementtype=MediumTransportElement; /* sigh! */
962      DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
963                                       inquiry_info,flags,
964                                       mode_sense->MediumTransportStart,
965                                       1, /* only get 1, sigh. */
966                                       mode_sense->MaxReadElementStatusData);
967      if (!DataBuffer) {
968        /* darn. Free up stuff and return. */
969        /****FIXME**** do a free on element data! */
970        FreeElementData(ElementStatus);
971        return NULL; 
972      } 
973    
974      ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
975                      DataBuffer,ElementStatus,mode_sense);
976
977      free(DataBuffer); /* sigh! */
978   }
979
980   /*---------------------- Sanity Checking ------------------- */
981
982   if (!ElementStatus->DataTransferElementCount)
983     FatalError("no Data Transfer Element reported\n");
984   if (ElementStatus->StorageElementCount == 0)
985     FatalError("no Storage Elements reported\n");
986
987
988   /* ---------------------- Reset SourceStorageElementNumbers ------- */
989
990   /* okay, re-write the SourceStorageElementNumber code  *AGAIN*.
991    * Pass1:
992    *   Translate from raw element # to our translated # (if possible).
993    * First, check the SourceStorageElementNumbers against the list of 
994    * filled slots. If the slots indicated are empty, we accept that list as
995    * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
996    *
997    * Pass2:
998    *
999    * If we had some invalid (or unknown) SourceStorageElementNumbers
1000    * then we must search for free slots, and assign SourceStorageElementNumbers
1001    * to those free slots. We happen to already built a list of free
1002    * slots as part of the process of reading the storage element numbers
1003    * from the tape. So that's easy enough to do! 
1004    */
1005
1006 #ifdef DEBUG_TAPELIST
1007   fprintf(stderr,"empty slots: %d\n",EmptyStorageElementCount);
1008   if (EmptyStorageElementCount) {
1009      for (i=0; i<EmptyStorageElementCount; i++) {
1010          fprintf(stderr,"empty: %d\n",EmptyStorageElementAddress[i]);
1011      }
1012   }
1013 #endif
1014
1015   /* Okay, now we re-assign origin slots if the "real" origin slot
1016    * is obviously defective: 
1017    */
1018   /* pass one: */
1019   invalid_sources=0; /* no invalid sources yet! */
1020   for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1021     int elnum;
1022     int translated_elnum = -1;
1023     /* if we have an element, then ... */
1024     if (ElementStatus->DataTransferElementFull[i]) {
1025       elnum=ElementStatus->DataTransferElementSourceStorageElementNumber[i];
1026       /* if we have an element number, then ... */
1027       if (elnum >= 0) {
1028          /* Now to translate the elnum: */
1029          for (j=0; j<ElementStatus->StorageElementCount; j++) {
1030              if (elnum == ElementStatus->StorageElementAddress[j]) {
1031                  translated_elnum=j; 
1032              }
1033          }
1034       /* now see if the element # is already occupied: */
1035         if (ElementStatus->StorageElementFull[translated_elnum]) {
1036           invalid_sources=1;
1037           break; /* break out of the loop! */
1038         } else {
1039            /* properly set the source... */
1040         ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1041                 translated_elnum;
1042         }
1043                  
1044       } else {
1045         /* if element # was not >=0, then we had an invalid source anyhow! */
1046         invalid_sources=1;
1047       }
1048     }
1049   }
1050
1051 #ifdef DEBUG_TAPELIST
1052    fprintf(stderr,"invalid_sources=%d\n",invalid_sources);
1053 #endif
1054
1055   /* Pass2: */
1056   /* okay, we have invalid sources, so let's see what they should be: */
1057   /* Note: If EmptyStorageElementCount is < # of drives, the leftover
1058    * drives will be assigned a -1 (see the initialization loop for
1059    * EmptyStorageElementAddress above), which will be reported as "slot 0"
1060    * by the user interface. This is an invalid value, but more useful for us
1061    * to have than just crapping out here :-(. 
1062    */ 
1063   if (invalid_sources) {
1064     empty_idx=0;
1065     for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
1066       if (ElementStatus->DataTransferElementFull[i]) {
1067 #ifdef DEBUG_TAPELIST
1068         fprintf(stderr,"for drive %d, changing source %d to %d (empty slot #%d)\n",
1069              i,
1070              ElementStatus->DataTransferElementSourceStorageElementNumber[i],
1071              EmptyStorageElementAddress[empty_idx],
1072              empty_idx);
1073 #endif
1074               
1075         ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
1076           EmptyStorageElementAddress[empty_idx++];
1077       }
1078     }
1079   }
1080
1081   /* and done! */      
1082   free(mode_sense);
1083   free(EmptyStorageElementAddress);
1084   return ElementStatus;
1085   
1086 }
1087
1088 /*************************************************************************/
1089
1090
1091
1092 /* Now the actual media movement routine! */
1093 RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
1094                        int DestinationAddress, 
1095                        ElementStatus_T *ElementStatus,
1096                        Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1097 {
1098   RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1099   CDB_T CDB;
1100   if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {  /* if using the ATTACHED API */
1101     CDB[0]=0xA7;   /* MOVE_MEDIUM_ATTACHED */
1102   } else {
1103     CDB[0] = 0xA5;              /* MOVE MEDIUM */
1104   }
1105   CDB[1] = 0;                   /* Reserved */
1106   CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF;  /* Transport Element Address MSB */
1107   CDB[3] = (ElementStatus->TransportElementAddress) & 0xff;   /* Transport Element Address LSB */
1108   CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
1109   CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
1110   CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
1111   CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
1112   CDB[8] = 0;                   /* Reserved */
1113   CDB[9] = 0;                   /* Reserved */
1114   if (flags->invert) {
1115     CDB[10] = 1;                        /* Reserved */
1116   } else {
1117     CDB[10] = 0;
1118   }
1119   /* eepos controls the tray for import/export elements, sometimes. */
1120   CDB[11] = 0 | (flags->eepos <<6);                     /* Control */
1121   
1122   if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1123                           NULL, 0, RequestSense) != 0) {
1124     return RequestSense;
1125   }
1126   free(RequestSense);
1127   return NULL; /* success! */
1128 }
1129
1130 /* for Linux, this creates a way to do a short erase... the @#$%@ st.c
1131  * driver defaults to doing a long erase!
1132  */
1133
1134 RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD) {
1135   RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1136   CDB_T CDB;
1137
1138   CDB[0]=0x19;
1139   CDB[1]=0;  /* Short! */
1140   CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
1141
1142   if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
1143                           NULL, 0, RequestSense) != 0) {
1144     return RequestSense;
1145   }
1146   free(RequestSense);
1147   return NULL;  /* Success! */
1148 }  
1149
1150
1151 #ifdef LONG_PRINT_REQUEST_SENSE
1152
1153 static char *sense_keys[] = {
1154   "No Sense",  /* 00 */
1155   "Recovered Error", /* 01 */
1156   "Not Ready",  /* 02 */
1157   "Medium Error",  /* 03 */
1158   "Hardware Error",  /* 04 */
1159   "Illegal Request",  /* 05 */
1160   "Unit Attention", /* 06 */
1161   "Data Protect",   /* 07 */
1162   "Blank Check", /* 08 */
1163   "0x09",   /* 09 */
1164   "0x0a",   /* 0a */
1165   "Aborted Command", /* 0b */
1166   "0x0c",  /* 0c */
1167   "Volume Overflow", /* 0d */
1168   "Miscompare",   /* 0e */
1169   "0x0f"  /* 0f */
1170 };
1171
1172 static char Yes[]="yes";
1173 static char No[]="no";
1174
1175 void PrintRequestSense(RequestSense_T *RequestSense)
1176 {
1177   char *msg;
1178
1179   fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
1180   fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
1181   if (RequestSense->ErrorCode==0x70) { 
1182     msg = "Current" ;
1183   } else if (RequestSense->ErrorCode==0x71) { 
1184     msg = "Deferred" ;
1185   } else {
1186     msg = "Unknown?!" ;
1187   }
1188   fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n",RequestSense->ErrorCode,msg);
1189   fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n",sense_keys[RequestSense->SenseKey]);
1190   fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
1191   fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
1192   fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
1193   if (RequestSense->Valid) {
1194     fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
1195   } 
1196   fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
1197   fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
1198   if (RequestSense->SKSV) {
1199     fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n",RequestSense->BitPointer);
1200   }
1201   fprintf(stderr,"mtx: Request Sense: BPV=%s\n",RequestSense->BPV ? Yes : No);
1202   fprintf(stderr,"mtx: Request Sense: Error in CDB=%s\n",RequestSense->CommandData ? Yes : No);
1203   fprintf(stderr,"mtx: Request Sense: SKSV=%s\n",RequestSense->SKSV ? Yes : No);
1204   fflush(stderr);
1205   if (RequestSense->BPV || RequestSense -> SKSV) {
1206     fprintf(stderr,"mtx: Request Sense: Field Pointer = %02X %02X\n",RequestSense->FieldData[0],RequestSense->FieldData[1]);
1207   }
1208 }
1209
1210
1211 #else
1212 void PrintRequestSense(RequestSense_T *RequestSense)
1213 {
1214   int i;
1215   fprintf(stderr, "mtx: Request Sense: %02X",
1216           ((unsigned char *) RequestSense)[0]);
1217   for (i = 1; i < sizeof(RequestSense_T); i++)
1218     fprintf(stderr, " %02X", ((unsigned char *) RequestSense)[i]);
1219   fprintf(stderr, "\n");
1220 }
1221
1222 #endif
1223
1224 /* $Date: 2002/09/27 17:23:28 $
1225  * $Log: mtxl.c,v $
1226  * Revision 1.8.2.7  2002/09/27 17:23:28  elgreen
1227  * Don't dereference pointer in barcode stuff
1228  *
1229  * Revision 1.8.2.6  2002/09/27 17:20:18  elgreen
1230  * doh, get rid of the + from the patch process!
1231  *
1232  * Revision 1.8.2.5  2002/09/27 17:17:13  elgreen
1233  * fix copy_barcode
1234  *
1235  * Revision 1.8.2.4  2002/09/27 16:49:43  elgreen
1236  * copy_barcode was off by one (sigh!)
1237  *
1238  * Revision 1.8.2.3  2002/02/05 16:51:11  elgreen
1239  * mtx 1.2.16pre3
1240  *
1241  * Revision 1.8.2.2  2002/01/22 16:27:47  elgreen
1242  * Handle too-big transport element descriptor lengths
1243  *
1244  * Revision 1.8.2.1  2002/01/17 22:24:35  elgreen
1245  * Handle ADIC silliness of saying that it has 1 import/export element whose
1246  * descriptor is 0 bytes in length
1247  *
1248  * Revision 1.8  2001/06/25 23:06:22  elgreen
1249  * Readying this for 1.2.13 release
1250  *
1251  * Revision 1.7  2001/06/25 04:56:35  elgreen
1252  * Kai to the rescue *again* :-)
1253  *
1254  * Revision 1.6  2001/06/24 07:02:01  elgreen
1255  * Kai's fix was better than mine.
1256  *
1257  * Revision 1.5  2001/06/24 06:59:19  elgreen
1258  * Kai found bug in the barcode backoff.
1259  *
1260  * Revision 1.4  2001/06/15 18:56:54  elgreen
1261  * Arg, it doesn't help to check for 0 robot arms if you force it to 1!
1262  *
1263  * Revision 1.3  2001/06/15 14:26:09  elgreen
1264  * Fixed brain-dead case of HP loaders that report they have no robot arms.
1265  *
1266  * Revision 1.2  2001/06/09 17:26:26  elgreen
1267  * Added 'nobarcode' command to mtx (to skip the initial request asking for
1268  * barcodes for mtx status purposes).
1269  *
1270  * Revision 1.1.1.1  2001/06/05 17:10:25  elgreen
1271  * Initial import into SourceForge
1272  *
1273  * Revision 1.29  2001/05/01 01:39:23  eric
1274  * Remove the Exabyte special case code, which seemed to be barfing me :-(.
1275  *
1276  * Revision 1.28  2001/04/28 00:03:10  eric
1277  * update for 1.2.12.
1278  *
1279  * Revision 1.27  2001/04/18 19:27:39  eric
1280  * whoops, move ImportExport parser into the $#%!@ ImportExport test :-(.
1281  *
1282  * Revision 1.26  2001/04/18 18:55:44  eric
1283  * turn too many storage elements into a warning rather than a fatal error (sigh!)
1284  *
1285  * Revision 1.25  2001/04/18 18:46:54  eric
1286  * $%!@# "C" precedence rules.
1287  *
1288  * Revision 1.24  2001/04/18 17:58:04  eric
1289  * initialize EmptyStorageElementCount (sigh)
1290  *
1291  * Revision 1.23  2001/04/18 16:55:03  eric
1292  * hopefully get the initialization for element status straigtened out.
1293  *
1294  * Revision 1.22  2001/04/18 16:32:59  eric
1295  * Cleaned up all -Wall messages.
1296  *
1297  * Revision 1.21  2001/04/18 16:08:08  eric
1298  * after re-arranging & fixing bugs
1299  *
1300  * Revision 1.20  2001/04/17 21:28:43  eric
1301  * mtx 1.2.11
1302  *
1303  * Revision 1.19  2001/04/02 20:12:48  eric
1304  * checkin, mtx 1.2.11pre6
1305  *
1306  * Revision 1.18  2001/03/06 01:37:05  eric
1307  * Solaris changes
1308  *
1309  * Revision 1.17  2001/02/26 20:56:12  eric
1310  * added debugging, removed Makefile
1311  *
1312  * Revision 1.16  2001/02/19 23:22:28  eric
1313  * Add E2 bits
1314  *
1315  * Revision 1.15  2001/02/19 23:02:04  eric
1316  * Apply SPARC patch.
1317  *
1318  * Revision 1.14  2001/01/26 15:58:10  eric
1319  * Make work on solaris?
1320  *
1321  * Revision 1.13  2001/01/17 19:39:01  eric
1322  * Changed mtx.h and mtxl.c to use autoconf stuff to figure out which SCSI
1323  * routines to use (this way, if we ever port to an OS that has an identical
1324  * SCSI interface to one of the supported OS's, we don't have to do anything
1325  * special!).
1326  *
1327  * Revision 1.12  2001/01/17 16:36:26  eric
1328  * added date & revision strings as needed
1329  *
1330  * Revision 1.11  2000/11/30 20:35:18  eric
1331  * Added Erase command.
1332  *
1333  */