f3beea343aa7abc484a24e9359466d9d8a2c8c3f
[debian/mtx] / mtxl.c
1 /*  MTX -- SCSI Tape Attached Medium Changer Control Program
2
3         Copyright 1997-1998 by Leonard N. Zubkoff.
4         Copyright 1999-2006 by Eric Lee Green.
5         Copyright 2007 by Robert Nelson <robertn@the-nelsons.org>
6
7         $Date: 2007-03-24 18:14:01 -0700 (Sat, 24 Mar 2007) $
8         $Revision: 166 $
9
10         This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
11         extracted from mtx.c, plus a near total re-write of most of the beast.
12
13         This program is free software; you may redistribute and/or modify it under
14         the terms of the GNU General Public License Version 2 as published by the
15         Free Software Foundation.
16
17         This program is distributed in the hope that it will be useful, but
18         WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
19         or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20         for complete details.
21 */
22
23
24 /*
25  *      FatalError: changed Feb. 2000 elg@badtux.org to eliminate a buffer
26  *      overflow :-(. That could be important if mtxl is SUID for some reason. 
27 */
28
29 #include "mtx.h"
30 #include "mtxl.h"
31
32 /* #define DEBUG_NSM 1 */
33
34 /* #define DEBUG_MODE_SENSE 1 */
35 /* #define DEBUG */
36 /* #define DEBUG_SCSI */
37 #define __WEIRD_CHAR_SUPPRESS 1 
38
39 /* zap the following define when we finally add real import/export support */
40 #define IMPORT_EXPORT_HACK 1 /* for the moment, import/export == storage */
41
42 /* First, do some SCSI routines: */
43
44 /* the camlib is used on FreeBSD. */
45 #if HAVE_CAMLIB_H
46 #       include "scsi_freebsd.c"
47 #endif
48
49 /* the scsi_ctl interface is used on HP/UX. */
50 #if HAVE_SYS_SCSI_CTL_H
51 #       include "scsi_hpux.c"
52 #endif
53
54 /* the 'sg' interface is used on Linux. */
55 #if HAVE_SCSI_SG_H
56 #       include "scsi_linux.c"
57 #endif
58
59 /* the IOCTL_SCSI_PASSTHROUGH interface is used on Windows. */
60 #if HAVE_DDK_NTDDSCSI_H || defined(_MSC_VER)
61 #       include "scsi_win32.c"
62 #endif
63
64 /* The 'uscsi' interface is used on Solaris. */
65 #if HAVE_SYS_SCSI_IMPL_USCSI_H
66 #       include "scsi_sun.c"
67 #endif
68
69 /* The 'gsc' interface, is used on AIX. */
70 #if HAVE_SYS_GSCDDS_H
71 #       include "scsi_aix.c"
72 #endif
73
74 /* The 'dslib' interface is used on SGI. */
75 #if HAVE_DSLIB_H
76 #       include "scsi_sgi.c"
77 #endif
78
79 /* Hmm, dunno what to do about Digital Unix at the moment. */
80 #ifdef DIGITAL_UNIX
81 #       include "du/scsi.c"
82 #endif
83
84 #ifdef VMS
85 #       include "[.vms]scsi.c"
86 #endif
87
88 extern char *argv0; /* something to let us do good error messages. */
89
90 /* create a global RequestSenseT value. */
91 RequestSense_T scsi_error_sense;
92
93 /* Now for some low-level SCSI stuff: */
94
95 Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense)
96 {
97         Inquiry_T *Inquiry;
98         CDB_T CDB;
99
100         Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));  
101
102         CDB[0] = 0x12;          /* INQUIRY */
103         CDB[1] = 0;                     /* EVPD = 0 */
104         CDB[2] = 0;                     /* Page Code */
105         CDB[3] = 0;                     /* Reserved */
106         CDB[4] = sizeof(Inquiry_T);     /* Allocation Length */
107         CDB[5] = 0;                     /* Control */
108
109         /* set us a very short timeout, sigh... */
110         SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
111
112         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
113                 Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
114         {
115 #ifdef DEBUG
116                 fprintf(stderr, "SCSI Inquiry Command failed\n");
117 #endif
118                 free(Inquiry);
119                 return NULL;  /* sorry! */
120         }
121         return Inquiry;
122 }
123
124
125 #if defined(DEBUG_NSM) || defined(DEBUG_EXCHANGE)
126 /* DEBUG */
127 static void dump_cdb(unsigned char *CDB, int len)
128 {
129         fprintf(stderr,"CDB:");
130         PrintHex(1, CDB, len);
131 }
132 #endif
133
134
135 #if defined(DEBUG_NSM) || defined(DEBUG_ADIC)
136 /* DEBUG */
137 static void dump_data(unsigned char *data, int len)
138 {
139         if (!len)
140         {
141                 fprintf(stderr,"**NO DATA**\n");
142                 return;
143         }
144
145         fprintf(stderr,"DATA:");
146         PrintHex(1, data, len);
147 }
148 #endif
149
150
151 int BigEndian16(unsigned char *BigEndianData)
152 {
153         return (BigEndianData[0] << 8) + BigEndianData[1];
154 }
155
156
157 int BigEndian24(unsigned char *BigEndianData)
158 {
159         return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
160 }
161
162
163 void FatalError(char *ErrorMessage, ...)
164 {
165 #define FORMAT_BUF_LEN 1024
166
167         char    FormatBuffer[FORMAT_BUF_LEN];
168         char    *SourcePointer;
169         char    *TargetPointer = FormatBuffer;
170         char    Character, LastCharacter = '\0';
171         int             numchars = 0;
172
173         va_list ArgumentPointer;
174         va_start(ArgumentPointer, ErrorMessage);
175         /*  SourcePointer = "mtx: "; */
176         sprintf(TargetPointer,"%s: ",argv0);
177         numchars=strlen(TargetPointer);
178
179         while ((Character = *ErrorMessage++) != '\0')
180         {
181                 if (LastCharacter == '%')
182                 {
183                         if (Character == 'm')
184                         {
185                                 SourcePointer = strerror(errno);
186                                 while ((Character = *SourcePointer++) != '\0')
187                                 {
188                                         *TargetPointer++ = Character;
189                                         numchars++;
190                                         if (numchars == FORMAT_BUF_LEN - 1)
191                                         {
192                                                 break;
193                                         }
194                                 }
195                                 if (numchars == FORMAT_BUF_LEN - 1)
196                                 {
197                                         break;  /* break outer loop... */
198                                 }
199                         }
200                         else
201                         {
202                                 *TargetPointer++ = '%';
203                                 *TargetPointer++ = Character;
204                                 numchars++;
205                                 if (numchars == FORMAT_BUF_LEN - 1)
206                                 {
207                                         break;
208                                 }
209                         }
210                         LastCharacter = '\0';
211                 }
212                 else
213                 {
214                         if (Character != '%')
215                         {
216                                 *TargetPointer++ = Character;
217                                 numchars++;
218                                 if (numchars == FORMAT_BUF_LEN-1)
219                                 {
220                                         break;
221                                 }
222                         }
223                         LastCharacter = Character;
224                 }
225         }
226
227         *TargetPointer = '\0';  /* works even if we had to break from above... */
228         vfprintf(stderr, FormatBuffer, ArgumentPointer);
229         va_end(ArgumentPointer);
230
231 #ifndef VMS
232         exit(1);
233 #else
234         sys$exit(VMS_ExitCode);
235 #endif
236 }
237
238 /* This is a really slow and stupid 'bzero' implementation'... */
239 void slow_bzero(char *buffer, int numchars)
240 {
241         while (numchars--)
242         {
243                 *buffer++ = 0;
244         }
245 }
246
247 /* malloc some memory while checking for out-of-memory conditions. */
248 void *xmalloc(size_t Size)
249 {
250         void *Result = (void *) malloc(Size);
251         if (Result == NULL)
252         {
253                 FatalError("cannot allocate %d bytes of memory\n", Size);
254         }
255         return Result;
256 }
257
258 /* malloc some memory, zeroing it too: */
259 void *xzmalloc(size_t Size)
260 {
261         void *Result = (void *)xmalloc(Size);
262
263         slow_bzero(Result, Size);
264         return Result;
265 }
266
267
268 int min(int x, int y)
269 {
270         return (x < y ? x : y);
271 }
272
273
274 int max(int x, int y)
275 {
276         return (x > y ? x : y);
277 }
278
279
280 /* Okay, this is a hack for the NSM modular jukebox series, which
281  * uses the "SEND DIAGNOSTIC" command to do shit. 
282  */
283
284 int SendNSMHack(DEVICE_TYPE MediumChangerFD, NSM_Param_T *nsm_command, 
285                 int param_len, int timeout)
286 {
287         CDB_T CDB;
288         int list_len = param_len + sizeof(NSM_Param_T) - 2048;
289
290         /* Okay, now for the command: */
291         CDB[0] = 0x1D;
292         CDB[1] = 0x10;
293         CDB[2] = 0;
294         CDB[3] = (unsigned char)(list_len >> 8);
295         CDB[4] = (unsigned char)list_len;
296         CDB[5] = 0;
297
298 #ifdef DEBUG_NSM
299         dump_cdb(&CDB,6);
300         dump_data(nsm_command,list_len);
301 #endif
302         fflush(stderr);
303         /* Don't set us any timeout unless timeout is > 0 */
304         if (timeout > 0)
305         {
306                 SCSI_Set_Timeout(timeout); /* 30 minutes, sigh! */
307         }
308
309         /* Now send the command: */
310         if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6, nsm_command, list_len, &scsi_error_sense))
311         {
312                 return -1; /* we failed */
313         }
314         return 0; /* Did it! */
315 }
316
317 NSM_Result_T *RecNSMHack(       DEVICE_TYPE MediumChangerFD,
318                                                         int param_len, int timeout)
319 {
320         CDB_T CDB;
321
322         NSM_Result_T *retval = (NSM_Result_T *)xzmalloc(sizeof(NSM_Result_T));
323
324         int list_len = param_len + sizeof(NSM_Result_T) - 0xffff;
325
326         /* Okay, now for the command: */
327         CDB[0] = 0x1C;
328         CDB[1] = 0x00;
329         CDB[2] = 0;
330         CDB[3] = (unsigned char)(list_len >> 8);
331         CDB[4] = (unsigned char)list_len;
332         CDB[5] = 0;
333
334 #ifdef DEBUG_NSM
335         dump_cdb(&CDB,6);
336 #endif
337
338         /* Don't set us any timeout unless timeout is > 0 */
339         if (timeout > 0)
340         {
341                 SCSI_Set_Timeout(timeout);
342         }
343
344         /* Now send the command: */
345         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, retval, list_len, &scsi_error_sense))
346         {
347                 return NULL; /* we failed */
348         }
349
350 #ifdef DEBUG_NSM
351         fprintf(stderr,
352                         "page_code=%02x page_len=%d command_code=%s\n",
353                         retval->page_code,
354                         (int) ((retval->page_len_msb << 8) + retval->page_len_lsb),
355         retval->command_code);
356 #endif
357
358         return retval; /* Did it! (maybe!)*/
359 }
360
361 /* Routine to inventory the library. Needed by, e.g., some Breece Hill
362  * loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
363  * has no parameters, such as a range to scan :-(. 
364  */
365
366 int Inventory(DEVICE_TYPE MediumChangerFD)
367 {
368         CDB_T   CDB;
369
370         /* okay, now for the command: */
371         CDB[0] = 0x07; 
372         CDB[1] = CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
373
374         /* set us a very long timeout, sigh... */
375         SCSI_Set_Timeout(30 * 60); /* 30 minutes, sigh! */
376
377         if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&scsi_error_sense) != 0)
378         {
379 #ifdef DEBUG
380                 PrintRequestSense(&scsi_error_sense);
381                 fprintf(stderr, "Initialize Element Status (0x07) failed\n");
382 #endif
383                 return -1;  /* could not do! */
384         }
385         return 0; /* did do! */
386 }
387
388 /* Routine to read the Mode Sense Element Address Assignment Page */
389 /* We try to read the page. If we can't read the page, we return NULL.
390  * Our caller really isn't too worried about why we could not read the
391  * page, it will simply default to some kind of default values. 
392  */ 
393 ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD)
394 {
395         CDB_T CDB;
396         ElementModeSense_T *retval;  /* processed SCSI. */
397         unsigned char input_buffer[136];
398         ElementModeSensePage_T *sense_page; /* raw SCSI. */
399
400         /* okay, now for the command: */
401         CDB[0] = 0x1A; /* Mode Sense(6) */
402         CDB[1] = 0x08; 
403         CDB[2] = 0x1D; /* Mode Sense Element Address Assignment Page */
404         CDB[3] = 0;
405         CDB[4] = 136; /* allocation_length... */
406         CDB[5] = 0;
407
408         /* clear the data buffer: */
409         slow_bzero((char *)&scsi_error_sense, sizeof(RequestSense_T));
410         slow_bzero((char *)input_buffer, sizeof(input_buffer));
411
412         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6,
413                                                         &input_buffer, sizeof(input_buffer), &scsi_error_sense) != 0)
414         {
415                 PrintRequestSense(&scsi_error_sense);
416                 fprintf(stderr,"Mode sense (0x1A) for Page 0x1D failed\n");
417                 fflush(stderr);
418                 return NULL; /* sorry, couldn't do it. */
419         }
420
421         /* Could do it, now build return value: */
422
423 #ifdef DEBUG_MODE_SENSE
424         PrintHex(0, input_buffer, 30);
425 #endif
426
427         /* first, skip past: mode data header, and block descriptor header if any */
428         sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
429
430 #ifdef DEBUG_MODE_SENSE
431         fprintf(stderr,"*sense_page=%x  %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
432         fflush(stderr);
433 #endif
434
435         retval = (ElementModeSense_T *)xzmalloc(sizeof(ElementModeSense_T));
436
437         /* Remember that all SCSI values are big-endian: */
438         retval->MediumTransportStart =
439                 (((int)sense_page->MediumTransportStartHi) << 8) +
440                 sense_page->MediumTransportStartLo;
441
442         retval->NumMediumTransport =
443                 (((int)(sense_page->NumMediumTransportHi))<<8) +
444                 sense_page->NumMediumTransportLo;
445
446         /* HACK! Some HP autochangers don't report NumMediumTransport right! */
447         /* ARG! TAKE OUT THE %#@!# HACK! */
448 #ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
449         if (!retval->NumMediumTransport)
450         {
451                 retval->NumMediumTransport = 1;
452         }
453 #endif
454
455 #ifdef DEBUG_MODE_SENSE
456         fprintf(stderr, "rawNumStorage= %d %d    rawNumImportExport= %d %d\n",
457                         sense_page->NumStorageHi, sense_page->NumStorageLo,
458                         sense_page->NumImportExportHi, sense_page->NumImportExportLo);
459         fprintf(stderr, "rawNumTransport=%d %d  rawNumDataTransfer=%d %d\n",
460                         sense_page->NumMediumTransportHi, sense_page->NumMediumTransportLo,
461                         sense_page->NumDataTransferHi, sense_page->NumDataTransferLo);
462         fflush(stderr);
463 #endif
464
465         retval->StorageStart =
466                 ((int)sense_page->StorageStartHi << 8) + sense_page->StorageStartLo;
467
468         retval->NumStorage =
469                 ((int)sense_page->NumStorageHi << 8) + sense_page->NumStorageLo;
470
471         retval->ImportExportStart =
472                 ((int)sense_page->ImportExportStartHi << 8) + sense_page->ImportExportStartLo; 
473
474         retval->NumImportExport =
475                 ((int)sense_page->NumImportExportHi << 8) + sense_page->NumImportExportLo;
476
477         retval->DataTransferStart =
478                 ((int)sense_page->DataTransferStartHi << 8) + sense_page->DataTransferStartLo;
479
480         retval->NumDataTransfer =
481                 ((int)sense_page->NumDataTransferHi << 8) + sense_page->NumDataTransferLo;
482
483         /* allocate a couple spares 'cause some HP autochangers and maybe others
484         * don't properly report the robotics arm(s) count here... 
485         */
486         retval->NumElements =
487                 retval->NumStorage+retval->NumImportExport +
488                 retval->NumDataTransfer+retval->NumMediumTransport;
489
490         retval->MaxReadElementStatusData =
491                 (sizeof(ElementStatusDataHeader_T) +
492                 4 * sizeof(ElementStatusPage_T) +
493                 retval->NumElements * sizeof(TransportElementDescriptor_T));
494
495 #ifdef IMPORT_EXPORT_HACK
496         retval->NumStorage = retval->NumStorage+retval->NumImportExport;
497 #endif
498
499 #ifdef DEBUG_MODE_SENSE
500         fprintf(stderr, "NumMediumTransport=%d\n", retval->NumMediumTransport);
501         fprintf(stderr, "NumStorage=%d\n", retval->NumStorage);
502         fprintf(stderr, "NumImportExport=%d\n", retval->NumImportExport);
503         fprintf(stderr, "NumDataTransfer=%d\n", retval->NumDataTransfer);
504         fprintf(stderr, "MaxReadElementStatusData=%d\n", retval->MaxReadElementStatusData);
505         fprintf(stderr, "NumElements=%d\n", retval->NumElements);
506         fflush(stderr);
507 #endif
508
509         return retval;
510 }
511
512 static void FreeElementData(ElementStatus_T *data)
513 {
514         free(data->DataTransferElementAddress);
515         free(data->DataTransferElementSourceStorageElementNumber);
516         free(data->DataTransferPrimaryVolumeTag);
517         free(data->DataTransferAlternateVolumeTag);
518         free(data->PrimaryVolumeTag);
519         free(data->AlternateVolumeTag);
520         free(data->StorageElementAddress);
521         free(data->StorageElementIsImportExport);
522         free(data->StorageElementFull);
523         free(data->DataTransferElementFull);
524 }
525
526
527 /* allocate data  */
528
529 static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense)
530 {
531         ElementStatus_T *retval;
532
533         retval = (ElementStatus_T *)xzmalloc(sizeof(ElementStatus_T));
534
535         /* now for the invidual component arrays.... */
536
537         retval->DataTransferElementAddress =
538                 (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
539         retval->DataTransferElementSourceStorageElementNumber = 
540                 (int *)xzmalloc(sizeof(int) * (mode_sense->NumDataTransfer + 1));
541         retval->DataTransferPrimaryVolumeTag =
542                 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
543         retval->DataTransferAlternateVolumeTag =
544                 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumDataTransfer + 1));
545         retval->PrimaryVolumeTag =
546                 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
547         retval->AlternateVolumeTag =
548                 (barcode *)xzmalloc(sizeof(barcode) * (mode_sense->NumStorage + 1));
549         retval->StorageElementAddress =
550                 (int *)xzmalloc(sizeof(int) * (mode_sense->NumStorage + 1));
551         retval->StorageElementIsImportExport =
552                 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
553         retval->StorageElementFull =
554                 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumStorage + 1));
555         retval->DataTransferElementFull =
556                 (boolean *)xzmalloc(sizeof(boolean) * (mode_sense->NumDataTransfer + 1));
557
558         return retval; /* sigh! */
559 }
560
561
562 void copy_barcode(unsigned char *src, unsigned char *dest)
563 {
564         int i;
565
566         for (i=0; i < 36; i++)
567         {
568                 *dest = *src++;
569
570                 if ((*dest < 32) || (*dest > 127))
571                 {
572                         *dest = '\0';
573                 }
574
575                 dest++;
576         }
577         *dest = 0; /* null-terminate */ 
578 }
579
580 /* This #%!@# routine has more parameters than I can count! */
581 static unsigned char *SendElementStatusRequestActual(
582                                         DEVICE_TYPE MediumChangerFD,
583                                         RequestSense_T *RequestSense,
584                                         Inquiry_T *inquiry_info, 
585                                         SCSI_Flags_T *flags,
586                                         int ElementStart,
587                                         int NumElements,
588                                         int NumBytes
589                                         )
590 {
591         CDB_T CDB;
592         boolean is_attached = false;
593
594         unsigned char *DataBuffer; /* size of data... */
595
596 #ifdef HAVE_GET_ID_LUN
597         scsi_id_t *scsi_id;
598 #endif
599         if (inquiry_info->MChngr && 
600                 inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
601         {
602                 is_attached = true;
603         }
604
605         if (flags->no_attached)
606         {
607                 /* override, sigh */ 
608                 is_attached = false;
609         }
610
611         DataBuffer = (unsigned char *)xzmalloc(NumBytes + 1);
612
613         slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
614
615 #ifdef HAVE_GET_ID_LUN
616         scsi_id = SCSI_GetIDLun(MediumChangerFD);
617 #endif
618
619         CDB[0] = 0xB8;          /* READ ELEMENT STATUS */
620
621         if (is_attached)
622         {
623                 CDB[0] = 0xB4;  /* whoops, READ_ELEMENT_STATUS_ATTACHED! */ 
624         }
625
626 #ifdef HAVE_GET_ID_LUN
627         CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 
628                 0 : 0x10) | flags->elementtype;  /* Lun + VolTag + Type code */
629         free(scsi_id);
630 #else
631         /* Element Type Code = 0, VolTag = 1 */
632         CDB[1] = (unsigned char)((flags->no_barcodes ? 0 : 0x10) | flags->elementtype);
633 #endif
634         /* Starting Element Address */
635         CDB[2] = (unsigned char)(ElementStart >> 8);
636         CDB[3] = (unsigned char)ElementStart;
637
638         /* Number Of Elements */
639         CDB[4]= (unsigned char)(NumElements >> 8);
640         CDB[5]= (unsigned char)NumElements;
641
642         CDB[6] = 0;                     /* Reserved */
643
644         /* allocation length */
645         CDB[7]= (unsigned char)(NumBytes >> 16);
646         CDB[8]= (unsigned char)(NumBytes >> 8);
647         CDB[9]= (unsigned char)NumBytes;
648
649         CDB[10] = 0;                    /* Reserved */
650         CDB[11] = 0;                    /* Control */
651
652 #ifdef DEBUG_BARCODE
653         fprintf(stderr,"CDB:\n");
654         PrintHex(2, CDB, 12);
655 #endif
656
657         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
658                 DataBuffer,NumBytes, RequestSense) != 0)
659         {
660
661 #ifdef DEBUG
662                 fprintf(stderr, "Read Element Status (0x%02X) failed\n", CDB[0]);
663 #endif
664
665                 /*
666                         First see if we have sense key of 'illegal request',
667                         additional sense code of '24', additional sense qualfier of 
668                         '0', and field in error of '4'. This means that we issued a request
669                         w/bar code reader and did not have one, thus must re-issue the request
670                         w/out barcode :-(.
671                 */
672
673                 /*
674                         Most autochangers and tape libraries set a sense key here if
675                         they do not have a bar code reader. For some reason, the ADIC DAT
676                         uses a different sense key? Let's retry w/o bar codes for *ALL*
677                         sense keys.
678                 */
679
680                 if (RequestSense->SenseKey > 1)
681                 {
682                         /* we issued a code requesting bar codes, there is no bar code reader? */
683                         /* clear out our sense buffer first... */
684                         slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
685
686                         CDB[1] &= ~0x10; /* clear bar code flag! */
687
688 #ifdef DEBUG_BARCODE
689                         fprintf(stderr,"CDB:\n");
690                         PrintHex(2, CDB, 12);
691 #endif
692
693                         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
694                                                                         DataBuffer, NumBytes, RequestSense) != 0)
695                         {
696                                 free(DataBuffer);
697                                 return NULL;
698                         }
699                 }
700                 else
701                 {
702                         free(DataBuffer);
703                         return NULL;
704                 }
705         }
706
707 #ifdef DEBUG_BARCODE
708         /* print a bunch of extra debug data :-(.  */
709         PrintRequestSense(RequestSense); /* see what it sez :-(. */
710         fprintf(stderr,"Data:\n");
711         PrintHex(2, DataBuffer, 40);
712 #endif  
713         return DataBuffer; /* we succeeded! */
714 }
715
716
717 unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
718                                                                                 RequestSense_T *RequestSense,
719                                                                                 Inquiry_T *inquiry_info, 
720                                                                                 SCSI_Flags_T *flags,
721                                                                                 int ElementStart,
722                                                                                 int NumElements,
723                                                                                 int NumBytes
724                                                                                 )
725 {
726         unsigned char *DataBuffer; /* size of data... */
727         int real_numbytes;
728
729         DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
730                                                                                                 RequestSense,
731                                                                                                 inquiry_info,
732                                                                                                 flags,
733                                                                                                 ElementStart,
734                                                                                                 NumElements,
735                                                                                                 NumBytes
736                                                                                                 );
737         /*
738                 One weird loader wants either 8 or BYTE_COUNT_OF_REPORT
739                 values for the ALLOCATION_LENGTH. Give it what it wants
740                 if we get an Sense Key of 05 Illegal Request with a 
741                 CDB position of 7 as the field in error.
742         */
743
744         if (DataBuffer == NULL &&
745                 RequestSense->SenseKey == 5 &&
746                 RequestSense->CommandData &&
747                 RequestSense->BitPointer == 7)
748         {
749                 NumBytes=8; /* send an 8 byte request */
750                 DataBuffer=SendElementStatusRequestActual(      MediumChangerFD,
751                                                                                                         RequestSense,
752                                                                                                         inquiry_info,
753                                                                                                         flags,
754                                                                                                         ElementStart,
755                                                                                                         NumElements,
756                                                                                                         NumBytes
757                                                                                                         );
758         }
759
760         /* the above code falls thru into this: */
761         if (DataBuffer != NULL)
762         {
763                 /* see if we need to retry with a bigger NumBytes: */
764                 real_numbytes = ((int)DataBuffer[5] << 16) +
765                                                 ((int)DataBuffer[6] << 8) +
766                                                  (int)DataBuffer[7] + 8;
767
768                 if (real_numbytes > NumBytes)
769                 {
770                         /* uh-oh, retry! */
771                         free(DataBuffer); /* solve memory leak */
772                         DataBuffer = SendElementStatusRequestActual(MediumChangerFD,
773                                                                                                                 RequestSense,
774                                                                                                                 inquiry_info,
775                                                                                                                 flags,
776                                                                                                                 ElementStart,
777                                                                                                                 NumElements,
778                                                                                                                 real_numbytes
779                                                                                                                 );
780                 }
781         }
782
783         return DataBuffer;
784 }
785
786
787
788 /******************* ParseElementStatus ***********************************/
789 /* This does the actual grunt work of parsing element status data. It fills
790  * in appropriate pieces of its input data. It may be called multiple times
791  * while we are gathering element status.
792  */
793
794 static void ParseElementStatus( int *EmptyStorageElementAddress,
795                                                                 int *EmptyStorageElementCount,
796                                                                 unsigned char *DataBuffer,
797                                                                 ElementStatus_T *ElementStatus,
798                                                                 ElementModeSense_T *mode_sense,
799                                                                 int *pNextElement
800                                                                 )
801 {
802         unsigned char *DataPointer = DataBuffer;
803         TransportElementDescriptor_T TEBuf;
804         TransportElementDescriptor_T *TransportElementDescriptor;
805         ElementStatusDataHeader_T *ElementStatusDataHeader;
806         Element2StatusPage_T *ElementStatusPage;
807         Element2StatusPage_T ESBuf;
808         int ElementCount;
809         int TransportElementDescriptorLength;
810         int BytesAvailable;
811         int ImportExportIndex;
812
813         ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
814         DataPointer += sizeof(ElementStatusDataHeader_T);
815         ElementCount =
816                 BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
817
818 #ifdef DEBUG
819         fprintf(stderr,"ElementCount=%d\n",ElementCount);
820         fflush(stderr);
821 #endif
822
823         while (ElementCount > 0)
824         {
825 #ifdef DEBUG
826                 int got_element_num=0;
827
828                 fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
829                 got_element_num++;
830 #endif
831
832                 memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
833                 ElementStatusPage = &ESBuf;
834                 DataPointer += sizeof(ElementStatusPage_T);
835
836                 TransportElementDescriptorLength =
837                         BigEndian16(ElementStatusPage->ElementDescriptorLength);
838
839                 if (TransportElementDescriptorLength <
840                         sizeof(TransportElementDescriptorShort_T))
841                 {
842                         /* Foo, Storage Element Descriptors can be 4 bytes?! */
843                         if ((ElementStatusPage->ElementTypeCode != MediumTransportElement &&
844                                 ElementStatusPage->ElementTypeCode != StorageElement &&
845                                 ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
846                                 TransportElementDescriptorLength < 4)
847                         {
848 #ifdef DEBUG
849                                 fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
850 #endif
851                                 FatalError("Transport Element Descriptor Length too short: %d\n", TransportElementDescriptorLength);
852                         }
853                 }
854 #ifdef DEBUG
855                 fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
856 #endif
857                 BytesAvailable =
858                         BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
859 #ifdef DEBUG
860                 fprintf(stderr,"%d bytes of descriptor data available in descriptor\n",
861                                 BytesAvailable);
862 #endif
863                 /* work around a bug in ADIC DAT loaders */
864                 if (BytesAvailable <= 0)
865                 {
866                         ElementCount--; /* sorry :-( */
867                 }
868                 while (BytesAvailable > 0)
869                 {
870                         /* TransportElementDescriptor =
871                         (TransportElementDescriptor_T *) DataPointer; */
872                         memcpy(&TEBuf, DataPointer, 
873                                 (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
874                                         TransportElementDescriptorLength  :
875                                         sizeof(TEBuf));
876                         TransportElementDescriptor = &TEBuf;
877
878                         if (pNextElement != NULL)
879                         {
880                                 if (BigEndian16(TransportElementDescriptor->ElementAddress) != 0 || *pNextElement == 0)
881                                 {
882                                         (*pNextElement) = BigEndian16(TransportElementDescriptor->ElementAddress) + 1;
883                                 }
884                                 else
885                                 {
886                                         return;
887                                 }
888                         }
889
890                         DataPointer += TransportElementDescriptorLength;
891                         BytesAvailable -= TransportElementDescriptorLength;
892                         ElementCount--;
893
894                         switch (ElementStatusPage->ElementTypeCode)
895                         {
896                         case MediumTransportElement:
897                                 ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
898 #ifdef DEBUG
899                                 fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress); 
900 #endif
901                                 break;
902
903                         /* we treat ImportExport elements as if they were normal
904                         * storage elements now, sigh...
905                         */
906                         case ImportExportElement:
907 #ifdef DEBUG
908                                 fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
909 #endif
910                                 if (ElementStatus->ImportExportCount >= mode_sense->NumImportExport)
911                                 {
912                                         fprintf(stderr,"Warning:Too Many Import/Export Elements Reported (expected %d, now have %d\n",
913                                         mode_sense->NumImportExport,
914                                         ElementStatus->ImportExportCount + 1);
915                                         fflush(stderr);
916                                         return; /* we're done :-(. */
917                                 }
918
919                                 ImportExportIndex = mode_sense->NumStorage - mode_sense->NumImportExport + ElementStatus->ImportExportCount;
920
921                                 ElementStatus->StorageElementAddress[ImportExportIndex] =
922                                         BigEndian16(TransportElementDescriptor->ElementAddress);
923                                 ElementStatus->StorageElementFull[ImportExportIndex] =
924                                         TransportElementDescriptor->Full;
925
926                                 if ( (TransportElementDescriptorLength > 11) && 
927                                         (ElementStatusPage->VolBits & E2_AVOLTAG))
928                                 {
929                                         copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
930                                                 ElementStatus->AlternateVolumeTag[ImportExportIndex]);
931                                 }
932                                 else
933                                 {
934                                         ElementStatus->AlternateVolumeTag[ImportExportIndex][0] = 0;  /* null string. */;
935                                 } 
936                                 if ((TransportElementDescriptorLength > 11) && 
937                                         (ElementStatusPage->VolBits & E2_PVOLTAG))
938                                 {
939                                         copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
940                                                 ElementStatus->PrimaryVolumeTag[ImportExportIndex]);
941                                 }
942                                 else
943                                 {
944                                         ElementStatus->PrimaryVolumeTag[ImportExportIndex][0]=0; /* null string. */
945                                 }
946
947                                 ElementStatus->StorageElementIsImportExport[ImportExportIndex] = 1;
948
949                                 ElementStatus->ImportExportCount++;
950                                 break;
951
952                         case StorageElement:
953 #ifdef DEBUG
954                                 fprintf(stderr,"StorageElementCount=%d  ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
955 #endif
956                                 /* ATL/Exabyte kludge -- skip slots that aren't installed :-( */
957                                 if (TransportElementDescriptor->AdditionalSenseCode==0x83 && 
958                                         TransportElementDescriptor->AdditionalSenseCodeQualifier==0x02) 
959                                         continue;
960
961                                 ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
962                                         BigEndian16(TransportElementDescriptor->ElementAddress);
963                                 ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
964                                         TransportElementDescriptor->Full;
965 #ifdef DEBUG
966                                 if (TransportElementDescriptor->Except)
967                                         fprintf(stderr,"ASC,ASCQ = 0x%x,0x%x ",TransportElementDescriptor->AdditionalSenseCode,TransportElementDescriptor->AdditionalSenseCodeQualifier);
968                                 fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
969 #endif
970                                 if (!TransportElementDescriptor->Full)
971                                 {
972                                         EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
973                                                 ElementStatus->StorageElementCount; /* slot idx. */
974                                         /*   ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
975                                 }
976                                 if ((TransportElementDescriptorLength >  11) && 
977                                         (ElementStatusPage->VolBits & E2_AVOLTAG))
978                                 {
979                                         copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
980                                                 ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
981                                 }
982                                 else
983                                 {
984                                         ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0;  /* null string. */;
985                                 } 
986                                 if ((TransportElementDescriptorLength > 11) && 
987                                         (ElementStatusPage->VolBits & E2_PVOLTAG))
988                                 {
989                                         copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
990                                                 ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
991                                 }
992                                 else
993                                 {
994                                         ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
995                                 }
996
997                                 ElementStatus->StorageElementCount++;
998                                 /*
999                                         Note that the original mtx had no check here for 
1000                                         buffer overflow, though some drives might mistakingly
1001                                         do one... 
1002                                 */
1003
1004                                 if (ElementStatus->StorageElementCount > mode_sense->NumStorage)
1005                                 {
1006                                         fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
1007                                                         mode_sense->NumStorage,
1008                                                         ElementStatus->StorageElementCount);
1009                                         fflush(stderr);
1010                                         return; /* we're done :-(. */
1011                                 }
1012                                 break;
1013
1014                         case DataTransferElement:
1015                                 /* tape drive not installed, go back to top of loop */
1016
1017                                 /* if (TransportElementDescriptor->Except) continue ; */
1018
1019                                 /* Note: This is for Exabyte tape libraries that improperly
1020                                 report that they have a 2nd tape drive when they don't. We
1021                                 could generalize this in an ideal world, but my attempt to
1022                                 do so failed with dual-drive Exabyte tape libraries that
1023                                 *DID* have the second drive. Sigh. 
1024                                 */
1025                                 if (TransportElementDescriptor->AdditionalSenseCode==0x83 && 
1026                                         TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
1027                                 {
1028                                         continue;
1029                                 }
1030
1031                                 /*      generalize it. Does it work? Let's try it! */
1032                                 /*      
1033                                         No, dammit, following does not work on dual-drive Exabyte
1034                                         'cause if a tape is in the drive, it sets the AdditionalSense
1035                                         code to something (sigh).
1036                                 */
1037                                 /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
1038                                         continue;
1039                                 */ 
1040
1041                                 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
1042                                         BigEndian16(TransportElementDescriptor->ElementAddress);
1043                                 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] = 
1044                                         TransportElementDescriptor->Full;
1045                                 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
1046                                         BigEndian16(TransportElementDescriptor->SourceStorageElementAddress);
1047
1048 #if DEBUG
1049                                 fprintf(stderr, "%d: ElementAddress = %d, Full = %d, SourceElement = %d\n", 
1050                                                 ElementStatus->DataTransferElementCount,
1051                                                 ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount],
1052                                                 ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount],
1053                                                 ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount]);
1054 #endif
1055                                 if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer)
1056                                 {
1057                                         FatalError("Too many Data Transfer Elements Reported\n");
1058                                 }
1059
1060                                 if (ElementStatusPage->VolBits & E2_PVOLTAG)
1061                                 {
1062                                         copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
1063                                         ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
1064                                 }
1065                                 else
1066                                 {
1067                                         ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
1068                                 }
1069
1070                                 if (ElementStatusPage->VolBits & E2_AVOLTAG)
1071                                 {
1072                                         copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
1073                                         ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
1074                                 }
1075                                 else
1076                                 {
1077                                         ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
1078                                 }
1079
1080                                 ElementStatus->DataTransferElementCount++;
1081
1082                                 /* 0 actually is a usable element address */
1083                                 /* if (DataTransferElementAddress == 0) */
1084                                 /*      FatalError( */
1085                                 /*  "illegal Data Transfer Element Address %d reported\n", */
1086                                 /* DataTransferElementAddress); */
1087                                 break;
1088
1089                         default:
1090                                 FatalError("illegal Element Type Code %d reported\n",
1091                                                         ElementStatusPage->ElementTypeCode);
1092                         }
1093                 }
1094         }
1095
1096 #ifdef DEBUG
1097         if (pNextElement)
1098                 fprintf(stderr,"Next start element will be %d\n",*pNextElement);
1099 #endif
1100 }
1101
1102
1103 /********************* Real ReadElementStatus ********************* */
1104
1105 /*
1106  * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
1107  * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
1108  * under Linux, which gets around the @#%@ 4k buffer size in Linux. 
1109  * We still have the restriction that Linux cuts off the last two
1110  * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
1111  * verbose widget won't work :-(. 
1112  
1113  * We now look for that "attached" bit in the inquiry_info to see whether
1114  * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
1115  * look at the device type in the inquiry_info to see whether it is a media
1116  * changer or tape device, and if it's a media changer device, we ignore the
1117  * attached bit (one beta tester found an old 4-tape DAT changer that set
1118  * the attached bit for both the tape device AND the media changer device). 
1119
1120 */
1121
1122 ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1123 {
1124         ElementStatus_T *ElementStatus;
1125
1126         unsigned char *DataBuffer; /* size of data... */
1127
1128         int EmptyStorageElementCount=0;
1129         int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
1130
1131         int empty_idx = 0;
1132         boolean is_attached = false;
1133         int i,j;
1134
1135         ElementModeSense_T *mode_sense = NULL;
1136
1137         if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
1138         {
1139                 is_attached = true;
1140         }
1141
1142         if (flags->no_attached)
1143         {
1144                 /* override, sigh */ 
1145                 is_attached = false;
1146         }
1147
1148         if (!is_attached)
1149         {
1150                 mode_sense = ReadAssignmentPage(MediumChangerFD);
1151         }
1152
1153         if (!mode_sense)
1154         {
1155                 mode_sense = (ElementModeSense_T *)xmalloc(sizeof(ElementModeSense_T));
1156                 mode_sense->NumMediumTransport = MAX_TRANSPORT_ELEMENTS;
1157                 mode_sense->NumStorage = MAX_STORAGE_ELEMENTS;
1158                 mode_sense->NumDataTransfer = MAX_TRANSFER_ELEMENTS;
1159                 mode_sense->MaxReadElementStatusData =
1160                         (sizeof(ElementStatusDataHeader_T) + 3 * sizeof(ElementStatusPage_T) +
1161                         (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS) *
1162                                 sizeof(TransportElementDescriptor_T));
1163
1164                 /* don't care about the others anyhow at the moment... */
1165         }
1166
1167         ElementStatus = AllocateElementData(mode_sense);
1168
1169         /* Now to initialize it (sigh).  */
1170         ElementStatus->StorageElementCount = 0;
1171         ElementStatus->DataTransferElementCount = 0;
1172
1173         /* first, allocate some empty storage stuff: Note that we pass this
1174         * down to ParseElementStatus (sigh!) 
1175         */
1176
1177         EmptyStorageElementAddress = (int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
1178         for (i = 0; i < mode_sense->NumStorage; i++)
1179         {
1180                 EmptyStorageElementAddress[i] = -1;
1181         }
1182
1183         /* Okay, now to send some requests for the various types of stuff: */
1184
1185         /* -----------STORAGE ELEMENTS---------------- */
1186         /* Let's start with storage elements: */
1187
1188         for (i = 0; i < mode_sense->NumDataTransfer; i++)
1189         {
1190                 /* initialize them to an illegal # so that we can fix later... */
1191                 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1; 
1192         }
1193
1194         if (flags->querytype == MTX_ELEMENTSTATUS_ORIGINAL)
1195         {
1196 #ifdef DEBUG
1197                 fprintf(stderr,"Using original element status polling method (storage, import/export, drivers etc independantly)\n");
1198 #endif
1199                 flags->elementtype = StorageElement; /* sigh! */
1200                 DataBuffer = SendElementStatusRequest(  MediumChangerFD, RequestSense,
1201                                                                                                 inquiry_info, flags,
1202                                                                                                 mode_sense->StorageStart,
1203                                                                                                 /* adjust for import/export. */
1204                                                                                                 mode_sense->NumStorage - mode_sense->NumImportExport,
1205                                                                                                 mode_sense->MaxReadElementStatusData);
1206
1207                 if (!DataBuffer)
1208                 {
1209 #ifdef DEBUG
1210                         fprintf(stderr,"Had no elements!\n");
1211 #endif
1212                         /* darn. Free up stuff and return. */
1213 #ifdef DEBUG_MODE_SENSE
1214                         PrintRequestSense(RequestSense);
1215 #endif
1216                         FreeElementData(ElementStatus);
1217                         return NULL; 
1218                 }
1219
1220 #ifdef DEBUG
1221                 fprintf(stderr, "Parsing storage elements\n");
1222 #endif
1223                 ParseElementStatus(EmptyStorageElementAddress, &EmptyStorageElementCount,
1224                         DataBuffer,ElementStatus,mode_sense,NULL);
1225
1226                 free(DataBuffer); /* sigh! */
1227
1228                 /* --------------IMPORT/EXPORT--------------- */
1229                 /* Next let's see if we need to do Import/Export: */
1230                 if (mode_sense->NumImportExport > 0)
1231                 {
1232 #ifdef DEBUG
1233                         fprintf(stderr,"Sending request for Import/Export status\n");
1234 #endif
1235                         flags->elementtype = ImportExportElement;
1236                         DataBuffer = SendElementStatusRequest(  MediumChangerFD,RequestSense,
1237                                                                                                         inquiry_info, flags,
1238                                                                                                         mode_sense->ImportExportStart,
1239                                                                                                         mode_sense->NumImportExport,
1240                                                                                                         mode_sense->MaxReadElementStatusData);
1241
1242                         if (!DataBuffer)
1243                         {
1244 #ifdef DEBUG
1245                                 fprintf(stderr,"Had no input/export element!\n");
1246 #endif
1247                                 /* darn. Free up stuff and return. */
1248 #ifdef DEBUG_MODE_SENSE
1249                                 PrintRequestSense(RequestSense);
1250 #endif
1251                                 FreeElementData(ElementStatus);
1252                                 return NULL;
1253                         }
1254 #ifdef DEBUG
1255                         fprintf(stderr,"Parsing inport/export element status\n");
1256 #endif
1257 #ifdef DEBUG_ADIC
1258                         dump_data(DataBuffer, 100);             /* dump some data :-(. */
1259 #endif
1260                         ParseElementStatus(     EmptyStorageElementAddress, &EmptyStorageElementCount,
1261                                                                 DataBuffer, ElementStatus, mode_sense, NULL);
1262
1263                         ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
1264                 }
1265
1266                 /* ----------------- DRIVES ---------------------- */
1267
1268 #ifdef DEBUG
1269                 fprintf(stderr,"Sending request for data transfer element (drive) status\n");
1270 #endif
1271                 flags->elementtype = DataTransferElement; /* sigh! */
1272                 DataBuffer = SendElementStatusRequest(  MediumChangerFD, RequestSense,
1273                                                                                                 inquiry_info, flags,
1274                                                                                                 mode_sense->DataTransferStart,
1275                                                                                                 mode_sense->NumDataTransfer,
1276                                                                                                 mode_sense->MaxReadElementStatusData);
1277                 if (!DataBuffer)
1278                 {
1279 #ifdef DEBUG
1280                         fprintf(stderr,"No data transfer element status.");
1281 #endif
1282                         /* darn. Free up stuff and return. */
1283 #ifdef DEBUG_MODE_SENSE
1284                         PrintRequestSense(RequestSense);
1285 #endif
1286                         FreeElementData(ElementStatus);
1287                         return NULL; 
1288                 }
1289
1290 #ifdef DEBUG
1291                 fprintf(stderr,"Parsing data for data transfer element (drive) status\n");
1292 #endif
1293                 ParseElementStatus(     EmptyStorageElementAddress, &EmptyStorageElementCount,
1294                                                         DataBuffer,ElementStatus, mode_sense, NULL);
1295
1296                 free(DataBuffer); /* sigh! */
1297
1298                 /* ----------------- Robot Arm(s) -------------------------- */
1299
1300                 /* grr, damned brain dead HP doesn't report that it has any! */
1301                 if (!mode_sense->NumMediumTransport)
1302                 { 
1303                         ElementStatus->TransportElementAddress = 0; /* default it sensibly :-(. */
1304                 }
1305                 else
1306                 {
1307 #ifdef DEBUG
1308                         fprintf(stderr,"Sending request for robot arm status\n");
1309 #endif
1310                         flags->elementtype = MediumTransportElement; /* sigh! */
1311                         DataBuffer = SendElementStatusRequest(  MediumChangerFD, RequestSense,
1312                                                                                                         inquiry_info, flags,
1313                                                                                                         mode_sense->MediumTransportStart,
1314                                                                                                         1, /* only get 1, sigh. */
1315                                                                                                         mode_sense->MaxReadElementStatusData);
1316                         if (!DataBuffer)
1317                         {
1318 #ifdef DEBUG
1319                                 fprintf(stderr,"Loader reports no robot arm!\n");
1320 #endif
1321                                 /* darn. Free up stuff and return. */
1322 #ifdef DEBUG_MODE_SENSE
1323                                 PrintRequestSense(RequestSense);
1324 #endif
1325                                 FreeElementData(ElementStatus);
1326                                 return NULL; 
1327                         } 
1328 #ifdef DEBUG
1329                         fprintf(stderr,"Parsing robot arm data\n");
1330 #endif
1331                         ParseElementStatus(     EmptyStorageElementAddress, &EmptyStorageElementCount,
1332                                                                 DataBuffer, ElementStatus, mode_sense, NULL);
1333
1334                         free(DataBuffer);
1335                 }
1336         }
1337         else
1338         {
1339                 int nLastEl=-1, nNextEl=0;
1340
1341 #ifdef DEBUG
1342                 fprintf(stderr,"Using alternative element status polling method (all elements)\n");
1343 #endif
1344                 /* ----------------- ALL Elements ---------------------- */
1345                 /*      Just keep asking for elements till no more are returned 
1346                         - increment our starting address as we go acording to the 
1347                         number of elements returned from the last call
1348                 */
1349
1350                 while(nLastEl!=nNextEl)
1351                 {
1352                         flags->elementtype = AllElementTypes;//StorageElement; /* sigh! */ /*XL1B2 firewire changer does not seem to respond to specific types so just use all elements*/
1353                         DataBuffer = SendElementStatusRequest(  MediumChangerFD,
1354                                                                                                         RequestSense,
1355                                                                                                         inquiry_info,
1356                                                                                                         flags,
1357                                                                                                         nNextEl,//mode_sense->StorageStart,
1358                                                                                                         /* adjust for import/export. */
1359                                                                                                         mode_sense->NumStorage - mode_sense->NumImportExport,//FIX ME:this should be a more sensible value
1360                                                                                                         mode_sense->MaxReadElementStatusData);
1361                         if (!DataBuffer)
1362                         {
1363                                 if (RequestSense->AdditionalSenseCode == 0x21 && 
1364                                 RequestSense->AdditionalSenseCodeQualifier == 0x01)
1365                                 {
1366                                         /* Error is invalid element address, we've probably just hit the end */
1367                                         break;
1368                                 }
1369
1370                                 /* darn. Free up stuff and return. */
1371                                 FreeElementData(ElementStatus);
1372                                 return NULL; 
1373                         } 
1374
1375                         nLastEl = nNextEl;
1376
1377                         ParseElementStatus(     EmptyStorageElementAddress, &EmptyStorageElementCount,
1378                                                                 DataBuffer, ElementStatus, mode_sense, &nNextEl);
1379
1380                         free(DataBuffer); /* sigh! */
1381                 }
1382
1383                 ElementStatus->StorageElementCount += ElementStatus->ImportExportCount;
1384         }
1385
1386         /*---------------------- Sanity Checking ------------------- */
1387
1388         if (ElementStatus->DataTransferElementCount == 0)
1389                 FatalError("no Data Transfer Element reported\n");
1390
1391         if (ElementStatus->StorageElementCount == 0)
1392                 FatalError("no Storage Elements reported\n");
1393
1394
1395         /* ---------------------- Reset SourceStorageElementNumbers ------- */
1396
1397         /*
1398          * Re-write the SourceStorageElementNumber code  *AGAIN*.
1399          *
1400          * Pass1:
1401          *      Translate from raw element # to our translated # (if possible).
1402          *      First, check the SourceStorageElementNumbers against the list of 
1403          *      filled slots. If the slots indicated are empty, we accept that list as
1404          *      valid. Otherwise decide the SourceStorageElementNumbers are invalid.
1405          *
1406          * Pass2:
1407          *      If we had some invalid (or unknown) SourceStorageElementNumbers
1408          *      then we must search for free slots, and assign SourceStorageElementNumbers
1409          *      to those free slots. We happen to already built a list of free
1410          *      slots as part of the process of reading the storage element numbers
1411          *      from the tape. So that's easy enough to do! 
1412          */
1413
1414 #ifdef DEBUG_TAPELIST
1415         fprintf(stderr, "empty slots: %d\n", EmptyStorageElementCount);
1416         if (EmptyStorageElementCount)
1417         {
1418                 for (i = 0; i < EmptyStorageElementCount; i++)
1419                 {
1420                         fprintf(stderr, "empty: %d\n", EmptyStorageElementAddress[i]);
1421                 }
1422         }
1423 #endif
1424
1425         /*
1426          *      Now we re-assign origin slots if the "real" origin slot
1427          *      is obviously defective: 
1428          */
1429         /* pass one: */
1430         for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
1431         {
1432                 int elnum;
1433
1434                 /* if we have an element, then ... */
1435                 if (ElementStatus->DataTransferElementFull[i])
1436                 {
1437                         elnum = ElementStatus->DataTransferElementSourceStorageElementNumber[i];
1438                         /* if we have an element number, then ... */
1439                         if (elnum >= 0)
1440                         {
1441                                 /* Now to translate the elnum: */
1442                                 ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
1443                                 for (j = 0; j < ElementStatus->StorageElementCount; j++)
1444                                 {
1445                                         if (elnum == ElementStatus->StorageElementAddress[j])
1446                                         {
1447                                                 /* now see if the element # is already occupied:*/
1448                                                 if (!ElementStatus->StorageElementFull[j])
1449                                                 {
1450                                                         /* properly set the source... */
1451                                                         ElementStatus->DataTransferElementSourceStorageElementNumber[i] = j;
1452                                                 }
1453                                         }
1454                                 }
1455                         }
1456                 }
1457         }
1458
1459         /* Pass2: */
1460         /*      We have invalid sources, so let's see what they should be: */
1461         /*      Note: If EmptyStorageElementCount is < # of drives, the leftover
1462          *      drives will be assigned a -1 (see the initialization loop for
1463          *      EmptyStorageElementAddress above), which will be reported as "slot 0"
1464          *      by the user interface. This is an invalid value, but more useful for us
1465          *      to have than just crapping out here :-(. 
1466         */
1467         empty_idx=0;
1468         for (i = 0; i < ElementStatus->DataTransferElementCount; i++)
1469         {
1470                 if (ElementStatus->DataTransferElementFull[i] && 
1471                         ElementStatus->DataTransferElementSourceStorageElementNumber[i] < 0)
1472                 {
1473 #ifdef DEBUG_TAPELIST
1474                         fprintf(stderr,"for drive %d, changing to %d (empty slot #%d)\n",
1475                                         i,
1476                                         EmptyStorageElementAddress[empty_idx],
1477                                         empty_idx);
1478 #endif
1479                         ElementStatus->DataTransferElementSourceStorageElementNumber[i] =
1480                                 EmptyStorageElementAddress[empty_idx++];
1481                 }
1482         }
1483
1484         /* and done! */
1485         free(mode_sense);
1486         free(EmptyStorageElementAddress);
1487         return ElementStatus;
1488 }
1489
1490 /*************************************************************************/
1491
1492 RequestSense_T *PositionElement(DEVICE_TYPE MediumChangerFD,
1493                 int DestinationAddress,
1494                 ElementStatus_T *ElementStatus)
1495 {
1496         RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1497         CDB_T CDB;
1498
1499         CDB[0] = 0x2b;
1500         CDB[1] = 0;
1501         CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1502         CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1503         CDB[4] = (unsigned char)(DestinationAddress >> 8);
1504         CDB[5] = (unsigned char)DestinationAddress;
1505         CDB[6] = 0;
1506         CDB[7] = 0;
1507         CDB[8] = 0;
1508         CDB[9] = 0;
1509
1510         if(SCSI_ExecuteCommand( MediumChangerFD, Output, &CDB, 10,
1511                                                         NULL, 0, RequestSense) != 0)
1512         {
1513                 return RequestSense;
1514         }
1515         free(RequestSense);
1516         return NULL; /* success */
1517 }
1518
1519
1520 /* Now the actual media movement routine! */
1521 RequestSense_T *MoveMedium(     DEVICE_TYPE MediumChangerFD, int SourceAddress,
1522                                                         int DestinationAddress, 
1523                                                         ElementStatus_T *ElementStatus,
1524                                                         Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
1525 {
1526         RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1527         CDB_T CDB;
1528
1529         if (inquiry_info->MChngr && inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)
1530         {
1531                 /* if using the ATTACHED API */
1532                 CDB[0] = 0xA7;          /* MOVE_MEDIUM_ATTACHED */
1533         }
1534         else
1535         {
1536                 CDB[0] = 0xA5;          /* MOVE MEDIUM */
1537         }
1538
1539         CDB[1] = 0;                     /* Reserved */
1540
1541         /* Transport Element Address */
1542         CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1543         CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1544
1545         /* Source Address */
1546         CDB[4] = (unsigned char)(SourceAddress >> 8);
1547         CDB[5] = (unsigned char)SourceAddress;
1548
1549         /* Destination Address */
1550         CDB[6] = (unsigned char)(DestinationAddress >> 8);
1551         CDB[7] = (unsigned char)DestinationAddress;
1552
1553         CDB[8] = 0;                     /* Reserved */
1554         CDB[9] = 0;                     /* Reserved */
1555
1556         if (flags->invert)
1557         {
1558                 CDB[10] = 1;                    /* Reserved */
1559         }
1560         else
1561         {
1562                 CDB[10] = 0;
1563         }
1564         /* eepos controls the tray for import/export elements, sometimes. */
1565         CDB[11] = flags->eepos << 6;                    /* Control */
1566
1567         if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1568                                                         NULL, 0, RequestSense) != 0)
1569         {
1570 #ifdef DEBUG
1571                 fprintf(stderr, "Move Medium (0x%02X) failed\n", CDB[0]);
1572 #endif
1573                 return RequestSense;
1574         }
1575
1576         free(RequestSense);
1577         return NULL; /* success! */
1578 }
1579
1580
1581 /* Now the actual Exchange Medium routine! */
1582 RequestSense_T *ExchangeMedium( DEVICE_TYPE MediumChangerFD, int SourceAddress,
1583                                                                 int DestinationAddress, int Dest2Address,
1584                                                                 ElementStatus_T *ElementStatus,
1585                                                                 SCSI_Flags_T *flags)
1586 {
1587         RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1588         CDB_T CDB;
1589
1590         CDB[0] = 0xA6;          /* EXCHANGE MEDIUM */
1591         CDB[1] = 0;                     /* Reserved */
1592
1593         /* Transport Element Address */
1594         CDB[2] = (unsigned char)(ElementStatus->TransportElementAddress >> 8);
1595         CDB[3] = (unsigned char)ElementStatus->TransportElementAddress;
1596
1597         /* Source Address */
1598         CDB[4] = (unsigned char)(SourceAddress >> 8);
1599         CDB[5] = (unsigned char)SourceAddress;
1600
1601         /* Destination Address */
1602         CDB[6] = (unsigned char)(DestinationAddress >> 8);
1603         CDB[7] = (unsigned char)DestinationAddress;
1604
1605         /* move destination back to source? */
1606         CDB[8] = (unsigned char)(Dest2Address >> 8);
1607         CDB[9] = (unsigned char)Dest2Address;
1608         CDB[10] = 0;
1609
1610         if (flags->invert)
1611         {
1612                 CDB[10] |= 2;                   /* INV2 */
1613         }
1614
1615         if (flags->invert2)
1616         {
1617                 CDB[1] |= 1;                    /* INV1 */
1618         }
1619
1620         /* eepos controls the tray for import/export elements, sometimes. */
1621         CDB[11] = flags->eepos << 6;                    /* Control */
1622
1623 #ifdef DEBUG_EXCHANGE
1624         dump_cdb(&CDB,12);
1625 #endif  
1626
1627         if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
1628                                                         NULL, 0, RequestSense) != 0)
1629         {
1630                 return RequestSense;
1631         }
1632         free(RequestSense);
1633         return NULL; /* success! */
1634 }
1635
1636
1637 /*
1638  * for Linux, this creates a way to do a short erase... the @#$%@ st.c
1639  * driver defaults to doing a long erase!
1640  */
1641
1642 RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD)
1643 {
1644         RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
1645         CDB_T CDB;
1646
1647         CDB[0] = 0x19;
1648         CDB[1] = 0;  /* Short! */
1649         CDB[2] = CDB[3] = CDB[4] = CDB[5] = 0;
1650
1651         if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
1652                                                         NULL, 0, RequestSense) != 0)
1653         {
1654 #ifdef DEBUG
1655                 fprintf(stderr, "Erase (0x19) failed\n");
1656 #endif
1657                 return RequestSense;
1658         }
1659
1660         free(RequestSense);
1661         return NULL;            /* Success! */
1662 }
1663
1664 /* Routine to send an LOAD/UNLOAD from the MMC/SSC spec to a device. 
1665  * For tapes and changers this can be used either to eject a tape 
1666  * or to eject a magazine (on some Seagate changers, when sent to LUN 1 ).
1667  * For CD/DVDs this is used to Load or Unload a disc which is required by
1668  * some media changers.
1669  */
1670
1671 int LoadUnload(DEVICE_TYPE fd, int bLoad)
1672 {
1673         CDB_T CDB;
1674         /* okay, now for the command: */
1675
1676         CDB[0] = 0x1B;
1677         CDB[4] = bLoad ? 3 : 2;
1678         CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1679
1680         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
1681         {
1682 #ifdef DEBUG_MODE_SENSE
1683                 PrintRequestSense(&scsi_error_sense);
1684                 fprintf(stderr, "Eject (0x1B) failed\n");
1685 #endif
1686                 return -1;  /* could not do! */
1687         }
1688         return 0; /* did do! */
1689 }
1690
1691 /* Routine to send an START/STOP from the MMC/SSC spec to a device. 
1692  * For tape drives this may be required prior to using the changer 
1693  * Load or Unload commands.
1694  * For CD/DVD drives this is used to Load or Unload a disc which may be
1695  * required by some media changers.
1696  */
1697
1698 int StartStop(DEVICE_TYPE fd, int bStart)
1699 {
1700         CDB_T CDB;
1701         /* okay, now for the command: */
1702
1703         CDB[0] = 0x1B;
1704         CDB[4] = bStart ? 1 : 0;
1705         CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1706
1707         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,NULL, 0, &scsi_error_sense) != 0)
1708         {
1709 #ifdef DEBUG_MODE_SENSE
1710                 PrintRequestSense(&scsi_error_sense);
1711                 fprintf(stderr, "Eject (0x1B) failed\n");
1712 #endif
1713                 return -1;  /* could not do! */
1714         }
1715         return 0; /* did do! */
1716 }
1717
1718 /* Routine to send a LOCK/UNLOCK from the SSC/MMC spec to a device. 
1719  * This can be used to prevent or allow the Tape or CD/DVD from being
1720  * removed. 
1721  */
1722
1723 int LockUnlock(DEVICE_TYPE fd, int bLock)
1724 {
1725         CDB_T CDB;
1726         /* okay, now for the command: */
1727
1728         CDB[0] = 0x1E;
1729         CDB[1] = CDB[2] = CDB[3] = CDB[5] = 0;
1730         CDB[4] = (char)bLock;
1731
1732         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, NULL, 0, &scsi_error_sense) != 0)
1733         {
1734 #ifdef DEBUG_MODE_SENSE
1735                 PrintRequestSense(&scsi_error_sense);
1736                 fprintf(stderr, "Eject (0x1B) failed\n");
1737 #endif
1738                 return -1;  /* could not do! */
1739         }
1740         return 0; /* did do! */
1741 }
1742
1743 static char Spaces[] = "                                                            ";
1744
1745 void PrintHex(int Indent, unsigned char *Buffer, int Length)
1746 {
1747         int             idxBuffer;
1748         int             idxAscii;
1749         int             PadLength;
1750         char    cAscii;
1751
1752         for (idxBuffer = 0; idxBuffer < Length; idxBuffer++)
1753         {
1754                 if ((idxBuffer % 16) == 0)
1755                 {
1756                         if (idxBuffer > 0)
1757                         {
1758                                 fputc('\'', stderr);
1759
1760                                 for (idxAscii = idxBuffer - 16; idxAscii < idxBuffer; idxAscii++)
1761                                 {
1762                                         cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x7F ? Buffer[idxAscii] : '.';
1763                                         fputc(cAscii, stderr);
1764                                 }
1765                                 fputs("'\n", stderr);
1766                         }
1767                         fprintf(stderr, "%.*s%04X: ", Indent, Spaces, idxBuffer);
1768                 }
1769                 fprintf(stderr, "%02X ", (unsigned char)Buffer[idxBuffer]);
1770         }
1771
1772         PadLength = 16 - (Length % 16);
1773
1774         if (PadLength > 0)
1775         {
1776                 fprintf(stderr, "%.*s'", 3 * PadLength, Spaces);
1777
1778                 for (idxAscii = idxBuffer - (16 - PadLength); idxAscii < idxBuffer; idxAscii++)
1779                 {
1780                         cAscii = Buffer[idxAscii] >= 0x20 && Buffer[idxAscii] < 0x80 ? Buffer[idxAscii] : '.';
1781                         fputc(cAscii, stderr);
1782                 }
1783                 fputs("'\n", stderr);
1784         }
1785
1786         fflush(stderr);
1787 }
1788
1789 static char *sense_keys[] =
1790 {
1791         "No Sense",                     /* 00 */
1792         "Recovered Error",      /* 01 */
1793         "Not Ready",            /* 02 */
1794         "Medium Error",         /* 03 */
1795         "Hardware Error",       /* 04 */
1796         "Illegal Request",      /* 05 */
1797         "Unit Attention",       /* 06 */
1798         "Data Protect",         /* 07 */
1799         "Blank Check",          /* 08 */
1800         "0x09",                         /* 09 */
1801         "0x0a",                         /* 0a */
1802         "Aborted Command",      /* 0b */
1803         "0x0c",                         /* 0c */
1804         "Volume Overflow",      /* 0d */
1805         "Miscompare",           /* 0e */
1806         "0x0f"                          /* 0f */
1807 };
1808
1809 static char Yes[] = "yes";
1810 static char No[] = "no";
1811
1812 void PrintRequestSense(RequestSense_T *RequestSense)
1813 {
1814         char *msg;
1815
1816         fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
1817         fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
1818
1819         if (RequestSense->ErrorCode == 0x70)
1820         { 
1821                 msg = "Current" ;
1822         }
1823         else if (RequestSense->ErrorCode == 0x71)
1824         {
1825                 msg = "Deferred" ;
1826         }
1827         else
1828         {
1829                 msg = "Unknown?!" ;
1830         }
1831
1832         fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n", RequestSense->ErrorCode, msg);
1833         fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n", sense_keys[RequestSense->SenseKey]);
1834         fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
1835         fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
1836         fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
1837
1838         if (RequestSense->Valid)
1839         {
1840                 fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
1841         }
1842
1843         fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
1844         fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
1845
1846         if (RequestSense->SKSV)
1847         {
1848                 fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n", RequestSense->BitPointer);
1849         }
1850
1851         fprintf(stderr, "mtx: Request Sense: BPV=%s\n", RequestSense->BPV ? Yes : No);
1852         fprintf(stderr, "mtx: Request Sense: Error in CDB=%s\n", RequestSense->CommandData ? Yes : No);
1853         fprintf(stderr, "mtx: Request Sense: SKSV=%s\n", RequestSense->SKSV ? Yes : No);
1854
1855         if (RequestSense->BPV || RequestSense -> SKSV)
1856         {
1857                 fprintf(stderr, "mtx: Request Sense: Field Pointer = %02X %02X\n",
1858                                 RequestSense->FieldData[0], RequestSense->FieldData[1]);
1859         }
1860
1861         fflush(stderr);
1862 }