8992fa24d49287098640b2ed03f5e5def0979c90
[debian/mtx] / loaderinfo.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  */
5
6 /* 
7 * $Date: 2007-03-24 18:38:20 -0700 (Sat, 24 Mar 2007) $
8 * $Revision: 167 $
9 */
10
11 /* What this does: Basically dumps out contents of:
12  *  Mode Sense: Element Address Assignment Page (0x1d)
13  *           1Eh (Transport Geometry Parameters) has a bit which indicates is 
14  *               a robot is  capable of rotating the media. It`s the 
15  *                `Rotate` bit, byte 2, bit 1.
16  *          Device Capabilities page (0x1f)
17  * Inquiry -- prints full inquiry info. 
18  *   DeviceType:
19  *    Manufacturer:
20  *    ProdID:  
21  *    ProdRevision:
22  *   If there is a byte 55, we use the Exabyte extension to 
23  * print out whether we have a bar code reader or not.  This is
24  * bit 0 of byte 55. 
25  *
26  * Next, we request element status on the drives. We do not
27  * request volume tags though. If Exabyte
28  * extensions are supported, we report the following information for
29  * each drive:
30  *
31  *  Drive number
32  *  EXCEPT (with ASC and ASCQ), if there is a problem. 
33  *  SCSI address and LUN
34  *  Tape drive Serial number
35  *   
36  */
37
38 #include <stdio.h>
39 #include "mtx.h"
40 #include "mtxl.h"
41
42 DEVICE_TYPE MediumChangerFD;  /* historic purposes... */
43
44 char *argv0;
45
46 /* A table for printing out the peripheral device type as ASCII. */ 
47 static char *PeripheralDeviceType[32] =
48 {
49         "Disk Drive",
50         "Tape Drive",
51         "Printer",
52         "Processor",
53         "Write-once",
54         "CD-ROM",
55         "Scanner",
56         "Optical",
57         "Medium Changer",
58         "Communications",
59         "ASC IT8",
60         "ASC IT8",
61         "RAID Array",
62         "Enclosure Services",
63         "OCR/W",
64         "Bridging Expander", /* 0x10 */
65         "Reserved",  /* 0x11 */
66         "Reserved", /* 0x12 */
67         "Reserved",  /* 0x13 */
68         "Reserved",  /* 0x14 */
69         "Reserved",  /* 0x15 */
70         "Reserved",  /* 0x16 */
71         "Reserved",  /* 0x17 */
72         "Reserved",  /* 0x18 */
73         "Reserved",  /* 0x19 */
74         "Reserved",  /* 0x1a */
75         "Reserved",  /* 0x1b */
76         "Reserved",  /* 0x1c */
77         "Reserved",  /* 0x1d */
78         "Reserved",  /* 0x1e */
79         "Unknown"    /* 0x1f */
80 };
81
82
83 /* okay, now for the structure of an Element Address Assignment Page:  */
84
85 typedef struct EAAP
86 {
87         unsigned char Page_Code;
88         unsigned char Parameter_Length;
89         unsigned char MediumTransportElementAddress[2];
90         unsigned char NumMediumTransportElements[2];
91         unsigned char FirstStorageElementAdddress[2];
92         unsigned char NumStorageElements[2];
93         unsigned char FirstImportExportElementAddress[2];
94         unsigned char NumImportExportElements[2];
95         unsigned char FirstDataTransferElementAddress[2];
96         unsigned char NumDataTransferElements[2];
97         unsigned char Reserved[2];
98 }       EAAP_Type;
99
100 /* okay, now for the structure of a transport geometry
101  * descriptor page:
102  */
103 typedef struct TGDP
104 {
105         unsigned char Page_Code;
106         unsigned char ParameterLength;
107         unsigned char Rotate;
108         unsigned char ElementNumber;  /* we don't care about this... */
109 }       TGDP_Type;
110
111
112 /* Structure of the Device Capabilities Page: */
113 typedef struct DCP 
114 {
115         unsigned char Page_Code;
116         unsigned char ParameterLength;
117         unsigned char CanStore;         /* bits about whether elements can store carts */
118         unsigned char SMC2_Caps;
119         unsigned char MT_Transfer;      /* bits about whether mt->xx transfers work. */
120         unsigned char ST_Transfer;      /* bits about whether st->xx transfers work. */
121         unsigned char IE_Transfer;      /* bits about whether id->xx transfers work. */
122         unsigned char DT_Transfer;      /* bits about whether DT->xx transfers work. */
123         unsigned char Reserved[4];      /* more reserved data */
124         unsigned char MT_Exchange;      /* bits about whether mt->xx exchanges work. */
125         unsigned char ST_Exchange;      /* bits about whether st->xx exchanges work. */
126         unsigned char IE_Exchange;      /* bits about whether id->xx exchanges work. */
127         unsigned char DT_Exchange;      /* bits about whether DT->xx exchanges work. */
128         unsigned char Reserved2[4];     /* more reserved data */
129 }       DCP_Type;
130
131 #define MT_BIT 0x01
132 #define ST_BIT 0x02
133 #define IE_BIT 0x04
134 #define DT_BIT 0x08
135
136 /* Okay, now for the inquiry information: */
137
138 static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
139 {
140         RequestSense_T RequestSense;
141         Inquiry_T *Inquiry;
142         int i;
143
144         Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
145         if (Inquiry == NULL) 
146         {
147                 PrintRequestSense(&RequestSense);
148                 FatalError("INQUIRY Command Failed\n");
149         }
150
151         printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
152
153         printf("Vendor ID: '");
154         for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
155                 printf("%c", Inquiry->VendorIdentification[i]);
156
157         printf("'\nProduct ID: '");
158         for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
159                 printf("%c", Inquiry->ProductIdentification[i]);
160
161         printf("'\nRevision: '");
162         for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
163                 printf("%c", Inquiry->ProductRevisionLevel[i]);
164
165         printf("'\n");
166
167         if (Inquiry->MChngr) 
168         {
169                 /* check the attached-media-changer bit... */
170                 printf("Attached Changer: Yes\n");
171         }
172         else
173         {
174                 printf("Attached Changer: No\n");
175         }
176
177         /* Now see if we have a bar code flag: */
178         if (Inquiry->AdditionalLength > 50) 
179         {
180                 /* see if we have 56 bytes: */
181                 if (Inquiry->VendorFlags & 1)
182                 {
183                         printf("Bar Code Reader: Yes\n");
184                 }
185                 else
186                 {
187                         printf("Bar Code Reader: No\n");
188                 }
189         }
190
191         free(Inquiry);          /* well, we're about to exit, but ... */
192 }
193
194 /*********** MODE SENSE *******************/
195 /* We need 3 different mode sense pages. This is a generic
196  * routine for obtaining mode sense pages. 
197  */
198
199 static unsigned char
200 *mode_sense(DEVICE_TYPE fd, char pagenum, int alloc_len,  RequestSense_T *RequestSense)
201 {
202         CDB_T CDB;
203         unsigned char *input_buffer;    /*the input buffer -- has junk prepended to
204                                                                          * actual sense page. 
205                                                                          */
206         unsigned char *tmp;
207         unsigned char *retval;                  /* the return value. */
208         int i,pagelen;
209
210         if (alloc_len > 255)
211         {
212                 FatalError("mode_sense(6) can only read up to 255 characters!\n");
213         }
214
215         input_buffer = (unsigned char *)xzmalloc(256); /* overdo it, eh? */
216
217         /* clear the sense buffer: */
218         slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
219
220         /* returns an array of bytes in the page, or, if not possible, NULL. */
221         CDB[0] = 0x1a; /* Mode Sense(6) */
222         CDB[1] = 0x08; 
223         CDB[2] = pagenum; /* the page to read. */
224         CDB[3] = 0;
225         CDB[4] = 255; /* allocation length. This does max of 256 bytes! */
226         CDB[5] = 0;
227
228         if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
229                                                         input_buffer, 255, RequestSense) != 0)
230         {
231 #ifdef DEBUG_MODE_SENSE
232                 fprintf(stderr,"Could not execute mode sense...\n");
233                 fflush(stderr);
234 #endif
235                 return NULL; /* sorry, couldn't do it. */
236         }
237
238         /* First skip past any header.... */
239         tmp = input_buffer + 4 + input_buffer[3];
240         /* now find out real length of page... */
241         pagelen=tmp[1] + 2;
242         retval = xmalloc(pagelen);
243         /* and copy our data to the new page. */
244         for (i = 0; i < pagelen; i++)
245         {
246                 retval[i] = tmp[i];
247         }
248         /* okay, free our input buffer: */
249         free(input_buffer);
250         return retval;
251 }
252
253 /* Report the Element Address Assignment Page */
254 static void ReportEAAP(DEVICE_TYPE MediumChangerFD)
255 {
256         EAAP_Type *EAAP; 
257         RequestSense_T RequestSense;
258
259         EAAP = (EAAP_Type *)mode_sense(MediumChangerFD, 0x1d, sizeof(EAAP_Type), &RequestSense);
260
261         if (EAAP == NULL)
262         {
263                 PrintRequestSense(&RequestSense);
264                 printf("EAAP: No\n");
265                 return;
266         }
267
268         /* we did get an EAAP, so do our thing: */
269         printf("EAAP: Yes\n");
270         printf("Number of Medium Transport Elements: %d\n", ( ((unsigned int)EAAP->NumMediumTransportElements[0]<<8) + (unsigned int)EAAP->NumMediumTransportElements[1]));
271         printf("Number of Storage Elements: %d\n", ( ((unsigned int)EAAP->NumStorageElements[0]<<8) + (unsigned int)EAAP->NumStorageElements[1]));
272         printf("Number of Import/Export Elements: %d\n", ( ((unsigned int)EAAP->NumImportExportElements[0]<<8) + (unsigned int)EAAP->NumImportExportElements[1]));
273         printf("Number of Data Transfer Elements: %d\n", ( ((unsigned int)EAAP->NumDataTransferElements[0]<<8) + (unsigned int)EAAP->NumDataTransferElements[1]));
274
275         free(EAAP);
276 }
277
278 /* See if we can get some invert information: */
279
280 static void Report_TGDP(DEVICE_TYPE MediumChangerFD)
281 {
282         TGDP_Type *result;
283
284         RequestSense_T RequestSense;
285
286         result=(TGDP_Type *)mode_sense(MediumChangerFD,0x1e,255,&RequestSense);
287
288         if (!result)
289         {
290                 printf("Transport Geometry Descriptor Page: No\n");
291                 return;
292         }
293
294         printf("Transport Geometry Descriptor Page: Yes\n");
295
296         /* Now print out the invert bit: */
297         if ( result->Rotate & 1 )
298         {
299                 printf("Invertable: Yes\n");
300         }
301         else
302         {
303                 printf("Invertable: No\n");
304         }
305
306         free(result);
307 }
308
309 /* Okay, let's get the Device Capabilities Page. We don't care
310  * about much here, just whether 'mtx transfer' will work (i.e., 
311  * ST->ST).
312  */
313
314 void TransferExchangeTargets(unsigned char ucValue, char *szPrefix)
315 {
316         if (ucValue & DT_BIT)
317         {
318                 printf("%sData Transfer", szPrefix);
319         }
320
321         if (ucValue & IE_BIT)
322         {
323                 printf("%s%sImport/Export", ucValue > (IE_BIT | (IE_BIT - 1)) ? ", " : "", szPrefix);
324         }
325
326         if (ucValue & ST_BIT)
327         {
328                 printf("%s%sStorage", ucValue > (ST_BIT | (ST_BIT - 1)) ? ", " : "", szPrefix);
329         }
330
331         if (ucValue & MT_BIT)
332         {
333                 printf("%s%sMedium Transfer", ucValue  > (MT_BIT | (MT_BIT - 1)) ? ", " : "", szPrefix);
334         }
335 }
336
337 static void Report_DCP(DEVICE_TYPE MediumChangerFD)
338 {
339         DCP_Type *result;
340         RequestSense_T RequestSense;
341
342         /* Get the page. */
343         result=(DCP_Type *)mode_sense(MediumChangerFD,0x1f,sizeof(DCP_Type),&RequestSense);
344         if (!result) 
345         {
346                 printf("Device Configuration Page: No\n");
347                 return;
348         }
349
350         printf("Device Configuration Page: Yes\n");
351
352         printf("Storage: ");
353
354         if (result->CanStore & DT_BIT)
355         {
356                 printf("Data Transfer");
357         }
358
359         if (result->CanStore & IE_BIT)
360         {
361                 printf("%sImport/Export", result->CanStore > (IE_BIT | (IE_BIT - 1)) ? ", " : "");
362         }
363
364         if (result->CanStore & ST_BIT)
365         {
366                 printf("%sStorage", result->CanStore > (ST_BIT | (ST_BIT - 1)) ? ", " : "");
367         }
368
369         if (result->CanStore & MT_BIT)
370         {
371                 printf("%sMedium Transfer", result->CanStore > (MT_BIT | (MT_BIT - 1)) ? ", " : "");
372         }
373
374         printf("\n");
375
376         printf("SCSI Media Changer (rev 2): ");
377
378         if (result->SMC2_Caps & 0x01)
379         {
380                 printf("Yes\n");
381
382                 printf("Volume Tag Reader Present: %s\n", result->SMC2_Caps & 0x02 ? "Yes" : "No");
383                 printf("Auto-Clean Enabled: %s\n", result->SMC2_Caps & 0x04 ? "Yes" : "No");
384         }
385         else
386         {
387                 printf("No\n");
388         }
389
390         printf("Transfer Medium Transport: ");
391         if ((result->MT_Transfer & 0x0F) != 0)
392         {
393                 TransferExchangeTargets(result->MT_Transfer, "->");
394         }
395         else
396         {
397                 printf("None");
398         }
399
400         printf("\nTransfer Storage: ");
401         if ((result->ST_Transfer & 0x0F) != 0)
402         {
403                 TransferExchangeTargets(result->ST_Transfer, "->");
404         }
405         else
406         {
407                 printf("None");
408         }
409
410         printf("\nTransfer Import/Export: ");
411         if ((result->IE_Transfer & 0x0F) != 0)
412         {
413                 TransferExchangeTargets(result->IE_Transfer, "->");
414         }
415         else
416         {
417                 printf("None");
418         }
419
420         printf("\nTransfer Data Transfer: ");
421         if ((result->DT_Transfer & 0x0F) != 0)
422         {
423                 TransferExchangeTargets(result->DT_Transfer, "->");
424         }
425         else
426         {
427                 printf("None");
428         }
429
430         printf("\nExchange Medium Transport: ");
431         if ((result->MT_Exchange & 0x0F) != 0)
432         {
433                 TransferExchangeTargets(result->MT_Exchange, "<>");
434         }
435         else
436         {
437                 printf("None");
438         }
439
440         printf("\nExchange Storage: ");
441         if ((result->ST_Exchange & 0x0F) != 0)
442         {
443                 TransferExchangeTargets(result->ST_Exchange, "<>");
444         }
445         else
446         {
447                 printf("None");
448         }
449
450         printf("\nExchange Import/Export: ");
451         if ((result->IE_Exchange & 0x0F) != 0)
452         {
453                 TransferExchangeTargets(result->IE_Exchange, "<>");
454         }
455         else
456         {
457                 printf("None");
458         }
459
460         printf("\nExchange Data Transfer: ");
461         if ((result->DT_Exchange & 0x0F) != 0)
462         {
463                 TransferExchangeTargets(result->DT_Exchange, "<>");
464         }
465         else
466         {
467                 printf("None");
468         }
469
470         printf("\n");
471
472         free(result);
473 }
474
475 void usage(void)
476 {
477         FatalError("Usage: loaderinfo -f <generic-device>\n");
478 }
479
480
481 /* we only have one argument: "-f <device>". */
482 int main(int argc, char **argv)
483 {
484         DEVICE_TYPE fd;
485         char *filename;
486
487         argv0=argv[0];
488         if (argc != 3)
489         {
490                 fprintf(stderr,"argc=%d",argc);
491                 usage();
492         }
493
494         if (strcmp(argv[1],"-f")!=0)
495         {
496                 usage();
497         }
498
499         filename=argv[2];
500
501         fd=SCSI_OpenDevice(filename);
502
503         /* Now to call the various routines: */
504         ReportInquiry(fd);
505         ReportEAAP(fd);
506         Report_TGDP(fd);
507         Report_DCP(fd);
508         exit(0);
509 }