/* Copyright 2000 Enhanced Software Technologies Inc. * Copyright 2007-2008 by Robert Nelson * 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 #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 \n"); } /* we only have one argument: "-f ". */ 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); }