Copyright Digital Equipment Corp. All rights reserved.

Example

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