summaryrefslogtreecommitdiff
path: root/loaderinfo.c
blob: 11aaeacd06c4800977841fb28fed25a6bf8232e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
/* Copyright 2000 Enhanced Software Technologies Inc.
 * Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
 *   Released under terms of the GNU General Public License as
 * required by the license on 'mtxl.c'.
 */

/* 
* $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
* $Revision: 193 $
*/

/* What this does: Basically dumps out contents of:
 *  Mode Sense: Element Address Assignment Page (0x1d)
 *           1Eh (Transport Geometry Parameters) has a bit which indicates is 
 *               a robot is  capable of rotating the media. It`s the 
 *                `Rotate` bit, byte 2, bit 1.
 *          Device Capabilities page (0x1f)
 * Inquiry -- prints full inquiry info. 
 *   DeviceType:
 *    Manufacturer:
 *    ProdID:  
 *    ProdRevision:
 *   If there is a byte 55, we use the Exabyte extension to 
 * print out whether we have a bar code reader or not.  This is
 * bit 0 of byte 55. 
 *
 * Next, we request element status on the drives. We do not
 * request volume tags though. If Exabyte
 * extensions are supported, we report the following information for
 * each drive:
 *
 *  Drive number
 *  EXCEPT (with ASC and ASCQ), if there is a problem. 
 *  SCSI address and LUN
 *  Tape drive Serial number
 *   
 */

#include <stdio.h>
#include "mtx.h"
#include "mtxl.h"

DEVICE_TYPE MediumChangerFD;  /* historic purposes... */

char *argv0;

/* A table for printing out the peripheral device type as ASCII. */ 
static char *PeripheralDeviceType[32] =
{
	"Disk Drive",
	"Tape Drive",
	"Printer",
	"Processor",
	"Write-once",
	"CD-ROM",
	"Scanner",
	"Optical",
	"Medium Changer",
	"Communications",
	"ASC IT8",
	"ASC IT8",
	"RAID Array",
	"Enclosure Services",
	"OCR/W",
	"Bridging Expander", /* 0x10 */
	"Reserved",  /* 0x11 */
	"Reserved", /* 0x12 */
	"Reserved",  /* 0x13 */
	"Reserved",  /* 0x14 */
	"Reserved",  /* 0x15 */
	"Reserved",  /* 0x16 */
	"Reserved",  /* 0x17 */
	"Reserved",  /* 0x18 */
	"Reserved",  /* 0x19 */
	"Reserved",  /* 0x1a */
	"Reserved",  /* 0x1b */
	"Reserved",  /* 0x1c */
	"Reserved",  /* 0x1d */
	"Reserved",  /* 0x1e */
	"Unknown"    /* 0x1f */
};


/* okay, now for the structure of an Element Address Assignment Page:  */

typedef struct EAAP
{
	unsigned char Page_Code;
	unsigned char Parameter_Length;
	unsigned char MediumTransportElementAddress[2];
	unsigned char NumMediumTransportElements[2];
	unsigned char FirstStorageElementAdddress[2];
	unsigned char NumStorageElements[2];
	unsigned char FirstImportExportElementAddress[2];
	unsigned char NumImportExportElements[2];
	unsigned char FirstDataTransferElementAddress[2];
	unsigned char NumDataTransferElements[2];
	unsigned char Reserved[2];
}	EAAP_Type;

/* okay, now for the structure of a transport geometry
 * descriptor page:
 */
typedef struct TGDP
{
	unsigned char Page_Code;
	unsigned char ParameterLength;
	unsigned char Rotate;
	unsigned char ElementNumber;  /* we don't care about this... */
}	TGDP_Type;


/* Structure of the Device Capabilities Page: */
typedef struct DCP 
{
	unsigned char Page_Code;
	unsigned char ParameterLength;
	unsigned char CanStore;		/* bits about whether elements can store carts */
	unsigned char SMC2_Caps;
	unsigned char MT_Transfer;	/* bits about whether mt->xx transfers work. */
	unsigned char ST_Transfer;	/* bits about whether st->xx transfers work. */
	unsigned char IE_Transfer;	/* bits about whether id->xx transfers work. */
	unsigned char DT_Transfer;	/* bits about whether DT->xx transfers work. */
	unsigned char Reserved[4];	/* more reserved data */
	unsigned char MT_Exchange;	/* bits about whether mt->xx exchanges work. */
	unsigned char ST_Exchange;	/* bits about whether st->xx exchanges work. */
	unsigned char IE_Exchange;	/* bits about whether id->xx exchanges work. */
	unsigned char DT_Exchange;	/* bits about whether DT->xx exchanges work. */
	unsigned char Reserved2[4];	/* more reserved data */
}	DCP_Type;

#define MT_BIT 0x01
#define ST_BIT 0x02
#define IE_BIT 0x04
#define DT_BIT 0x08

/* Okay, now for the inquiry information: */

static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
{
	RequestSense_T RequestSense;
	Inquiry_T *Inquiry;
	int i;

	Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
	if (Inquiry == NULL) 
	{
		PrintRequestSense(&RequestSense);
		FatalError("INQUIRY Command Failed\n");
	}

	printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);

	printf("Vendor ID: '");
	for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
		printf("%c", Inquiry->VendorIdentification[i]);

	printf("'\nProduct ID: '");
	for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
		printf("%c", Inquiry->ProductIdentification[i]);

	printf("'\nRevision: '");
	for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
		printf("%c", Inquiry->ProductRevisionLevel[i]);

	printf("'\n");

	if (Inquiry->MChngr) 
	{
		/* check the attached-media-changer bit... */
		printf("Attached Changer: Yes\n");
	}
	else
	{
		printf("Attached Changer: No\n");
	}

	/* Now see if we have a bar code flag: */
	if (Inquiry->AdditionalLength > 50) 
	{
		/* see if we have 56 bytes: */
		if (Inquiry->VendorFlags & 1)
		{
			printf("Bar Code Reader: Yes\n");
		}
		else
		{
			printf("Bar Code Reader: No\n");
		}
	}

	free(Inquiry);		/* well, we're about to exit, but ... */
}

/*********** MODE SENSE *******************/
/* We need 3 different mode sense pages. This is a generic
 * routine for obtaining mode sense pages. 
 */

static unsigned char
*mode_sense(DEVICE_TYPE fd, char pagenum, int alloc_len,  RequestSense_T *RequestSense)
{
	CDB_T CDB;
	unsigned char *input_buffer;	/*the input buffer -- has junk prepended to
									 * actual sense page. 
									 */
	unsigned char *tmp;
	unsigned char *retval;			/* the return value. */
	int i,pagelen;

	if (alloc_len > 255)
	{
		FatalError("mode_sense(6) can only read up to 255 characters!\n");
	}

	input_buffer = (unsigned char *)xzmalloc(256); /* overdo it, eh? */

	/* clear the sense buffer: */
	slow_bzero((char *)RequestSense, sizeof(RequestSense_T));

	/* returns an array of bytes in the page, or, if not possible, NULL. */
	CDB[0] = 0x1a; /* Mode Sense(6) */
	CDB[1] = 0x08; 
	CDB[2] = pagenum; /* the page to read. */
	CDB[3] = 0;
	CDB[4] = 255; /* allocation length. This does max of 256 bytes! */
	CDB[5] = 0;

	if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
							input_buffer, 255, RequestSense) != 0)
	{
#ifdef DEBUG_MODE_SENSE
		fprintf(stderr,"Could not execute mode sense...\n");
		fflush(stderr);
#endif
		return NULL; /* sorry, couldn't do it. */
	}

	/* First skip past any header.... */
	tmp = input_buffer + 4 + input_buffer[3];
	/* now find out real length of page... */
	pagelen=tmp[1] + 2;
	retval = xmalloc(pagelen);
	/* and copy our data to the new page. */
	for (i = 0; i < pagelen; i++)
	{
		retval[i] = tmp[i];
	}
	/* okay, free our input buffer: */
	free(input_buffer);
	return retval;
}

/* Report the Element Address Assignment Page */
static void ReportEAAP(DEVICE_TYPE MediumChangerFD)
{
	EAAP_Type *EAAP; 
	RequestSense_T RequestSense;

	EAAP = (EAAP_Type *)mode_sense(MediumChangerFD, 0x1d, sizeof(EAAP_Type), &RequestSense);

	if (EAAP == NULL)
	{
		PrintRequestSense(&RequestSense);
		printf("EAAP: No\n");
		return;
	}

	/* we did get an EAAP, so do our thing: */
	printf("EAAP: Yes\n");
	printf("Number of Medium Transport Elements: %d\n", ( ((unsigned int)EAAP->NumMediumTransportElements[0]<<8) + (unsigned int)EAAP->NumMediumTransportElements[1]));
	printf("Number of Storage Elements: %d\n", ( ((unsigned int)EAAP->NumStorageElements[0]<<8) + (unsigned int)EAAP->NumStorageElements[1]));
	printf("Number of Import/Export Elements: %d\n", ( ((unsigned int)EAAP->NumImportExportElements[0]<<8) + (unsigned int)EAAP->NumImportExportElements[1]));
	printf("Number of Data Transfer Elements: %d\n", ( ((unsigned int)EAAP->NumDataTransferElements[0]<<8) + (unsigned int)EAAP->NumDataTransferElements[1]));

	free(EAAP);
}

/* See if we can get some invert information: */

static void Report_TGDP(DEVICE_TYPE MediumChangerFD)
{
	TGDP_Type *result;

	RequestSense_T RequestSense;

	result=(TGDP_Type *)mode_sense(MediumChangerFD,0x1e,255,&RequestSense);

	if (!result)
	{
		printf("Transport Geometry Descriptor Page: No\n");
		return;
	}

	printf("Transport Geometry Descriptor Page: Yes\n");

	/* Now print out the invert bit: */
	if ( result->Rotate & 1 )
	{
		printf("Invertable: Yes\n");
	}
	else
	{
		printf("Invertable: No\n");
	}

	free(result);
}

/* Okay, let's get the Device Capabilities Page. We don't care
 * about much here, just whether 'mtx transfer' will work (i.e., 
 * ST->ST).
 */

void TransferExchangeTargets(unsigned char ucValue, char *szPrefix)
{
	if (ucValue & DT_BIT)
	{
		printf("%sData Transfer", szPrefix);
	}

	if (ucValue & IE_BIT)
	{
		printf("%s%sImport/Export", ucValue > (IE_BIT | (IE_BIT - 1)) ? ", " : "", szPrefix);
	}

	if (ucValue & ST_BIT)
	{
		printf("%s%sStorage", ucValue > (ST_BIT | (ST_BIT - 1)) ? ", " : "", szPrefix);
	}

	if (ucValue & MT_BIT)
	{
		printf("%s%sMedium Transfer", ucValue  > (MT_BIT | (MT_BIT - 1)) ? ", " : "", szPrefix);
	}
}

static void Report_DCP(DEVICE_TYPE MediumChangerFD)
{
	DCP_Type *result;
	RequestSense_T RequestSense;

	/* Get the page. */
	result=(DCP_Type *)mode_sense(MediumChangerFD,0x1f,sizeof(DCP_Type),&RequestSense);
	if (!result) 
	{
		printf("Device Configuration Page: No\n");
		return;
	}

	printf("Device Configuration Page: Yes\n");

	printf("Storage: ");

	if (result->CanStore & DT_BIT)
	{
		printf("Data Transfer");
	}

	if (result->CanStore & IE_BIT)
	{
		printf("%sImport/Export", result->CanStore > (IE_BIT | (IE_BIT - 1)) ? ", " : "");
	}

	if (result->CanStore & ST_BIT)
	{
		printf("%sStorage", result->CanStore > (ST_BIT | (ST_BIT - 1)) ? ", " : "");
	}

	if (result->CanStore & MT_BIT)
	{
		printf("%sMedium Transfer", result->CanStore > (MT_BIT | (MT_BIT - 1)) ? ", " : "");
	}

	printf("\n");

	printf("SCSI Media Changer (rev 2): ");

	if (result->SMC2_Caps & 0x01)
	{
		printf("Yes\n");

		printf("Volume Tag Reader Present: %s\n", result->SMC2_Caps & 0x02 ? "Yes" : "No");
		printf("Auto-Clean Enabled: %s\n", result->SMC2_Caps & 0x04 ? "Yes" : "No");
	}
	else
	{
		printf("No\n");
	}

	printf("Transfer Medium Transport: ");
	if ((result->MT_Transfer & 0x0F) != 0)
	{
		TransferExchangeTargets(result->MT_Transfer, "->");
	}
	else
	{
		printf("None");
	}

	printf("\nTransfer Storage: ");
	if ((result->ST_Transfer & 0x0F) != 0)
	{
		TransferExchangeTargets(result->ST_Transfer, "->");
	}
	else
	{
		printf("None");
	}

	printf("\nTransfer Import/Export: ");
	if ((result->IE_Transfer & 0x0F) != 0)
	{
		TransferExchangeTargets(result->IE_Transfer, "->");
	}
	else
	{
		printf("None");
	}

	printf("\nTransfer Data Transfer: ");
	if ((result->DT_Transfer & 0x0F) != 0)
	{
		TransferExchangeTargets(result->DT_Transfer, "->");
	}
	else
	{
		printf("None");
	}

	printf("\nExchange Medium Transport: ");
	if ((result->MT_Exchange & 0x0F) != 0)
	{
		TransferExchangeTargets(result->MT_Exchange, "<>");
	}
	else
	{
		printf("None");
	}

	printf("\nExchange Storage: ");
	if ((result->ST_Exchange & 0x0F) != 0)
	{
		TransferExchangeTargets(result->ST_Exchange, "<>");
	}
	else
	{
		printf("None");
	}

	printf("\nExchange Import/Export: ");
	if ((result->IE_Exchange & 0x0F) != 0)
	{
		TransferExchangeTargets(result->IE_Exchange, "<>");
	}
	else
	{
		printf("None");
	}

	printf("\nExchange Data Transfer: ");
	if ((result->DT_Exchange & 0x0F) != 0)
	{
		TransferExchangeTargets(result->DT_Exchange, "<>");
	}
	else
	{
		printf("None");
	}

	printf("\n");

	free(result);
}

void usage(void)
{
	FatalError("Usage: loaderinfo -f <generic-device>\n");
}


/* we only have one argument: "-f <device>". */
int main(int argc, char **argv)
{
	DEVICE_TYPE fd;
	char *filename;

	argv0=argv[0];
	if (argc != 3)
	{
		usage();
	}

	if (strcmp(argv[1],"-f")!=0)
	{
		usage();
	}

	filename=argv[2];

	fd=SCSI_OpenDevice(filename);

	/* Now to call the various routines: */
	ReportInquiry(fd);
	ReportEAAP(fd);
	Report_TGDP(fd);
	Report_DCP(fd);
	exit(0);
}