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