Imported Upstream version 1.2.16rel
[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: 2001/06/19 21:51:32 $
5  * $Revision: 1.2 $
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   FatalError("Usage: tapeinfo -f <generic-device>\n");
57 }
58
59 /* A table for printing out the peripheral device type as ASCII. */ 
60 static char *PeripheralDeviceType[32] = {
61   "Disk Drive",
62   "Tape Drive",
63   "Printer",
64   "Processor",
65   "Write-once",
66   "CD-ROM",
67   "Scanner",
68   "Optical",
69   "Medium Changer",
70   "Communications",
71   "ASC IT8",
72   "ASC IT8",
73   "RAID Array",
74   "Enclosure Services",
75   "OCR/W",
76   "Bridging Expander", /* 0x10 */
77   "Reserved",  /* 0x11 */
78   "Reserved", /* 0x12 */
79   "Reserved",  /* 0x13 */
80   "Reserved",  /* 0x14 */
81   "Reserved",  /* 0x15 */
82   "Reserved",  /* 0x16 */
83   "Reserved",  /* 0x17 */
84   "Reserved",  /* 0x18 */
85   "Reserved",  /* 0x19 */
86   "Reserved",  /* 0x1a */
87   "Reserved",  /* 0x1b */
88   "Reserved",  /* 0x1c */
89   "Reserved",  /* 0x1d */
90   "Reserved",  /* 0x1e */
91   "Unknown"    /* 0x1f */
92 };
93
94
95
96 /* we call it MediumChangerFD for history reasons, sigh. */
97
98 /* now to print inquiry information: Copied from other one.... */
99
100 static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
101 {
102   RequestSense_T RequestSense;
103   Inquiry_T *Inquiry;
104   int i;
105
106   Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
107   if (Inquiry == NULL) 
108     {
109       PrintRequestSense(&RequestSense);
110       FatalError("INQUIRY Command Failed\n");
111     }
112   
113   printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
114   printf("Vendor ID: '");
115   for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
116     printf("%c", Inquiry->VendorIdentification[i]);
117   printf("'\nProduct ID: '");
118   for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
119     printf("%c", Inquiry->ProductIdentification[i]);
120   printf("'\nRevision: '");
121   for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
122     printf("%c", Inquiry->ProductRevisionLevel[i]);
123   printf("'\n");\
124   if (Inquiry->MChngr) {  /* check the attached-media-changer bit... */
125     printf("Attached Changer: Yes\n");
126   } else {
127     printf("Attached Changer: No\n");
128   }
129   free(Inquiry);  /* well, we're about to exit, but ... */
130 }
131
132
133
134
135 /* Okay, now for the Log Sense Tape Alert Page (if supported): */
136 #define TAPEALERT_SIZE 2048  /* max size of tapealert buffer. */ 
137 #define MAX_TAPE_ALERT 0x41
138
139 static char *tapealert_messages[]= {
140   "Undefined", /* 0 */
141   "         Read: Having problems reading (slowing down)", /* 1 */
142   "        Write: Having problems writing (losing capacity)", /* 2 */
143   "   Hard Error: Uncorrectable read/write error", /* 3 */
144   "        Media: Media Performance Degraded, Data Is At Risk", /* 4 */
145   " Read Failure: Tape faulty or tape drive broken", /* 5 */
146   "Write Failure: Tape faulty or tape drive broken", /* 6 */
147   "   Media Life: The tape has reached the end of its useful life", /* 7 */
148   "Not Data Grade:Replace cartridge with one  containing data grade tape",/*8*/
149   "Write Protect: Attempted to write to a write-protected cartridge",/*9 */
150   "   No Removal: Cannot unload, initiator is preventing media removal", /*a*/
151   "Cleaning Media:Cannot back up or restore to a cleaning cartridge", /* b */
152   "   Bad Format: The loaded tape contains data in an unsupported format", /*c */
153   " Snapped Tape: The data cartridge contains a broken tape", /* d */
154   "Undefined", /* e */
155   "Undefined", /* f */
156   "Undefined", /* 10 */
157   "Undefined", /* 11 */
158   "Undefined", /* 12 */
159   "Undefined", /* 13 */
160   "    Clean Now: The tape drive neads cleaning NOW", /* 0x14 */
161   "Clean Periodic:The tape drive needs to be cleaned at next opportunity", /* 0x15 */
162   "Cleaning Media:Cannot clean because cleaning cartridge used up, insert new cleaning cartridge to clean the drive", /* 0x16 */
163   "Undefined", /* 0x17 */
164   "Undefined", /* 0x18 */
165   "Undefined", /* 0x19 */
166   "Undefined", /* 0x1a */
167   "Undefined", /* 0x1b */
168   "Undefined", /* 0x1c */
169   "Undefined", /* 0x1d */
170   "   Hardware A: Tape drive has a problem not read/write related", /* 0x1e */
171   "   Hardware B: Tape drive has a problem not read/write related", /* 0x1f */
172   "    Interface: Problem with SCSI interface between tape drive and initiator", /* 0x20 */
173   "  Eject Media: The current operation has failed. Eject and reload media", /* 0x21 */
174   "Download Fail: Attempt to download new firmware failed", /* 0x22 */
175   "Undefined", /* 0x23 */
176   "Undefined", /* 0x24 */
177   "Undefined", /* 0x25 */
178   "Undefined", /* 0x26 */
179   "Undefined", /* 0x27 */
180   "Loader Hardware A: Changer having problems communicating with tape drive", /* 0x28   40 */
181   "Loader Stray Tape: Stray tape left in drive from prior error", /* 0x29 41 */
182   "Loader Hardware B: Autoloader mechanism has a fault", /* 0x2a 42 */
183   "  Loader Door: Loader door is open, please close it", /* 0x2b 43 */
184   "Undefined", /* 0x2c */
185   "Undefined", /* 0x2d */
186   "Undefined", /* 0x2e */
187   "Undefined", /* 0x2f */
188   "Undefined", /* 0x30 */
189   "Undefined", /* 0x31 */
190   "Undefined", /* 0x32 */
191   "Undefined", /* 0x33 */
192   "Undefined", /* 0x34 */
193   "Undefined", /* 0x35 */
194   "Undefined", /* 0x36 */
195   "Undefined", /* 0x37 */
196   "Undefined", /* 0x38 */
197   "Undefined", /* 0x39 */
198   "Undefined", /* 0x3a */
199   "Undefined", /* 0x3b */
200   "Undefined", /* 0x3c */
201   "Undefined", /* 0x3d */
202   "Undefined", /* 0x3e */
203   "Undefined", /* 0x3f */
204   "Undefined" /* 0x40 */
205 };
206
207
208 struct tapealert_struct {
209   int length;
210   unsigned char *data;
211 };
212   
213   
214 static struct tapealert_struct *RequestTapeAlert(DEVICE_TYPE fd, RequestSense_T *sense) {
215   CDB_T CDB;
216   
217   struct tapealert_struct *result;
218   int i,tapealert_len,result_idx;
219   
220   unsigned char buffer[TAPEALERT_SIZE];
221   unsigned char *walkptr;
222
223   slow_bzero(buffer,TAPEALERT_SIZE); /*zero it... */
224
225   /* now to create the CDB block: */
226   CDB[0]=0x4d;   /* Log Sense */
227   CDB[1]=0;   
228   CDB[2]=0x2e;   /* Tape Alert Page. */
229   CDB[3]=0;
230   CDB[4]=0;
231   CDB[5]=0;
232   CDB[6]=0;
233   CDB[7]=TAPEALERT_SIZE>>8 & 0xff;  /* hi byte, allocation size */
234   CDB[8]=TAPEALERT_SIZE & 0xff;     /* lo byte, allocation size */
235   CDB[9]=0;  /* reserved */ 
236
237   if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,TAPEALERT_SIZE,sense)!=0){
238     return NULL;
239   }
240
241   result=xmalloc(sizeof(struct tapealert_struct));
242
243   /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
244    * byte 0 should be 0x2e, byte 1 == 0, bytes 2,3 tell how long the
245    * tapealert page is. 
246    */
247   if ((buffer[0]&0x3f) != 0x2e) {
248     result->data=NULL;
249     result->length=0;
250     return result;
251   }
252
253   tapealert_len=(((int)buffer[2])<<8) + buffer[3];
254   
255
256   if (!tapealert_len) {
257     result->length=0;
258     result->data=NULL;
259     return result;
260   }
261
262   /* okay, now allocate data and move the buffer over there: */
263   result->length=MAX_TAPE_ALERT;
264   result->data=xzmalloc(MAX_TAPE_ALERT); /* alloc & zero. */
265   
266   walkptr=&buffer[4];
267   i=0;
268   while (i<tapealert_len) {
269     
270
271     result_idx=(((int)walkptr[0])<<8) + walkptr[1]; /* the parameter #. */
272     if (result_idx > 0 && result_idx < MAX_TAPE_ALERT) {
273       if (walkptr[4]) {
274         result->data[result_idx]=1; 
275       } else {
276         result->data[result_idx]=0;
277       }
278 #ifdef DEBUGOLD1
279       fprintf(stderr,"Alert[0x%x]=%d\n",result_idx,result->data[result_idx]);
280       fflush(stderr);
281 #endif
282     } else {
283       FatalError("Invalid tapealert page: %d\n",result_idx);
284     }
285     i=i+4+walkptr[3]; /* length byte! */
286     walkptr=walkptr+4+walkptr[3]; /* next! */
287   }
288   return result;
289 }
290
291 static void ReportTapeAlert(DEVICE_TYPE fd) {
292   /* we actually ignore a bad sense reading, like might happen if the 
293    * tape drive does not support the tapealert page. 
294    */
295   
296   RequestSense_T RequestSense;
297   
298   struct tapealert_struct *result;
299   int i;
300
301   result=RequestTapeAlert(fd,&RequestSense);
302   if (!result) return; /* sorry. Don't print sense here. */
303   if (!result->length) return; /* sorry, no alerts valid. */
304   
305   for (i=0;i<result->length;i++) {
306     if (result->data[i]) {
307       printf("TapeAlert[%d]:%s.\n",i,tapealert_messages[i]);
308     }
309   }
310   free(result->data);
311   free(result);
312   return;
313 }
314
315 static unsigned char
316 *mode_sense(DEVICE_TYPE fd, int pagenum, int alloc_len,  RequestSense_T *RequestSense) {
317   CDB_T CDB;
318
319   unsigned char *input_buffer; /*the input buffer -- has junk prepended to
320                                 * actual sense page. 
321                                 */
322   unsigned char *tmp;
323   unsigned char *retval;  /* the return value. */
324   int i,pagelen;
325
326   if (alloc_len > 255) {
327     FatalError("mode_sense(6) can only read up to 255 characters!\n");
328   }
329
330   input_buffer=(unsigned char *)xzmalloc(256); /* overdo it, eh? */
331
332   /* clear the sense buffer: */
333   slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
334   
335
336   /* returns an array of bytes in the page, or, if not possible, NULL. */
337   CDB[0]=0x1a; /* Mode Sense(6) */
338   CDB[1]=0; 
339   CDB[2]=pagenum; /* the page to read. */
340   CDB[3]=0;
341   CDB[4]=255; /* allocation length. This does max of 256 bytes! */
342   CDB[5]=0;
343   
344   if (SCSI_ExecuteCommand(fd,Input,&CDB,6,
345                           input_buffer,255,RequestSense) != 0) {
346 #ifdef DEBUG_MODE_SENSE
347     fprintf(stderr,"Could not execute mode sense...\n");
348     fflush(stderr);
349 #endif
350     return NULL; /* sorry, couldn't do it. */
351   }
352
353   /* Oh hell, write protect is the only thing I have: always print
354    * it if our mode page was 0x0fh, before skipping past buffer: 
355    * if the media is *NOT* write protected, just skip, sigh. 
356    *
357    * Oh poops, the blocksize is reported in the block descriptor header
358 <   * too. Again, just print if our mode page was 0x0f...
359    */
360   if (pagenum == 0x0f) {
361     int blocklen;
362     if (input_buffer[2] & 0x80) {
363       printf("WriteProtect: yes\n");
364     }
365     if (input_buffer[2] & 0x70) {
366       printf("BufferedMode: yes\n");
367     }
368     if (input_buffer[1] ) {
369       printf("Medium Type: 0x%x\n", input_buffer[1]);
370     } else {
371       printf("Medium Type: Not Loaded\n");
372     }
373     printf("Density Code: 0x%x\n", input_buffer[4]);
374     /* Put out the block size: */
375     blocklen=((int)input_buffer[9]<<16)+
376       ((int)input_buffer[10]<<8)+
377       input_buffer[11];
378     printf("BlockSize: %d\n", blocklen);
379     /* This doesn't seem to do anything useful on my Tandberg so I
380      * am taking it out -- elg
381      */ 
382     /* Put out the # Blocks */
383     /* blocklen=((int)input_buffer[5]<<16)+ */
384     /*      ((int)input_buffer[6]<<8)+ */
385     /*      input_buffer[7]; */
386     /* printf("Num Blocks:  %d\n", blocklen); */
387   }
388   /* First skip past any header.... */
389   tmp=input_buffer+4+input_buffer[3];
390   /* now find out real length of page... */
391   pagelen=tmp[1]+2;
392   retval=xmalloc(pagelen);
393   /* and copy our data to the new page. */
394   for (i=0;i<pagelen;i++) {
395     retval[i]=tmp[i];
396   }
397   /* okay, free our input buffer: */
398   free(input_buffer);
399   return retval;
400 }
401
402
403
404
405
406 #define DCE_MASK 0x80
407 #define DCC_MASK 0x40
408 #define DDE_MASK 0x80
409
410 static void ReportCompressionPage(DEVICE_TYPE fd) {
411   /* actually ignore a bad sense reading, like might happen if the tape
412    * drive does not support the mode sense compression page. 
413    */
414
415   RequestSense_T RequestSense;
416
417   unsigned char *compression_page;
418
419
420   compression_page=mode_sense(fd,0x0f,16,&RequestSense);
421
422   if (!compression_page) {
423     return;  /* sorry! */
424   }
425
426   /* Okay, we now have the compression page. Now print stuff from it: */
427   printf("DataCompEnabled: %s\n", (compression_page[2]&DCE_MASK)? "yes" : "no");
428   printf("DataCompCapable: %s\n", (compression_page[2]&DCC_MASK)? "yes" : "no");
429   printf("DataDeCompEnabled: %s\n", (compression_page[3]&DDE_MASK)? "yes" : "no");
430   printf("CompType: 0x%x\n",
431          (compression_page[4]<<24)+(compression_page[5]<<16)+(compression_page[6]<<8)+compression_page[7]);
432   printf("DeCompType: 0x%x\n",
433         (compression_page[8]<<24)+ (compression_page[9]<<16)+(compression_page[10]<<8)+compression_page[11]);
434   free(compression_page);
435   return;
436 }
437   
438 /* Now for the device configuration mode page: */
439
440 static void ReportConfigPage(DEVICE_TYPE fd) {
441   RequestSense_T RequestSense;
442   unsigned char *config_page;
443   
444   config_page=mode_sense(fd,0x10,16,&RequestSense);
445   if (!config_page) return;
446
447   /* Now to print the stuff: */
448   printf("ActivePartition: %d\n",config_page[3]);
449   /* The following does NOT work accurately on any tape drive I know of... */
450   /*  printf("DevConfigComp: %s\n", config_page[14] ? "yes" : "no"); */
451   printf("EarlyWarningSize: %d\n",
452          (config_page[11]<<16)+(config_page[12]<<8)+config_page[13]);
453   return;
454 }
455
456 /* ***************************************
457  * Medium Partition Page:
458  * 
459  * The problem here, as we oh so graphically demonstrated during debugging
460  * of the Linux 'st' driver :-), is that there are actually *TWO* formats for
461  * the Medium Partition Page: There is the "long" format, where there is a
462  * partition size word for each partition on the page, and there is a "short"
463  * format, beloved of DAT drives, which only has a partition size word for
464  * partition #1 (and no partition size word for partition #0, and no
465  * provisions for any more partitions). So we must look at the size and
466  * # of partitions defined to know what to report as what. 
467  *
468  ********************************************/
469
470 static void ReportPartitionPage(DEVICE_TYPE fd) {
471   RequestSense_T RequestSense;
472   unsigned char *partition_page;
473
474   int num_parts,max_parts;
475   int i;
476
477   partition_page=mode_sense(fd,0x11,255,&RequestSense);
478   if (!partition_page) return;
479
480   /* Okay, now we have either old format or new format: */
481   num_parts=partition_page[3];
482   max_parts=partition_page[2];
483
484   printf("NumPartitions:%d\n",num_parts);
485   printf("MaxPartitions:%d\n",max_parts);
486   if (!num_parts) {  /* if no additional partitions, then ... */ 
487     free(partition_page);
488     return;
489   }
490   
491   /* we know we have at least one partition if we got here. Check the
492    * page size field. If it is 8 or below, then we are the old format....
493    */
494
495 #ifdef DEBUG_PARTITION
496   fprintf(stderr,"partition_page[1]=%d\n",partition_page[1]);
497   fflush(stderr);
498 #endif
499   if (partition_page[1]==8) {
500     /* old-style! */
501     printf("Partition1:%d\n",(partition_page[8]<<8)+partition_page[9]);
502   } else {
503     /* new-style! */
504     for (i=0;i<=max_parts;i++) {
505 #ifdef DEBUG_PARTITION    
506       fprintf(stderr,"partition%d:[%d]%d [%d]%d\n",i,8+i*2,
507               partition_page[8+i*2]<<8, 9+i*2,partition_page[9+i*2]);
508       fflush(stderr);
509 #endif
510       printf("Partition%d:%d\n",i,
511              (partition_page[8+i*2]<<8)+partition_page[9+i*2]);
512     }
513   }
514   free(partition_page);
515   return;
516 }
517
518 static void ReportSerialNumber(DEVICE_TYPE fd) {
519   /* actually ignore a bad sense reading, like might happen if the
520      tape drive does not support the inquiry page 0x80. 
521   */
522
523   RequestSense_T sense;
524   CDB_T CDB;
525 #define WILD_SER_SIZE 30
526   unsigned char buffer[WILD_SER_SIZE]; /* just wildly overestimate serial# length! */
527
528   int i,lim;
529   char *bufptr; 
530   
531   CDB[0]=0x12;  /* INQUIRY */
532   CDB[1]= 1;     /* EVPD = 1 */
533   CDB[2]=0x80;   /* The serial # page, hopefully. */
534   CDB[3]=0  ; /* reserved */
535   CDB[4]=WILD_SER_SIZE;
536   CDB[5]=0;
537
538   if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
539                           &buffer, sizeof(buffer), &sense) != 0)  {
540     /* PrintRequestSense(&sense); */ /* zap debugging output :-) */
541     /* printf("No Serial Number: None\n"); */
542     return; 
543   }
544   
545   /* okay, we have something in our buffer. Byte 3 should be the length of
546      the sernum field, and bytes 4 onward are the serial #. */
547   
548   lim=(int)buffer[3];
549   bufptr= &(buffer[4]);
550   
551   printf("SerialNumber: '");
552   for (i=0;i<lim;i++) {
553     putchar(*bufptr++);
554   }
555   printf("'\n");
556   return ; /* done! */
557 }
558     
559
560 /*  Read Block Limits! */
561
562 void ReportBlockLimits(DEVICE_TYPE fd) {
563   RequestSense_T sense;
564   CDB_T CDB;
565   unsigned char buffer[6];
566
567
568   CDB[0]=0x05;  /* READ_BLOCK_LIMITS */
569   CDB[1]=0;
570   CDB[2]=0;
571   CDB[3]=0;  /* 1-5 all unused. */
572   CDB[4]=0;
573   CDB[5]=0; 
574
575   slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
576   if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,6,&sense)!=0){
577     return;
578   }
579   
580   /* okay, but if we did get a result, print it: */
581   printf("MinBlock:%d\n",
582          (buffer[4]<<8)+buffer[5]);
583   printf("MaxBlock:%d\n",
584          (buffer[1]<<16)+(buffer[2]<<8)+buffer[3]);
585
586   return;
587 }
588   
589 /* Do a READ_POSITION. This may not be always valid, but (shrug). */
590 void ReadPosition(DEVICE_TYPE fd) {
591   RequestSense_T sense;
592   CDB_T CDB;
593   unsigned char buffer[20];
594   unsigned int position;
595
596
597   CDB[0]=0x34;  /* READ_POSITION */
598   CDB[1]=0;
599   CDB[2]=0;
600   CDB[3]=0;  /* 1-9 all unused. */
601   CDB[4]=0;
602   CDB[5]=0; 
603   CDB[6]=0;
604   CDB[7]=0;
605   CDB[8]=0;
606   CDB[9]=0;
607
608   slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
609
610   /* set the timeout: */
611   SCSI_Set_Timeout(2); /* set timeout to 2 seconds! */
612
613   /* if we don't get a result (e.g. we issue this to a disk drive), punt. */
614   if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,20,&sense)!=0){
615     return;
616   }
617   
618   SCSI_Default_Timeout(); /* reset it to 5 minutes, sigh! */
619   /* okay, but if we did get a result, print it: */
620 #define RBL_BOP 0x80
621 #define RBL_EOP 0x40
622 #define RBL_BCU 0x20
623 #define RBL_BYCU  0x10
624 #define RBL_R1 0x08
625 #define RBL_BPU 0x04
626 #define RBL_PERR 0x02
627
628   /* If we have BOP, go ahead and print that. */
629   if (buffer[0]&RBL_BOP) {
630     printf("BOP: yes\n");
631   }
632   /* if we have valid data, print it: */
633   if (buffer[0]&RBL_BPU) {
634     printf("Block Position: -1");
635   } else {
636     
637     position= (unsigned int) (((unsigned int)buffer[4]<<24) +
638                               ((unsigned int)buffer[5]<<16) + 
639                               ((unsigned int)buffer[6]<<8) + buffer[7]);
640     printf("Block Position: %d\n",position);
641   }
642   return;
643 }
644
645 /* Test unit ready: This will tell us whether the tape drive
646  * is currently ready to read or write.
647  */
648
649 int TestUnitReady(DEVICE_TYPE fd) {
650   RequestSense_T sense;
651   CDB_T CDB;
652   unsigned char buffer[6];
653
654
655   CDB[0]=0x00;  /* TEST_UNIT_READY */
656   CDB[1]=0;
657   CDB[2]=0;
658   CDB[3]=0;  /* 1-5 all unused. */
659   CDB[4]=0;
660   CDB[5]=0; 
661
662   slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
663   if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0){
664     printf("Ready: no\n");
665     return 0;
666   }
667   
668   printf("Ready: yes\n");
669   return 1;
670 }
671
672 /* We write a filemarks of 0 before going to grab position, in order
673  * to insure that data in the buffer is not a problem. 
674  */
675
676 int WriteFileMarks(DEVICE_TYPE fd,int count) {
677   RequestSense_T sense;
678   CDB_T CDB;
679   unsigned char buffer[6];
680
681
682   CDB[0]=0x10;  /* WRITE_FILE_MARKS */
683   CDB[1]=0;
684   CDB[2]=(count >> 16) & 0xff;
685   CDB[3]=(count >>8) & 0xff;
686   CDB[4]=count & 0xff;
687   CDB[5]=0; 
688
689   /* we really don't care if this command works or not, sigh.  */
690   slow_bzero((unsigned char *)&sense,sizeof(RequestSense_T));
691   if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0){
692     return 1;
693   }
694   return 0;
695 }
696
697
698 /* This will get the SCSI ID and LUN of the target device, if such
699  * is available from the OS. Currently only Linux supports this,
700  * but other drivers could, if someone wants to write a 
701  * SCSI_GetIDLun function for them. 
702  */
703 #ifdef HAVE_GET_ID_LUN
704
705 static void ReportIDLun(DEVICE_TYPE fd) {
706   scsi_id_t *scsi_id;
707
708   scsi_id=SCSI_GetIDLun(fd);
709   printf("SCSI ID: %d\nSCSI LUN: %d\n",scsi_id->id,scsi_id->lun);
710 }
711
712 #endif
713
714 /* we only have one argument: "-f <device>". */
715 int main(int argc, char **argv) {
716   DEVICE_TYPE fd;
717   char *filename;
718
719   argv0=argv[0];
720   
721   if (argc != 3) {
722     fprintf(stderr,"argc=%d",argc);
723     usage();
724   }
725
726   if (strcmp(argv[1],"-f")!=0) {
727     usage();
728   }
729   filename=argv[2];
730   
731   fd=SCSI_OpenDevice(filename);
732   
733   /* Now to call the various routines: */
734   ReportInquiry(fd);
735   ReportSerialNumber(fd);
736   ReportTapeAlert(fd);
737   /* ReportConfigPage(fd);  */
738   /* ReportPartitionPage(fd); */
739   ReportBlockLimits(fd); 
740 #ifdef HAVE_GET_ID_LUN
741   ReportIDLun(fd);
742 #endif
743
744   /* okay, we should only report position if the unit is ready :-(. */
745   if (TestUnitReady(fd)) {
746     ReportCompressionPage(fd); 
747     ReadPosition(fd); 
748   }
749
750
751   exit(0);
752 }
753