1 /* Copyright 2000 Enhanced Software Technologies Inc.
2 * Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
3 * Released under terms of the GNU General Public License as
4 * required by the license on 'mtxl.c'.
5 * $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
9 /*#define DEBUG_PARTITION */
12 /* What this does: This basically dumps out the contents of the following
15 * Inquiry -- prints full inquiry info. If it's not a tape drive, this is
22 * Log Sense: TapeAlert Page (if supported):
23 * TapeAlert:[message#]<Message> e.g. "TapeAlert:[22]Cleaning Cartridge Worn Out"
26 * Data Compression Page:
27 * DataCompEnabled:<yes|no>
28 * DataCompCapable:<yes|no>
29 * DataDeCompEnabled:<yes|no>
33 * Device Configuration Page:
35 * DevConfigComp:<#> -- the compression byte in device config page.
36 * EarlyWarningSize:<#> -- size of early warning buffer?
38 * Medium Partition Page:
42 * Partition[1]:<size>...
44 * Read Block Limits command:
45 * MinBlock:<#> -- Minimum block size.
46 * MaxBlock:<#> -- Maximum block size.
58 FatalError("Usage: tapeinfo -f <generic-device>\n");
61 /* A table for printing out the peripheral device type as ASCII. */
62 static char *PeripheralDeviceType[32] =
79 "Bridging Expander", /* 0x10 */
80 "Reserved", /* 0x11 */
81 "Reserved", /* 0x12 */
82 "Reserved", /* 0x13 */
83 "Reserved", /* 0x14 */
84 "Reserved", /* 0x15 */
85 "Reserved", /* 0x16 */
86 "Reserved", /* 0x17 */
87 "Reserved", /* 0x18 */
88 "Reserved", /* 0x19 */
89 "Reserved", /* 0x1a */
90 "Reserved", /* 0x1b */
91 "Reserved", /* 0x1c */
92 "Reserved", /* 0x1d */
93 "Reserved", /* 0x1e */
99 /* we call it MediumChangerFD for history reasons, sigh. */
101 /* now to print inquiry information: Copied from other one.... */
103 static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
105 RequestSense_T RequestSense;
109 Inquiry = RequestInquiry(MediumChangerFD, &RequestSense);
112 PrintRequestSense(&RequestSense);
113 FatalError("INQUIRY Command Failed\n");
116 printf("Product Type: %s\n", PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
118 printf("Vendor ID: '");
119 for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
120 printf("%c", Inquiry->VendorIdentification[i]);
122 printf("'\nProduct ID: '");
123 for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
124 printf("%c", Inquiry->ProductIdentification[i]);
126 printf("'\nRevision: '");
127 for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
128 printf("%c", Inquiry->ProductRevisionLevel[i]);
133 /* check the attached-media-changer bit... */
134 printf("Attached Changer API: Yes\n");
138 printf("Attached Changer API: No\n");
141 free(Inquiry); /* well, we're about to exit, but ... */
147 /* Okay, now for the Log Sense Tape Alert Page (if supported): */
148 #define TAPEALERT_SIZE 2048 /* max size of tapealert buffer. */
149 #define MAX_TAPE_ALERT 0x41
151 static char *tapealert_messages[] =
154 " Read: Having problems reading (slowing down)", /* 1 */
155 " Write: Having problems writing (losing capacity)", /* 2 */
156 " Hard Error: Uncorrectable read/write error", /* 3 */
157 " Media: Media Performance Degraded, Data Is At Risk", /* 4 */
158 " Read Failure: Tape faulty or tape drive broken", /* 5 */
159 "Write Failure: Tape faulty or tape drive broken", /* 6 */
160 " Media Life: The tape has reached the end of its useful life", /* 7 */
161 "Not Data Grade:Replace cartridge with one containing data grade tape",/*8*/
162 "Write Protect: Attempted to write to a write-protected cartridge",/*9 */
163 " No Removal: Cannot unload, initiator is preventing media removal", /*a*/
164 "Cleaning Media:Cannot back up or restore to a cleaning cartridge", /* b */
165 " Bad Format: The loaded tape contains data in an unsupported format", /*c */
166 " Snapped Tape: The data cartridge contains a broken tape", /* d */
169 "Undefined", /* 10 */
170 "Undefined", /* 11 */
171 "Undefined", /* 12 */
172 "Undefined", /* 13 */
173 " Clean Now: The tape drive neads cleaning NOW", /* 0x14 */
174 "Clean Periodic:The tape drive needs to be cleaned at next opportunity", /* 0x15 */
175 "Cleaning Media:Cannot clean because cleaning cartridge used up, insert new cleaning cartridge to clean the drive", /* 0x16 */
176 "Undefined", /* 0x17 */
177 "Undefined", /* 0x18 */
178 "Undefined", /* 0x19 */
179 "Undefined", /* 0x1a */
180 "Undefined", /* 0x1b */
181 "Undefined", /* 0x1c */
182 "Undefined", /* 0x1d */
183 " Hardware A: Tape drive has a problem not read/write related", /* 0x1e */
184 " Hardware B: Tape drive has a problem not read/write related", /* 0x1f */
185 " Interface: Problem with SCSI interface between tape drive and initiator", /* 0x20 */
186 " Eject Media: The current operation has failed. Eject and reload media", /* 0x21 */
187 "Download Fail: Attempt to download new firmware failed", /* 0x22 */
188 "Undefined", /* 0x23 */
189 "Undefined", /* 0x24 */
190 "Undefined", /* 0x25 */
191 "Undefined", /* 0x26 */
192 "Undefined", /* 0x27 */
193 "Loader Hardware A: Changer having problems communicating with tape drive", /* 0x28 40 */
194 "Loader Stray Tape: Stray tape left in drive from prior error", /* 0x29 41 */
195 "Loader Hardware B: Autoloader mechanism has a fault", /* 0x2a 42 */
196 " Loader Door: Loader door is open, please close it", /* 0x2b 43 */
197 "Undefined", /* 0x2c */
198 "Undefined", /* 0x2d */
199 "Undefined", /* 0x2e */
200 "Undefined", /* 0x2f */
201 "Undefined", /* 0x30 */
202 "Undefined", /* 0x31 */
203 "Undefined", /* 0x32 */
204 "Undefined", /* 0x33 */
205 "Undefined", /* 0x34 */
206 "Undefined", /* 0x35 */
207 "Undefined", /* 0x36 */
208 "Undefined", /* 0x37 */
209 "Undefined", /* 0x38 */
210 "Undefined", /* 0x39 */
211 "Undefined", /* 0x3a */
212 "Undefined", /* 0x3b */
213 "Undefined", /* 0x3c */
214 "Undefined", /* 0x3d */
215 "Undefined", /* 0x3e */
216 "Undefined", /* 0x3f */
217 "Undefined" /* 0x40 */
220 typedef struct TapeCapacityStruct
222 unsigned int partition0_remaining;
223 unsigned int partition1_remaining;
224 unsigned int partition0_size;
225 unsigned int partition1_size;
230 static void dump_data(unsigned char *data, int len)
234 fprintf(stderr,"DATA:");
235 PrintHex(1, data, len);
239 fprintf(stderr, "**NO DATA**\n");
245 /* Request the tape capacity page defined by some DAT autoloaders. */
247 static TapeCapacity *RequestTapeCapacity(DEVICE_TYPE fd, RequestSense_T *sense)
250 TapeCapacity *result;
253 unsigned char buffer[TAPEALERT_SIZE]; /* Overkill, but ... */
255 slow_bzero((char *)buffer,TAPEALERT_SIZE); /*zero it... */
257 /* now to create the CDB block: */
258 CDB[0] = 0x4d; /* Log Sense */
260 CDB[2] = 0x31; /* Tape Capacity Page. */
265 CDB[7] = TAPEALERT_SIZE >> 8 & 0xff; /* hi byte, allocation size */
266 CDB[8] = TAPEALERT_SIZE & 0xff; /* lo byte, allocation size */
267 CDB[9] = 0; /* reserved */
269 if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, TAPEALERT_SIZE, sense) != 0)
271 /* fprintf(stderr,"RequestTapeCapacity: Command failed: Log Sense\n"); */
275 /* dump_data(buffer,64); */
277 /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
278 * byte 0 should be 0x31, byte 1 == 0, bytes 2,3 tell how long the
281 if ((buffer[0]&0x3f) != 0x31)
283 /* fprintf(stderr,"RequestTapeCapacity: Invalid header for page (not 0x31).\n"); */
287 result_len = ((int)buffer[2] << 8) + buffer[3];
289 if (result_len != 32)
291 /* fprintf(stderr,"RequestTapeCapacity: Page was %d bytes long, not 32 bytes\n",result_len); */
292 return NULL; /* This Is Not The Page You're Looking For */
295 result = xmalloc(sizeof(TapeCapacity));
297 /* okay, now allocate data and move the buffer over there: */
299 /* 0 1 2 3 4 5 6 7 8 9
300 DATA: 31 00 00 20 00 01 4c 04 01 3a
301 10 11 12 13 14 15 16 17 18 19
302 DATA: 81 0c 00 02 4c 04 00 00 00 00
303 20 21 22 23 24 25 26 27 28 29
304 DATA: 00 03 4c 04 01 3f 4b 1f 00 04
306 DATA: 4c 04 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 00 00 00 00 00 00
312 result->partition0_remaining =
313 ((unsigned int)buffer[8] << 24) +
314 ((unsigned int)buffer[9] << 16) +
315 ((unsigned int)buffer[10] << 8) +
318 result->partition1_remaining =
319 ((unsigned int)buffer[16] << 24) +
320 ((unsigned int)buffer[17] << 16) +
321 ((unsigned int)buffer[18] << 8) +
324 result->partition0_size =
325 ((unsigned int)buffer[24] << 24) +
326 ((unsigned int)buffer[25] << 16) +
327 ((unsigned int)buffer[26] << 8) +
330 result->partition1_size =
331 ((unsigned int)buffer[32] << 24) +
332 ((unsigned int)buffer[33] << 16) +
333 ((unsigned int)buffer[34] << 8) +
341 struct tapealert_struct
347 static struct tapealert_struct *RequestTapeAlert(DEVICE_TYPE fd, RequestSense_T *sense)
351 struct tapealert_struct *result;
352 int i, tapealert_len, result_idx;
354 unsigned char buffer[TAPEALERT_SIZE];
355 unsigned char *walkptr;
357 slow_bzero((char *)buffer, TAPEALERT_SIZE); /*zero it... */
359 /* now to create the CDB block: */
360 CDB[0] = 0x4d; /* Log Sense */
362 CDB[2] = 0x2e; /* Tape Alert Page. */
367 CDB[7] = TAPEALERT_SIZE >> 8 & 0xff; /* hi byte, allocation size */
368 CDB[8] = TAPEALERT_SIZE & 0xff; /* lo byte, allocation size */
369 CDB[9] = 0; /* reserved */
371 if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,TAPEALERT_SIZE,sense)!=0)
376 result = xmalloc(sizeof(struct tapealert_struct));
378 /* okay, we have stuff in the result buffer: the first 4 bytes are a header:
379 * byte 0 should be 0x2e, byte 1 == 0, bytes 2,3 tell how long the
382 if ((buffer[0]&0x3f) != 0x2e)
389 tapealert_len = ((int)buffer[2] << 8) + buffer[3];
398 /* okay, now allocate data and move the buffer over there: */
399 result->length = MAX_TAPE_ALERT;
400 result->data = xzmalloc(MAX_TAPE_ALERT); /* alloc & zero. */
402 walkptr = &buffer[4];
405 while (i < tapealert_len)
407 result_idx=(((int)walkptr[0])<<8) + walkptr[1]; /* the parameter #. */
408 if (result_idx > 0 && result_idx < MAX_TAPE_ALERT)
412 result->data[result_idx] = 1;
416 result->data[result_idx] = 0;
419 fprintf(stderr,"Alert[0x%x]=%d\n",result_idx,result->data[result_idx]);
425 FatalError("Invalid tapealert page: %d\n",result_idx);
428 i = i + 4 + walkptr[3]; /* length byte! */
429 walkptr = walkptr + 4 + walkptr[3]; /* next! */
434 static void ReportTapeCapacity(DEVICE_TYPE fd)
436 /* we actually ignore a bad sense reading, like might happen if the
437 * tape drive does not support the tape capacity page.
440 RequestSense_T RequestSense;
442 TapeCapacity *result;
444 result=RequestTapeCapacity(fd,&RequestSense);
449 printf("Partition 0 Remaining Kbytes: %d\n", result->partition0_remaining);
450 printf("Partition 0 Size in Kbytes: %d\n", result->partition0_size);
452 if (result->partition1_size)
454 printf("Partition 1 Remaining Kbytes: %d\n", result->partition1_remaining);
455 printf("Partition 1 Size in Kbytes: %d\n", result->partition1_size);
463 static void ReportTapeAlert(DEVICE_TYPE fd)
465 /* we actually ignore a bad sense reading, like might happen if the
466 * tape drive does not support the tapealert page.
469 RequestSense_T RequestSense;
471 struct tapealert_struct *result;
474 result=RequestTapeAlert(fd,&RequestSense);
477 return; /* sorry. Don't print sense here. */
480 return; /* sorry, no alerts valid. */
482 for (i = 0; i < result->length; i++)
486 printf("TapeAlert[%d]: %s.\n", i, tapealert_messages[i]);
495 *mode_sense(DEVICE_TYPE fd, char pagenum, int alloc_len, RequestSense_T *RequestSense)
499 unsigned char *input_buffer;
501 unsigned char *retval;
506 FatalError("mode_sense(6) can only read up to 255 characters!\n");
509 input_buffer = (unsigned char *)xzmalloc(256); /* overdo it, eh? */
511 /* clear the sense buffer: */
512 slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
514 /* returns an array of bytes in the page, or, if not possible, NULL. */
515 CDB[0] = 0x1a; /* Mode Sense(6) */
517 CDB[2] = pagenum; /* the page to read. */
519 CDB[4] = 255; /* allocation length. This does max of 256 bytes! */
522 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, input_buffer, 255, RequestSense) != 0)
524 #ifdef DEBUG_MODE_SENSE
525 fprintf(stderr,"Could not execute mode sense...\n");
528 return NULL; /* sorry, couldn't do it. */
531 /* Oh hell, write protect is the only thing I have: always print
532 * it if our mode page was 0x0fh, before skipping past buffer:
533 * if the media is *NOT* write protected, just skip, sigh.
535 * Oh poops, the blocksize is reported in the block descriptor header
536 * < * too. Again, just print if our mode page was 0x0f...
542 if (input_buffer[2] & 0x80)
544 printf("WriteProtect: yes\n");
547 if (input_buffer[2] & 0x70)
549 printf("BufferedMode: yes\n");
552 if (input_buffer[1] )
554 printf("Medium Type: 0x%x\n", input_buffer[1]);
558 printf("Medium Type: Not Loaded\n");
561 printf("Density Code: 0x%x\n", input_buffer[4]);
562 /* Put out the block size: */
564 blocklen = ((int)input_buffer[9] << 16)+
565 ((int)input_buffer[10] << 8)+
568 printf("BlockSize: %d\n", blocklen);
571 /* First skip past any header.... */
572 tmp = input_buffer + 4 + input_buffer[3];
574 /* now find out real length of page... */
575 pagelen = tmp[1] + 2;
576 retval = xmalloc(pagelen);
578 /* and copy our data to the new page. */
579 for (i=0;i<pagelen;i++)
584 /* okay, free our input buffer: */
590 #define DCE_MASK 0x80
591 #define DCC_MASK 0x40
592 #define DDE_MASK 0x80
594 static void ReportCompressionPage(DEVICE_TYPE fd)
596 /* actually ignore a bad sense reading, like might happen if the tape
597 * drive does not support the mode sense compression page.
600 RequestSense_T RequestSense;
602 unsigned char *compression_page;
604 compression_page=mode_sense(fd,0x0f,16,&RequestSense);
606 if (!compression_page)
611 /* Okay, we now have the compression page. Now print stuff from it: */
612 printf("DataCompEnabled: %s\n", (compression_page[2] & DCE_MASK)? "yes" : "no");
613 printf("DataCompCapable: %s\n", (compression_page[2] & DCC_MASK)? "yes" : "no");
614 printf("DataDeCompEnabled: %s\n", (compression_page[3] & DDE_MASK)? "yes" : "no");
615 printf("CompType: 0x%x\n",
616 (compression_page[4] << 24) +
617 (compression_page[5] << 16) +
618 (compression_page[6] << 8) +
619 compression_page[7]);
621 printf("DeCompType: 0x%x\n",
622 (compression_page[8] << 24) +
623 (compression_page[9] << 16) +
624 (compression_page[10] << 8) +
625 compression_page[11]);
627 free(compression_page);
630 /* Now for the device configuration mode page: */
632 static void ReportConfigPage(DEVICE_TYPE fd)
634 RequestSense_T RequestSense;
635 unsigned char *config_page;
637 config_page = mode_sense(fd, 0x10, 16, &RequestSense);
641 /* Now to print the stuff: */
642 printf("ActivePartition: %d\n", config_page[3]);
644 /* The following does NOT work accurately on any tape drive I know of... */
645 /* printf("DevConfigComp: %s\n", config_page[14] ? "yes" : "no"); */
646 printf("EarlyWarningSize: %d\n",
647 (config_page[11] << 16) +
648 (config_page[12] << 8) +
652 /* ***************************************
653 * Medium Partition Page:
655 * The problem here, as we oh so graphically demonstrated during debugging
656 * of the Linux 'st' driver :-), is that there are actually *TWO* formats for
657 * the Medium Partition Page: There is the "long" format, where there is a
658 * partition size word for each partition on the page, and there is a "short"
659 * format, beloved of DAT drives, which only has a partition size word for
660 * partition #1 (and no partition size word for partition #0, and no
661 * provisions for any more partitions). So we must look at the size and
662 * # of partitions defined to know what to report as what.
664 ********************************************/
666 static void ReportPartitionPage(DEVICE_TYPE fd)
668 RequestSense_T RequestSense;
669 unsigned char *partition_page;
671 int num_parts,max_parts;
674 partition_page=mode_sense(fd,0x11,255,&RequestSense);
678 /* Okay, now we have either old format or new format: */
679 num_parts = partition_page[3];
680 max_parts = partition_page[2];
682 printf("NumPartitions: %d\n", num_parts);
683 printf("MaxPartitions: %d\n", max_parts);
687 /* if no additional partitions, then ... */
688 free(partition_page);
692 /* we know we have at least one partition if we got here. Check the
693 * page size field. If it is 8 or below, then we are the old format....
696 #ifdef DEBUG_PARTITION
697 fprintf(stderr,"partition_page[1]=%d\n",partition_page[1]);
700 if (partition_page[1]==8)
703 printf("Partition1: %d\n",(partition_page[8]<<8)+partition_page[9]);
708 for (i=0;i<=max_parts;i++)
710 #ifdef DEBUG_PARTITION
711 fprintf(stderr,"partition%d:[%d]%d [%d]%d\n", i, 8 + i * 2,
712 partition_page[8+i*2]<<8, 9+i*2,partition_page[9 + i * 2]);
715 printf("Partition%d: %d\n", i,
716 (partition_page[8 + i * 2] << 8) + partition_page[9 + i * 2]);
719 free(partition_page);
722 static void ReportSerialNumber(DEVICE_TYPE fd)
724 /* Actually ignore a bad sense reading, like might happen if the
725 tape drive does not support the inquiry page 0x80.
728 RequestSense_T sense;
731 #define WILD_SER_SIZE 30
732 unsigned char buffer[WILD_SER_SIZE]; /* just wildly overestimate serial# length! */
737 CDB[0] = 0x12; /* INQUIRY */
738 CDB[1] = 1; /* EVPD = 1 */
739 CDB[2] = 0x80; /* The serial # page, hopefully. */
740 CDB[3] = 0; /* reserved */
741 CDB[4] = WILD_SER_SIZE;
744 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, &buffer, sizeof(buffer), &sense) != 0)
746 /* PrintRequestSense(&sense); */ /* zap debugging output :-) */
747 /* printf("No Serial Number: None\n"); */
751 /* okay, we have something in our buffer. Byte 3 should be the length of
752 the sernum field, and bytes 4 onward are the serial #. */
754 lim = (int)buffer[3];
755 bufptr = (char *)&(buffer[4]);
757 printf("SerialNumber: '");
765 /* Read Block Limits! */
767 void ReportBlockLimits(DEVICE_TYPE fd)
769 RequestSense_T sense;
771 unsigned char buffer[6];
773 CDB[0] = 0x05; /* READ_BLOCK_LIMITS */
776 CDB[3] = 0; /* 1-5 all unused. */
780 slow_bzero((char *)&sense,sizeof(RequestSense_T));
781 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 6, &sense) != 0)
786 /* okay, but if we did get a result, print it: */
787 printf("MinBlock: %d\n", (buffer[4] << 8) + buffer[5]);
788 printf("MaxBlock: %d\n", (buffer[1] << 16) + (buffer[2]<<8) + buffer[3]);
791 /* Do a READ_POSITION. This may not be always valid, but (shrug). */
792 void ReadPosition(DEVICE_TYPE fd)
794 RequestSense_T sense;
796 unsigned char buffer[20];
797 unsigned int position;
799 CDB[0] = 0x34; /* READ_POSITION */
802 CDB[3] = 0; /* 1-9 all unused. */
810 slow_bzero((char *)&sense, sizeof(RequestSense_T));
812 SCSI_Set_Timeout(2); /* set timeout to 2 seconds! */
814 /* if we don't get a result (e.g. we issue this to a disk drive), punt. */
815 if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, 20, &sense) != 0)
820 SCSI_Default_Timeout(); /* reset it to 5 minutes, sigh! */
821 /* okay, but if we did get a result, print it: */
826 #define RBL_BYCU 0x10
829 #define RBL_PERR 0x02
831 /* If we have BOP, go ahead and print that. */
832 if (buffer[0]&RBL_BOP)
834 printf("BOP: yes\n");
837 /* if we have valid data, print it: */
838 if (buffer[0]&RBL_BPU)
840 printf("Block Position: -1");
844 position = (unsigned int)(((unsigned int)buffer[4] << 24) +
845 ((unsigned int)buffer[5] << 16) +
846 ((unsigned int)buffer[6] << 8) +
849 printf("Block Position: %d\n",position);
853 /* Test unit ready: This will tell us whether the tape drive
854 * is currently ready to read or write.
857 int TestUnitReady(DEVICE_TYPE fd)
859 RequestSense_T sense;
861 unsigned char buffer[6];
863 CDB[0] = 0x00; /* TEST_UNIT_READY */
866 CDB[3] = 0; /* 1-5 all unused. */
870 slow_bzero((char *)&sense,sizeof(RequestSense_T));
871 if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0)
873 printf("Ready: no\n");
877 printf("Ready: yes\n");
881 /* We write a filemarks of 0 before going to grab position, in order
882 * to insure that data in the buffer is not a problem.
885 int WriteFileMarks(DEVICE_TYPE fd,int count)
887 RequestSense_T sense;
889 unsigned char buffer[6];
891 CDB[0] = 0x10; /* WRITE_FILE_MARKS */
893 CDB[2] = (unsigned char)(count >> 16);
894 CDB[3] = (unsigned char)(count >> 8);
895 CDB[4] = (unsigned char)count;
898 /* we really don't care if this command works or not, sigh. */
899 slow_bzero((char *)&sense, sizeof(RequestSense_T));
900 if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 0, &sense) != 0)
909 /* This will get the SCSI ID and LUN of the target device, if such
910 * is available from the OS. Currently only Linux supports this,
911 * but other drivers could, if someone wants to write a
912 * SCSI_GetIDLun function for them.
914 #ifdef HAVE_GET_ID_LUN
916 static void ReportIDLun(DEVICE_TYPE fd)
920 scsi_id = SCSI_GetIDLun(fd);
921 printf("SCSI ID: %d\nSCSI LUN: %d\n", scsi_id->id, scsi_id->lun);
926 /* we only have one argument: "-f <device>". */
927 int main(int argc, char **argv)
936 fprintf(stderr,"argc=%d",argc);
940 if (strcmp(argv[1],"-f")!=0)
946 fd=SCSI_OpenDevice(filename);
948 /* Now to call the various routines: */
950 ReportSerialNumber(fd);
952 ReportBlockLimits(fd);
954 #ifdef HAVE_GET_ID_LUN
958 /* okay, we should only report position if the unit is ready :-(. */
959 if (TestUnitReady(fd))
961 ReportCompressionPage(fd);
963 ReportTapeCapacity(fd); /* only if we have it */
964 ReportConfigPage(fd); /* only valid if unit is ready. */
965 ReportPartitionPage(fd);