Imported Upstream version 1.3.11
[debian/mtx] / tapeinfo.c
1 /* Copyright 2000 Enhanced Software Technologies Inc.
2  *   Released under terms of the GNU General Public License as
3  * required by the license on 'mtxl.c'.
4  * $Date: 2007-03-24 18:14:01 -0700 (Sat, 24 Mar 2007) $
5  * $Revision: 166 $
6  */
7
8 /*#define DEBUG_PARTITION */
9 /*#define DEBUG 1 */
10
11 /* What this does: This basically dumps out the contents of the following
12  * pages:
13  *
14  * Inquiry -- prints full inquiry info. If it's not a tape drive, this is
15  * the end of things.
16  *    DeviceType:
17  *    Manufacturer:
18  *    ProdID:  
19  *    ProdRevision:
20  *
21  * Log Sense: TapeAlert Page (if supported):
22  *    TapeAlert:[message#]<Message>  e.g. "TapeAlert:[22]Cleaning Cartridge Worn Out"
23  *  
24  * Mode Sense: 
25  *  Data Compression Page:
26  *    DataCompEnabled:<yes|no>
27  *    DataCompCapable:<yes|no>
28  *    DataDeCompEnabled:<yes|no>
29  *    CompType:<number>
30  *    DeCompType:<number>
31  *
32  *  Device Configuration Page:
33  *    ActivePartition:<#>
34  *    DevConfigComp:<#>    -- the compression byte in device config page.
35  *    EarlyWarningSize:<#> -- size of early warning buffer?
36  *
37  *  Medium Partition Page:
38  *    NumPartitions:<#>
39  *    MaxPartitions:<#>
40  *    Partition[0]:<size>
41  *    Partition[1]:<size>...
42  *
43  * Read Block Limits command:
44  *    MinBlock:<#>  -- Minimum block size.
45  *    MaxBlock:<#>  -- Maximum block size. 
46  */
47
48 #include <stdio.h>
49 #include <string.h>
50 #include "mtx.h"
51 #include "mtxl.h"
52
53 char    *argv0;
54
55 void usage(void)
56 {
57         FatalError("Usage: tapeinfo -f <generic-device>\n");
58 }
59
60 /* A table for printing out the peripheral device type as ASCII. */ 
61 static char *PeripheralDeviceType[32] =
62 {
63         "Disk Drive",
64         "Tape Drive",
65         "Printer",
66         "Processor",
67         "Write-once",
68         "CD-ROM",
69         "Scanner",
70         "Optical",
71         "Medium Changer",
72         "Communications",
73         "ASC IT8",
74         "ASC IT8",
75         "RAID Array",
76         "Enclosure Services",
77         "OCR/W",
78         "Bridging Expander",    /* 0x10 */
79         "Reserved",                             /* 0x11 */
80         "Reserved",                             /* 0x12 */
81         "Reserved",                             /* 0x13 */
82         "Reserved",                             /* 0x14 */
83         "Reserved",                             /* 0x15 */
84         "Reserved",                             /* 0x16 */
85         "Reserved",                             /* 0x17 */
86         "Reserved",                             /* 0x18 */
87         "Reserved",                             /* 0x19 */
88         "Reserved",                             /* 0x1a */
89         "Reserved",                             /* 0x1b */
90         "Reserved",                             /* 0x1c */
91         "Reserved",                             /* 0x1d */
92         "Reserved",                             /* 0x1e */
93         "Unknown"                               /* 0x1f */
94 };
95
96
97
98 /* we call it MediumChangerFD for history reasons, sigh. */
99
100 /* now to print inquiry information: Copied from other one.... */
101
102 static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
103 {
104         RequestSense_T RequestSense;
105         Inquiry_T *Inquiry;
106         int i;
107
108         Inquiry = RequestInquiry(MediumChangerFD, &RequestSense);
109         if (Inquiry == NULL)
110         {
111                 PrintRequestSense(&RequestSense);
112                 FatalError("INQUIRY Command Failed\n");
113         }
114
115         printf("Product Type: %s\n", PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
116
117         printf("Vendor ID: '");
118         for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
119                 printf("%c", Inquiry->VendorIdentification[i]);
120
121         printf("'\nProduct ID: '");
122         for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
123                 printf("%c", Inquiry->ProductIdentification[i]);
124
125         printf("'\nRevision: '");
126         for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
127                 printf("%c", Inquiry->ProductRevisionLevel[i]);
128         printf("'\n");
129
130         if (Inquiry->MChngr)
131         {
132                 /* check the attached-media-changer bit... */
133                 printf("Attached Changer API: Yes\n");
134         }
135         else
136         {
137                 printf("Attached Changer API: No\n");
138         }
139
140         free(Inquiry);          /* well, we're about to exit, but ... */
141 }
142
143
144
145
146 /* Okay, now for the Log Sense Tape Alert Page (if supported): */
147 #define TAPEALERT_SIZE 2048  /* max size of tapealert buffer. */ 
148 #define MAX_TAPE_ALERT 0x41
149
150 static char *tapealert_messages[] =
151 {
152         "Undefined", /* 0 */
153         "         Read: Having problems reading (slowing down)", /* 1 */
154         "        Write: Having problems writing (losing capacity)", /* 2 */
155         "   Hard Error: Uncorrectable read/write error", /* 3 */
156         "        Media: Media Performance Degraded, Data Is At Risk", /* 4 */
157         " Read Failure: Tape faulty or tape drive broken", /* 5 */
158         "Write Failure: Tape faulty or tape drive broken", /* 6 */
159         "   Media Life: The tape has reached the end of its useful life", /* 7 */
160         "Not Data Grade:Replace cartridge with one  containing data grade tape",/*8*/
161         "Write Protect: Attempted to write to a write-protected cartridge",/*9 */
162         "   No Removal: Cannot unload, initiator is preventing media removal", /*a*/
163         "Cleaning Media:Cannot back up or restore to a cleaning cartridge", /* b */
164         "   Bad Format: The loaded tape contains data in an unsupported format", /*c */
165         " Snapped Tape: The data cartridge contains a broken tape", /* d */
166         "Undefined", /* e */
167         "Undefined", /* f */
168         "Undefined", /* 10 */
169         "Undefined", /* 11 */
170         "Undefined", /* 12 */
171         "Undefined", /* 13 */
172         "    Clean Now: The tape drive neads cleaning NOW", /* 0x14 */
173         "Clean Periodic:The tape drive needs to be cleaned at next opportunity", /* 0x15 */
174         "Cleaning Media:Cannot clean because cleaning cartridge used up, insert new cleaning cartridge to clean the drive", /* 0x16 */
175         "Undefined", /* 0x17 */
176         "Undefined", /* 0x18 */
177         "Undefined", /* 0x19 */
178         "Undefined", /* 0x1a */
179         "Undefined", /* 0x1b */
180         "Undefined", /* 0x1c */
181         "Undefined", /* 0x1d */
182         "   Hardware A: Tape drive has a problem not read/write related", /* 0x1e */
183         "   Hardware B: Tape drive has a problem not read/write related", /* 0x1f */
184         "    Interface: Problem with SCSI interface between tape drive and initiator", /* 0x20 */
185         "  Eject Media: The current operation has failed. Eject and reload media", /* 0x21 */
186         "Download Fail: Attempt to download new firmware failed", /* 0x22 */
187         "Undefined", /* 0x23 */
188         "Undefined", /* 0x24 */
189         "Undefined", /* 0x25 */
190         "Undefined", /* 0x26 */
191         "Undefined", /* 0x27 */
192         "Loader Hardware A: Changer having problems communicating with tape drive", /* 0x28   40 */
193         "Loader Stray Tape: Stray tape left in drive from prior error", /* 0x29 41 */
194         "Loader Hardware B: Autoloader mechanism has a fault", /* 0x2a 42 */
195         "  Loader Door: Loader door is open, please close it", /* 0x2b 43 */
196         "Undefined", /* 0x2c */
197         "Undefined", /* 0x2d */
198         "Undefined", /* 0x2e */
199         "Undefined", /* 0x2f */
200         "Undefined", /* 0x30 */
201         "Undefined", /* 0x31 */
202         "Undefined", /* 0x32 */
203         "Undefined", /* 0x33 */
204         "Undefined", /* 0x34 */
205         "Undefined", /* 0x35 */
206         "Undefined", /* 0x36 */
207         "Undefined", /* 0x37 */
208         "Undefined", /* 0x38 */
209         "Undefined", /* 0x39 */
210         "Undefined", /* 0x3a */
211         "Undefined", /* 0x3b */
212         "Undefined", /* 0x3c */
213         "Undefined", /* 0x3d */
214         "Undefined", /* 0x3e */
215         "Undefined", /* 0x3f */
216         "Undefined" /* 0x40 */
217 };
218
219 typedef struct TapeCapacityStruct
220 {
221         unsigned int partition0_remaining;
222         unsigned int partition1_remaining;
223         unsigned int partition0_size;
224         unsigned int partition1_size;
225 }       TapeCapacity;
226
227 #if defined(DEBUG)
228 /* DEBUG */
229 static void dump_data(unsigned char *data, int len)
230 {
231         if (len != 0)
232         {
233                 fprintf(stderr,"DATA:");
234                 PrintHex(1, data, len);
235         }
236         else
237         {
238                 fprintf(stderr, "**NO DATA**\n");
239         }
240 }
241 #endif
242
243
244 /* Request the tape capacity page defined by some DAT autoloaders. */
245
246 static TapeCapacity *RequestTapeCapacity(DEVICE_TYPE fd, RequestSense_T *sense)
247 {
248         CDB_T CDB;
249         TapeCapacity *result;
250         int result_len;
251
252         unsigned char buffer[TAPEALERT_SIZE]; /* Overkill, but ... */
253
254         slow_bzero((char *)buffer,TAPEALERT_SIZE); /*zero it... */
255
256         /* now to create the CDB block: */
257         CDB[0] = 0x4d;   /* Log Sense */
258         CDB[1] = 0;   
259         CDB[2] = 0x31;   /* Tape Capacity Page. */
260         CDB[3] = 0;
261         CDB[4] = 0;
262         CDB[5] = 0;
263         CDB[6] = 0;
264         CDB[7] = TAPEALERT_SIZE >> 8 & 0xff;    /* hi byte, allocation size */
265         CDB[8] = TAPEALERT_SIZE & 0xff;                 /* lo byte, allocation size */
266         CDB[9] = 0;                                                             /* reserved */ 
267
268         if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, TAPEALERT_SIZE, sense) != 0)
269         {
270                 /*    fprintf(stderr,"RequestTapeCapacity: Command failed: Log Sense\n"); */
271                 return NULL;
272         }
273
274         /* dump_data(buffer,64); */
275
276         /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
277         * byte 0 should be 0x31, byte 1 == 0, bytes 2,3 tell how long the
278         * log page is. 
279         */
280         if ((buffer[0]&0x3f) != 0x31)
281         {
282                 /*    fprintf(stderr,"RequestTapeCapacity: Invalid header for page (not 0x31).\n"); */
283                 return NULL;
284         }
285
286         result_len = ((int)buffer[2] << 8) + buffer[3];
287
288         if (result_len != 32)
289         {
290                 /*   fprintf(stderr,"RequestTapeCapacity: Page was %d bytes long, not 32 bytes\n",result_len); */
291                 return NULL; /* This Is Not The Page You're Looking For */
292         }
293
294         result = xmalloc(sizeof(TapeCapacity));
295
296         /* okay, now allocate data and move the buffer over there: */
297
298                 /*      0  1  2  3  4  5  6  7  8  9
299         DATA:   31 00 00 20 00 01 4c 04 01 3a
300                         10 11 12 13 14 15 16 17 18 19
301         DATA:   81 0c 00 02 4c 04 00 00 00 00
302                         20 21 22 23 24 25 26 27 28 29
303         DATA:   00 03 4c 04 01 3f 4b 1f 00 04
304                         30 31 32 33 34 35
305         DATA:   4c 04 00 00 00 00 00 00 00 00
306         DATA:   00 00 00 00 00 00 00 00 00 00
307         DATA:   00 00 00 00 00 00 00 00 00 00
308         DATA:   00 00 00 00
309         */
310
311         result->partition0_remaining =
312                 ((unsigned int)buffer[8]  << 24) +
313                 ((unsigned int)buffer[9]  << 16) +
314                 ((unsigned int)buffer[10] <<  8) + 
315                 buffer[11];
316
317         result->partition1_remaining =
318                 ((unsigned int)buffer[16] << 24) +
319                 ((unsigned int)buffer[17] << 16) +
320                 ((unsigned int)buffer[18] <<  8) +
321                 buffer[19];
322
323         result->partition0_size =
324                 ((unsigned int)buffer[24] << 24) +
325                 ((unsigned int)buffer[25] << 16) +
326                 ((unsigned int)buffer[26] <<  8) +
327                 buffer[27];
328
329         result->partition1_size =
330                 ((unsigned int)buffer[32] << 24) +
331                 ((unsigned int)buffer[33] << 16) +
332                 ((unsigned int)buffer[34] <<  8) +
333                 buffer[35]; 
334
335         return result;
336 }
337
338
339
340 struct tapealert_struct
341 {
342         int length;
343         unsigned char *data;
344 };
345
346 static struct tapealert_struct *RequestTapeAlert(DEVICE_TYPE fd, RequestSense_T *sense)
347 {
348         CDB_T CDB;
349
350         struct tapealert_struct *result;
351         int i, tapealert_len, result_idx;
352
353         unsigned char buffer[TAPEALERT_SIZE];
354         unsigned char *walkptr;
355
356         slow_bzero((char *)buffer, TAPEALERT_SIZE); /*zero it... */
357
358         /* now to create the CDB block: */
359         CDB[0] = 0x4d;  /* Log Sense */
360         CDB[1] = 0;
361         CDB[2] = 0x2e;  /* Tape Alert Page. */
362         CDB[3] = 0;
363         CDB[4] = 0;
364         CDB[5] = 0;
365         CDB[6] = 0;
366         CDB[7] = TAPEALERT_SIZE >> 8 & 0xff;    /* hi byte, allocation size */
367         CDB[8] = TAPEALERT_SIZE & 0xff;                 /* lo byte, allocation size */
368         CDB[9] = 0;                                                             /* reserved */
369
370         if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,TAPEALERT_SIZE,sense)!=0)
371         {
372                 return NULL;
373         }
374
375         result = xmalloc(sizeof(struct tapealert_struct));
376
377         /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
378          * byte 0 should be 0x2e, byte 1 == 0, bytes 2,3 tell how long the
379          * tapealert page is. 
380          */
381         if ((buffer[0]&0x3f) != 0x2e)
382         {
383                 result->data = NULL;
384                 result->length = 0;
385                 return result;
386         }
387
388         tapealert_len = ((int)buffer[2] << 8) + buffer[3];
389
390         if (!tapealert_len)
391         {
392                 result->length = 0;
393                 result->data = NULL;
394                 return result;
395         }
396
397         /* okay, now allocate data and move the buffer over there: */
398         result->length = MAX_TAPE_ALERT;
399         result->data = xzmalloc(MAX_TAPE_ALERT); /* alloc & zero. */
400
401         walkptr = &buffer[4];
402         i = 0;
403
404         while (i < tapealert_len)
405         {
406                 result_idx=(((int)walkptr[0])<<8) + walkptr[1]; /* the parameter #. */
407                 if (result_idx > 0 && result_idx < MAX_TAPE_ALERT)
408                 {
409                         if (walkptr[4])
410                         {
411                                 result->data[result_idx] = 1; 
412                         }
413                         else
414                         {
415                                 result->data[result_idx] = 0;
416                         }
417 #ifdef DEBUGOLD1
418                         fprintf(stderr,"Alert[0x%x]=%d\n",result_idx,result->data[result_idx]);
419                         fflush(stderr);
420 #endif
421                 }
422                 else
423                 {
424                         FatalError("Invalid tapealert page: %d\n",result_idx);
425                 }
426
427                 i = i + 4 + walkptr[3]; /* length byte! */
428                 walkptr = walkptr + 4 + walkptr[3]; /* next! */
429         }
430         return result;
431 }
432
433 static void ReportTapeCapacity(DEVICE_TYPE fd)
434 {
435         /* we actually ignore a bad sense reading, like might happen if the 
436          * tape drive does not support the tape capacity page. 
437          */
438
439         RequestSense_T RequestSense;
440
441         TapeCapacity *result;
442
443         result=RequestTapeCapacity(fd,&RequestSense);
444
445         if (!result)
446                 return;
447
448         printf("Partition 0 Remaining Kbytes: %d\n", result->partition0_remaining);
449         printf("Partition 0 Size in Kbytes: %d\n", result->partition0_size);
450
451         if (result->partition1_size)
452         {
453                 printf("Partition 1 Remaining Kbytes: %d\n", result->partition1_remaining);
454                 printf("Partition 1 Size in Kbytes: %d\n", result->partition1_size);
455         }
456
457         free(result);
458 }
459
460
461
462 static void ReportTapeAlert(DEVICE_TYPE fd)
463 {
464         /* we actually ignore a bad sense reading, like might happen if the 
465          * tape drive does not support the tapealert page. 
466          */
467
468         RequestSense_T RequestSense;
469
470         struct tapealert_struct *result;
471         int i;
472
473         result=RequestTapeAlert(fd,&RequestSense);
474
475         if (!result)
476                 return; /* sorry. Don't print sense here. */
477
478         if (!result->length)
479                 return; /* sorry, no alerts valid. */
480
481         for (i = 0; i < result->length; i++)
482         {
483                 if (result->data[i])
484                 {
485                         printf("TapeAlert[%d]: %s.\n", i, tapealert_messages[i]);
486                 }
487         }
488
489         free(result->data);
490         free(result);
491 }
492
493 static unsigned char
494 *mode_sense(DEVICE_TYPE fd, char pagenum, int alloc_len,  RequestSense_T *RequestSense)
495 {
496         CDB_T CDB;
497
498         unsigned char *input_buffer;
499         unsigned char *tmp;
500         unsigned char *retval;
501         int i, pagelen;
502
503         if (alloc_len > 255)
504         {
505                 FatalError("mode_sense(6) can only read up to 255 characters!\n");
506         }
507
508         input_buffer = (unsigned char *)xzmalloc(256); /* overdo it, eh? */
509
510         /* clear the sense buffer: */
511         slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
512
513         /* returns an array of bytes in the page, or, if not possible, NULL. */
514         CDB[0] = 0x1a; /* Mode Sense(6) */
515         CDB[1] = 0; 
516         CDB[2] = pagenum; /* the page to read. */
517         CDB[3] = 0;
518         CDB[4] = 255; /* allocation length. This does max of 256 bytes! */
519         CDB[5] = 0;
520
521         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, input_buffer, 255, RequestSense) != 0)
522         {
523 #ifdef DEBUG_MODE_SENSE
524                 fprintf(stderr,"Could not execute mode sense...\n");
525                 fflush(stderr);
526 #endif
527                 return NULL; /* sorry, couldn't do it. */
528         }
529
530         /* Oh hell, write protect is the only thing I have: always print
531          * it if our mode page was 0x0fh, before skipping past buffer: 
532          * if the media is *NOT* write protected, just skip, sigh. 
533          *
534          * Oh poops, the blocksize is reported in the block descriptor header
535          * <   * too. Again, just print if our mode page was 0x0f...
536          */
537         if (pagenum == 0x0f)
538         {
539                 int blocklen;
540
541                 if (input_buffer[2] & 0x80)
542                 {
543                         printf("WriteProtect: yes\n");
544                 }
545
546                 if (input_buffer[2] & 0x70)
547                 {
548                         printf("BufferedMode: yes\n");
549                 }
550
551                 if (input_buffer[1] )
552                 {
553                         printf("Medium Type: 0x%x\n", input_buffer[1]);
554                 }
555                 else
556                 {
557                         printf("Medium Type: Not Loaded\n");
558                 }
559
560                 printf("Density Code: 0x%x\n", input_buffer[4]);
561                 /* Put out the block size: */
562
563                 blocklen =      ((int)input_buffer[9]  << 16)+
564                                         ((int)input_buffer[10] <<  8)+
565                                         input_buffer[11];
566
567                 printf("BlockSize: %d\n", blocklen);
568         }
569
570         /* First skip past any header.... */
571         tmp = input_buffer + 4 + input_buffer[3];
572
573         /* now find out real length of page... */
574         pagelen = tmp[1] + 2;
575         retval = xmalloc(pagelen);
576
577         /* and copy our data to the new page. */
578         for (i=0;i<pagelen;i++)
579         {
580                 retval[i]=tmp[i];
581         }
582
583         /* okay, free our input buffer: */
584         free(input_buffer);
585         return retval;
586 }
587
588
589 #define DCE_MASK 0x80
590 #define DCC_MASK 0x40
591 #define DDE_MASK 0x80
592
593 static void ReportCompressionPage(DEVICE_TYPE fd)
594 {
595         /* actually ignore a bad sense reading, like might happen if the tape
596         * drive does not support the mode sense compression page. 
597         */
598
599         RequestSense_T RequestSense;
600
601         unsigned char *compression_page;
602
603         compression_page=mode_sense(fd,0x0f,16,&RequestSense);
604
605         if (!compression_page)
606         {
607                 return;  /* sorry! */
608         }
609
610         /* Okay, we now have the compression page. Now print stuff from it: */
611         printf("DataCompEnabled: %s\n", (compression_page[2] & DCE_MASK)? "yes" : "no");
612         printf("DataCompCapable: %s\n", (compression_page[2] & DCC_MASK)? "yes" : "no");
613         printf("DataDeCompEnabled: %s\n", (compression_page[3] & DDE_MASK)? "yes" : "no");
614         printf("CompType: 0x%x\n",
615                 (compression_page[4] << 24) +
616                 (compression_page[5] << 16) +
617                 (compression_page[6] <<  8) +
618                  compression_page[7]);
619
620         printf("DeCompType: 0x%x\n",
621                 (compression_page[8]  << 24) +
622                 (compression_page[9]  << 16) +
623                 (compression_page[10] <<  8) +
624                  compression_page[11]);
625
626         free(compression_page);
627 }
628
629 /* Now for the device configuration mode page: */
630
631 static void ReportConfigPage(DEVICE_TYPE fd)
632 {
633         RequestSense_T RequestSense;
634         unsigned char *config_page;
635
636         config_page = mode_sense(fd, 0x10, 16, &RequestSense);
637         if (!config_page)
638                 return;
639
640         /* Now to print the stuff: */
641         printf("ActivePartition: %d\n", config_page[3]);
642
643         /* The following does NOT work accurately on any tape drive I know of... */
644         /*  printf("DevConfigComp: %s\n", config_page[14] ? "yes" : "no"); */
645         printf("EarlyWarningSize: %d\n",
646                 (config_page[11] << 16) +
647                 (config_page[12] <<  8) +
648                  config_page[13]);
649 }
650
651 /* ***************************************
652  * Medium Partition Page:
653  * 
654  * The problem here, as we oh so graphically demonstrated during debugging
655  * of the Linux 'st' driver :-), is that there are actually *TWO* formats for
656  * the Medium Partition Page: There is the "long" format, where there is a
657  * partition size word for each partition on the page, and there is a "short"
658  * format, beloved of DAT drives, which only has a partition size word for
659  * partition #1 (and no partition size word for partition #0, and no
660  * provisions for any more partitions). So we must look at the size and
661  * # of partitions defined to know what to report as what. 
662  *
663  ********************************************/
664
665 static void ReportPartitionPage(DEVICE_TYPE fd)
666 {
667         RequestSense_T RequestSense;
668         unsigned char *partition_page;
669
670         int num_parts,max_parts;
671         int i;
672
673         partition_page=mode_sense(fd,0x11,255,&RequestSense);
674         if (!partition_page)
675                 return;
676
677         /* Okay, now we have either old format or new format: */
678         num_parts = partition_page[3];
679         max_parts = partition_page[2];
680
681         printf("NumPartitions: %d\n", num_parts);
682         printf("MaxPartitions: %d\n", max_parts);
683
684         if (!num_parts)
685         {
686                 /* if no additional partitions, then ... */ 
687                 free(partition_page);
688                 return;
689         }
690
691         /* we know we have at least one partition if we got here. Check the
692          * page size field. If it is 8 or below, then we are the old format....
693          */
694
695 #ifdef DEBUG_PARTITION
696         fprintf(stderr,"partition_page[1]=%d\n",partition_page[1]);
697         fflush(stderr);
698 #endif
699         if (partition_page[1]==8)
700         {
701                 /* old-style! */
702                 printf("Partition1: %d\n",(partition_page[8]<<8)+partition_page[9]);
703         }
704         else
705         {
706                 /* new-style! */
707                 for (i=0;i<=max_parts;i++)
708                 {
709 #ifdef DEBUG_PARTITION
710                         fprintf(stderr,"partition%d:[%d]%d [%d]%d\n", i, 8 + i * 2,
711                         partition_page[8+i*2]<<8, 9+i*2,partition_page[9 + i * 2]);
712                         fflush(stderr);
713 #endif
714                         printf("Partition%d: %d\n", i,
715                                         (partition_page[8 + i * 2] << 8) + partition_page[9 + i * 2]);
716                 }
717         }
718         free(partition_page);
719 }
720
721 static void ReportSerialNumber(DEVICE_TYPE fd)
722 {
723         /*      Actually ignore a bad sense reading, like might happen if the
724                 tape drive does not support the inquiry page 0x80. 
725         */
726
727         RequestSense_T sense;
728         CDB_T CDB;
729
730 #define WILD_SER_SIZE 30
731 unsigned char buffer[WILD_SER_SIZE]; /* just wildly overestimate serial# length! */
732
733         int i, lim;
734         char *bufptr; 
735
736         CDB[0] = 0x12;                  /* INQUIRY */
737         CDB[1] = 1;                             /* EVPD = 1 */
738         CDB[2] = 0x80;                  /* The serial # page, hopefully. */
739         CDB[3] = 0;                             /* reserved */
740         CDB[4] = WILD_SER_SIZE;
741         CDB[5] = 0;
742
743         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, &buffer, sizeof(buffer), &sense) != 0)
744         {
745                 /* PrintRequestSense(&sense); */ /* zap debugging output :-) */
746                 /* printf("No Serial Number: None\n"); */
747                 return; 
748         }
749
750         /* okay, we have something in our buffer. Byte 3 should be the length of
751         the sernum field, and bytes 4 onward are the serial #. */
752
753         lim = (int)buffer[3];
754         bufptr = (char *)&(buffer[4]);
755
756         printf("SerialNumber: '");
757         for (i=0;i<lim;i++)
758         {
759                 putchar(*bufptr++);
760         }
761         printf("'\n");
762 }
763
764 /*  Read Block Limits! */
765
766 void ReportBlockLimits(DEVICE_TYPE fd)
767 {
768         RequestSense_T sense;
769         CDB_T CDB;
770         unsigned char buffer[6];
771
772         CDB[0] = 0x05;  /* READ_BLOCK_LIMITS */
773         CDB[1] = 0;
774         CDB[2] = 0;
775         CDB[3] = 0;             /* 1-5 all unused. */
776         CDB[4] = 0;
777         CDB[5] = 0; 
778
779         slow_bzero((char *)&sense,sizeof(RequestSense_T));
780         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 6, &sense) != 0)
781         {
782                 return;
783         }
784
785         /* okay, but if we did get a result, print it: */
786         printf("MinBlock: %d\n", (buffer[4] << 8) + buffer[5]);
787         printf("MaxBlock: %d\n", (buffer[1] << 16) + (buffer[2]<<8) + buffer[3]);
788 }
789
790 /* Do a READ_POSITION. This may not be always valid, but (shrug). */
791 void ReadPosition(DEVICE_TYPE fd)
792 {
793         RequestSense_T sense;
794         CDB_T CDB;
795         unsigned char buffer[20];
796         unsigned int position;
797
798         CDB[0] = 0x34;          /* READ_POSITION */
799         CDB[1] = 0;
800         CDB[2] = 0;
801         CDB[3] = 0;                     /* 1-9 all unused. */
802         CDB[4] = 0;
803         CDB[5] = 0;
804         CDB[6] = 0;
805         CDB[7] = 0;
806         CDB[8] = 0;
807         CDB[9] = 0;
808
809         slow_bzero((char *)&sense, sizeof(RequestSense_T));
810
811         SCSI_Set_Timeout(2); /* set timeout to 2 seconds! */
812
813         /* if we don't get a result (e.g. we issue this to a disk drive), punt. */
814         if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, 20, &sense) != 0)
815         {
816                 return;
817         }
818
819         SCSI_Default_Timeout(); /* reset it to 5 minutes, sigh! */
820         /* okay, but if we did get a result, print it: */
821
822 #define RBL_BOP 0x80
823 #define RBL_EOP 0x40
824 #define RBL_BCU 0x20
825 #define RBL_BYCU  0x10
826 #define RBL_R1 0x08
827 #define RBL_BPU 0x04
828 #define RBL_PERR 0x02
829
830         /* If we have BOP, go ahead and print that. */
831         if (buffer[0]&RBL_BOP)
832         {
833                 printf("BOP: yes\n");
834         }
835
836         /* if we have valid data, print it: */
837         if (buffer[0]&RBL_BPU)
838         {
839                 printf("Block Position: -1");
840         }
841         else
842         {
843                 position = (unsigned int)(((unsigned int)buffer[4] << 24) +
844                                                                   ((unsigned int)buffer[5] << 16) +
845                                                                   ((unsigned int)buffer[6] <<  8) +
846                                                                         buffer[7]);
847
848                 printf("Block Position: %d\n",position);
849         }
850 }
851
852 /* Test unit ready: This will tell us whether the tape drive
853  * is currently ready to read or write.
854  */
855
856 int TestUnitReady(DEVICE_TYPE fd)
857 {
858         RequestSense_T sense;
859         CDB_T CDB;
860         unsigned char buffer[6];
861
862         CDB[0] = 0x00;          /* TEST_UNIT_READY */
863         CDB[1] = 0;
864         CDB[2] = 0;
865         CDB[3] = 0;                     /* 1-5 all unused. */
866         CDB[4] = 0;
867         CDB[5] = 0;
868
869         slow_bzero((char *)&sense,sizeof(RequestSense_T));
870         if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0)
871         {
872                 printf("Ready: no\n");
873                 return 0;
874         }
875
876         printf("Ready: yes\n");
877         return 1;
878 }
879
880 /* We write a filemarks of 0 before going to grab position, in order
881  * to insure that data in the buffer is not a problem. 
882  */
883
884 int WriteFileMarks(DEVICE_TYPE fd,int count)
885 {
886         RequestSense_T sense;
887         CDB_T CDB;
888         unsigned char buffer[6];
889
890         CDB[0] = 0x10;  /* WRITE_FILE_MARKS */
891         CDB[1] = 0;
892         CDB[2] = (unsigned char)(count >> 16);
893         CDB[3] = (unsigned char)(count >> 8);
894         CDB[4] = (unsigned char)count;
895         CDB[5] = 0; 
896
897         /* we really don't care if this command works or not, sigh.  */
898         slow_bzero((char *)&sense, sizeof(RequestSense_T));
899         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 0, &sense) != 0)
900         {
901                 return 1;
902         }
903
904         return 0;
905 }
906
907
908 /* This will get the SCSI ID and LUN of the target device, if such
909  * is available from the OS. Currently only Linux supports this,
910  * but other drivers could, if someone wants to write a 
911  * SCSI_GetIDLun function for them. 
912  */
913 #ifdef HAVE_GET_ID_LUN
914
915 static void ReportIDLun(DEVICE_TYPE fd)
916 {
917         scsi_id_t *scsi_id;
918
919         scsi_id = SCSI_GetIDLun(fd);
920         printf("SCSI ID: %d\nSCSI LUN: %d\n", scsi_id->id, scsi_id->lun);
921 }
922
923 #endif
924
925 /* we only have one argument: "-f <device>". */
926 int main(int argc, char **argv)
927 {
928         DEVICE_TYPE fd;
929         char *filename;
930
931         argv0=argv[0];
932
933         if (argc != 3)
934         {
935                 fprintf(stderr,"argc=%d",argc);
936                 usage();
937         }
938
939         if (strcmp(argv[1],"-f")!=0)
940         {
941                 usage();
942         }
943         filename=argv[2];
944
945         fd=SCSI_OpenDevice(filename);
946
947         /* Now to call the various routines: */
948         ReportInquiry(fd);
949         ReportSerialNumber(fd);
950         ReportTapeAlert(fd);
951         ReportBlockLimits(fd); 
952
953 #ifdef HAVE_GET_ID_LUN
954         ReportIDLun(fd);
955 #endif
956
957         /* okay, we should only report position if the unit is ready :-(. */
958         if (TestUnitReady(fd))
959         {
960                 ReportCompressionPage(fd); 
961                 ReadPosition(fd); 
962                 ReportTapeCapacity(fd);         /* only if we have it */
963                 ReportConfigPage(fd);           /* only valid if unit is ready. */
964                 ReportPartitionPage(fd); 
965         }
966
967         exit(0);
968 }