/*
* 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 ;
}