/* * This is an example of using mrd_read_element_status(3mrd). * * Due to the complexity of the SCSI Read Element Status data * all this example will do is format the headers found in the * data. It won't try to format the element data. It also * calls mrd_read_element_status(3mrd) twice, once to determine * the needed data size for the remaining data and again to get * the actual data. * * Usage: * * mrd_read_element_status robot type start count * * If an unrecognized element type is used, the routine will use * a type of zero, which is allowed by the SCSI-2 specification. */ #ifndef lint static char SccsId[] = "@(#)mrd_read_element_status.c 1.3 (mrd-example) 3/5/97" ; #endif #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <mrd_common.h> #include <mrd_message.h> /* * The SCSI specification says that a request size of 8 * bytes will have the underlying device only return a * header indicating the number of bytes needed for the * command. */ #define SCSI_RES_MIN (8) /* * Given a string, resembling one of the element types, * return the SCSI type code for it. */ struct { int code ; char *string ; } etypes[] = { TRANSPORT, "transport", SLOT, "slot", DRIVE, "drive", PORT, "port", } ; convert_type(char *etype) { register i ; /* * For each entry in the array. */ for(i = 0; i < sizeof(etypes)/sizeof(etypes[0]); i++) /* * Do a case insensitive comparison, allowing * abbreviations. Return as soon as a match is * found. Return -1 if one isn't found. */ #ifdef vms if( strncmp(etypes[i].string, etype, strlen(etype)) == 0 ) #else if( strncasecmp(etypes[i].string, etype, strlen(etype)) == 0 ) #endif return etypes[i].code ; return 0 ; } /* * When an 8 byte Read Element Status command is handed * to a compliant medium changer, it is supposed to fill * enough of the header section to say how much data is * needed for the full command. We that here, if we get * a reasonable value, allocate sufficient space for the * real command and return the pointer to it. If there * is an error or command returns a zero byte report * return NULL. */ unsigned char * res_size(robot_info_t *robot_info, int type, int start, int count, size_t *bytes) { unsigned char data[SCSI_RES_MIN] ; /* minimum data */ int status ; /* command status */ dev_status_t dev_status ; /* In case of error */ unsigned char *report ; /* Data space */ /* * Read Element Status commands rarely fail, they just * succeed, but return no data. Clear all the fields * so we'll have an easier time seeing if any data was * returned. */ memset((void *)data, 0, SCSI_RES_MIN) ; status = mrd_read_element_status(robot_info, type, start, count, data, SCSI_RES_MIN, &dev_status) ; /* * But sometimes they do fail. */ if( status != MRD_STATUS_SUCCESS ) { printf("Size Read Element Status failed on %s: %s.\n", robot_info->robot_name, mrd_strstatus(status)) ; return NULL ; } /* * Calculate the report size. */ *bytes = (data[RES_REPORT_MSB] << 16) | (data[RES_REPORT_ISB] << 8) | data[RES_REPORT_LSB] ; /* * The report size doesn't include the 8 bytes needed for * the first headers. */ *bytes += RES_DATA_HEADER ; if( *bytes == 0 ) { printf("The report size is zero on %s.\n", robot_info->robot_name) ; return NULL ; } if((report = (unsigned char *)malloc(*bytes)) == NULL ) printf("Can't allocate %ld bytes for %s: %s.\n", *bytes, robot_info->robot_name, strerror(errno)) ; return report ; } /* * Print out the Read Element Status data headers. There * is an 8 byte data header, that describes the remaining * data, which is zero or more Status Pages, one for each * element type. * * Each Element Status Page consists of an 8 byte header * for the element type described by that page and then * element descriptors. */ print_res_data(robot_info_t *robot_info, unsigned char *dp) { int first ; /* First element reported */ int elements ; /* Number of elements reported */ int report ; /* Bytes in the total report */ int descriptor ; /* Each element descriptor size */ int bytes ; /* The per-element report size */ /* * The first two bytes of the overall header is the * first element reported. */ first = (dp[RES_FIRST_MSB] << 8) | dp[RES_FIRST_LSB] ; /* * The next two bytes are the number of elements in * the report. */ elements = (dp[RES_COUNT_MSB] << 8) | dp[RES_COUNT_LSB] ; /* * Three of the remaining bytes are the total report size. */ report = (dp[RES_REPORT_MSB] << 16) | (dp[RES_REPORT_ISB] << 8) | dp[RES_REPORT_LSB] ; printf("RES Data Header:\n") ; printf(" First Element Address: %d\n", first) ; printf(" Number of Elements: %d\n", elements) ; printf(" Byte Count of Report: %d\n", report) ; /* * As long as bytes of report remaining, print each * element type header. * * Here we play a curious pointer game. The RES_ * constants defined in mrd_common.h for the element * header assume a single element type with offsets * from the beginning of the data. But real Read * Element Status Data can have multiple element * types in it. For successive element type we'll * add the per-element report size to the address * of the base data. This should put the per-element * header at the right relative place. */ report -= RES_DATA_HEADER ; while( report > 0 ) { /* * Calculate the descriptor size. */ descriptor = (dp[RES_DESC_MSB] << 8) | dp[RES_DESC_LSB] ; /* * And the per element report. */ bytes = (dp[RES_BYTES_MSB] << 16) | (dp[RES_BYTES_ISB] << 8) | dp[RES_BYTES_LSB] ; printf(" Descriptor Header:\n") ; printf(" Element Type: %s\n", mrd_strelement(dp[RES_TYPE])) ; printf(" Primary Volume Tag: %x\n", dp[RES_TAGS] & ELEMENT_PVOLTAG) ; printf(" Alternate Volume Tag: %x\n", dp[RES_TAGS] & ELEMENT_AVOLTAG) ; printf(" Descriptor Length: %d\n", descriptor) ; printf(" Descriptor Report: %d", bytes) ; /* * Include the number of elements of this type. */ if( descriptor ) printf(" (%d)\n", bytes / descriptor) ; else putchar('\n') ; /* * Protection against the odd insane loader. */ if( bytes == 0 ) break ; /* * Go to the next header. Formatting the element * data is left as an exercise to the reader. */ dp += (bytes + RES_DATA_PAGE) ; /* * Lose some bytes... */ report -= (bytes + RES_DATA_PAGE) ; } } main(int argc, char *argv[]) { int status ; /* return status */ int type ; /* Element type */ int start ; /* First element */ int count ; /* Number of elements */ size_t bytes ; /* Bytes of data */ char *robot ; /* Robot to open */ unsigned char *data ; /* Element Status data */ robot_info_t robot_info ; /* Robot data */ dev_status_t dev_status ; /* Device status */ char log_info[MRD_MAX_LOG_STRING+1] ; /* * Check that there are enough arguments. */ if( argc < 5 ) { printf("usage: %s robot type start count\n", argv[0]) ; exit(1) ; } else { robot = argv[1] ; type = convert_type(argv[2]) ; start = atoi(argv[3]) ; count = atoi(argv[4]) ; } /* * Initialize the channel field of the robot_info, so * mrd_startup(3mrd) will actually open the robot. */ robot_info.channel = BAD_CHANNEL ; status = mrd_startup(robot, &robot_info, log_info) ; if( status != MRD_STATUS_SUCCESS ) { printf("Startup failed: %s: %s.\n", mrd_strstatus(status), log_info[0] ? log_info : "none") ; exit(1) ; } if( type == 0 ) printf("Data size needed for all elements %d - %d...", start, start + count) ; else printf("Data size needed for %s %d - %d...", mrd_strelement(type), start, start + count) ; fflush(stdout) ; switch( type ) { case SLOT: start += robot_info.slot_start ; break ; case PORT: start += robot_info.port_start ; break ; case TRANSPORT: start += robot_info.transport_start ; break ; case DRIVE: start += robot_info.device_start ; break ; } /* * Allocate sufficient space for the command. This * function prints its own error messages, so we can * just exit. */ data = res_size(&robot_info, type, start, count, &bytes) ; if( data == NULL ) exit(1) ; printf("%d bytes.\n", bytes) ; /* * Now do the full Read Element Status command. */ status = mrd_read_element_status(&robot_info, type, start, count, data, bytes, &dev_status) ; if( status != MRD_STATUS_SUCCESS ) { printf("Read Element Status failed on %s: %s.\n", robot, mrd_strstatus(status)) ; free(data) ; exit(1) ; } /* * We appear to have valid Read Element Status data. Print * out the results. */ print_res_data(&robot_info, data) ; (void)mrd_shutdown(&robot_info) ; return 0 ; }