VMS Help  —  MRD Library, mrd_read_elem_stat, 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 ;
    }
Close Help