HP OpenVMS I/O User’s Reference Manual: OpenVMS Version 8.4 > Chapter 8 Using the OpenVMS Generic SCSI Class Driver

8.10 Call a Generic SCSI Class Driver

Example 8-1 is an application that uses the generic SCSI class driver to send a SCSI INQUIRY command to a device.

Example 8-1 Generic SCSI Class Driver Call Example

/*
** 
* © 2009 Hewlett-Packard Development Company, L.P.
*
* Confidential computer software. Valid license from HP and/or its
* subsidiaries required for possession, use, or copying.
*
* Consistent with FAR 12.211 and 12.212, Commercial Computer Software,
* Computer Software Documentation, and Technical Data for Commercial Items
* are licensed to the U.S. Government under vendor's standard commercial
* license.
*
* Neither HP nor any of its subsidiaries shall be liable for technical or
* editorial errors or omissions contained herein. The information in this
* document is provided "as is" without warranty of any kind and is subject
* to change without notice. The warranties for HP products are set forth 
* in the express limited warranty statements accompanying such products. 
* Nothing herein should be construed as constituting an additional 
* warranty.
*
*/

#ifdef VAX
#module gktest "V01-03"
#else
#pragma module gktest "V01-03"
#endif

/*
**++
**  FACILITY:  SYS$EXAMPLES
**
**  MODULE DESCRIPTION:
**
**	GKTEST -- Generic SCSI device inquiry example.  This program
**	uses the SCSI generic class driver to send an inquiry command
**	to a device on the SCSI bus and send the resulting status to
**	stdout.  PHY_IO and DIAGNOSE privileges are needed to run this
**	program.
**
**  AUTHORS:
**
**      Hewlett-Packard
**
**  CREATION DATE:  28-Aug-2009  (adapted from previous OpenVMS version)
**
**  DESIGN ISSUES:
**
**	To be appropriately upwardly-compatible, it would be better
**	that this module use a SCSI descriptor structure definition
**	from an appropriate header file (something like scsidef.h).
**	At the time of most recent modification, no such file was
**	available for OpenVMS.
**
**
**  MODIFICATION HISTORY:
**
**  X-1 DCP001					28-Aug-2009
**	Use structure members that are more "type-sensitive".
**
**  X-2 DCP002					11-Sep-2009
**	Modifications to platform-specific macro names.
**  X-3 					05-Oct-2009
**	Modify status checking to return proper error code from
**	$qio.
**--
*/


/*
**
**  INCLUDE FILES
**
*/

#include <stdio.h>
#include <ctype.h>
#include <iodef.h>
#include <descrip.h>
#include <starlet.h>

/*	 
**  "De-comment" (and if necessary modify) the following if the
**  appropriate header file becomes available:
#include <scsidef.h>
*/	 


/*
**
**  MACRO DEFINITIONS
**
*/


#define GK_EFN 0		    /*  Event flag number */

#define INQUIRY_OPCODE 0x12	    /*  Operation code for SCSI inquiry */
#define INQUIRY_DATA_LENGTH 0x24    /*  Length of inquiry buffer */


/*
** SCSI definitions:
**
** Ideally, these definitions should come from a header file provided
** with the system.  At the time that this example was written and at
** the time of last update, no such file was available. For now, we
** define right here fields we need from the SCSI descriptor for this
** example; this should be replaced with the appropriate #include,
** should such a header file become available.  The reader should note
** that some of the field names and types in that header file may
** differ slightly from what's shown here; when and if the header file
** becomes available, code which does depend on the names should use
** the appropriate header file names.  Code which depends on getting
** the types right may need to re-cast these members when referencing
** them.
*/

/* Generic SCSI command descriptor */

struct SCSI$DESC {
    unsigned int    SCSI$L_OPCODE;	     /* SCSI Operation Code */
    unsigned int    SCSI$L_FLAGS;	     /* SCSI Flags Bit Map */
    char	* SCSI$A_CMD_ADDR;	     /* ->SCSI command buffer */
    unsigned int	SCSI$L_CMD_LEN;	     /* SCSI command length, bytes */
    char	* SCSI$A_DATA_ADDR;	     /* ->SCSI data buffer */
    unsigned int	SCSI$L_DATA_LEN;     /* SCSI data length, bytes */
    unsigned int	SCSI$L_PAD_LEN;	     /* SCSI pad length, bytes */
    unsigned int	SCSI$L_PH_CH_TMOUT;  /* SCSI phase change timeout, sec */
    unsigned int	SCSI$L_DISCON_TMOUT; /* SCSI disconnect timeout, sec */
    unsigned int	SCSI$L_RES_1;	     /* Reserved */
    unsigned int	SCSI$L_RES_2;	     /* Reserved */
    unsigned int	SCSI$L_RES_3;	     /* Reserved */
    unsigned int	SCSI$L_RES_4;	     /* Reserved */
    unsigned int	SCSI$L_RES_5;	     /* Reserved */
    unsigned int	SCSI$L_RES_6;	     /* Reserved */
    } ;  

/* SCSI Input/Output Status Block */

#ifdef __ALPHA
#pragma member_alignment save
#pragma nomember_alignment
#endif

struct SCSI$IOSB {
    unsigned short int	SCSI$W_VMS_STAT;	    /* VMS status code */
    unsigned long int	SCSI$L_IOSB_TFR_CNT; /* Actual #bytes transferred */
    char 	SCSI$B_IOSB_FILL_1;
    unsigned char	SCSI$B_IOSB_STS;		/* SCSI device status */
    };

#ifdef __ALPHA
#pragma member_alignment restore
#endif


/* SCSI status codes and flag field constants */

#define SCSI$K_GOOD_STATUS      0
#define SCSI$K_READ		0X1	/* direction of transfer=read */
#define SCSI$V_FL_ENAB_DIS	1	/* enable disconnects */
#define SCSI$K_FL_ENAB_DIS	0X2	/* enable disconnects */

/* end of SCSI definitions */

/* data declarations */

char scsi_status, 
     inquiry_command[6] = {INQUIRY_OPCODE, 0, 0, 0,
			     INQUIRY_DATA_LENGTH, 0}, 
     inquiry_data[INQUIRY_DATA_LENGTH], 
     gk_device[] = {"GKA0"};


main ()

{

    unsigned short int gk_chan, 
		       transfer_length;
    int i, 
	status;



/* Set up the descriptor with the SCSI information to be sent to the target */

    struct SCSI$DESC gk_desc = { 1,  /* Pass-through - the only code defined */
				 SCSI$K_READ|SCSI$K_FL_ENAB_DIS, /* flags   */
				 &inquiry_command[0],   /* command addr */
				 6,			/* command length*/
				 &inquiry_data[0],	/* data addr */
				 INQUIRY_DATA_LENGTH,   /* data length */
				 0,			/* pad length */
				 180,			/* phase timeout */
				 60,			/* disconnect timeout */
				 0, 0, 0, 0, 0, 0 };    /* reserved */

    struct SCSI$IOSB gk_iosb ;

    $DESCRIPTOR (gk_device_desc, gk_device);

/*  Assign the device channel */
    status = sys$assign ( &gk_device_desc,  &gk_chan,  0,  0);
    if (!(status & 1))
    {
    	printf ("Unable to assign channel to %s", &gk_device[0]);
	sys$exit (status);
    }

/* Issue the QIO to send the inquiry command and receive the inquiry data */

    status = sys$qiow ( GK_EFN,  gk_chan,  IO$_DIAGNOSE,  &gk_iosb,  0,  0, 
                      &gk_desc,  15*4,  0,  0,  0,  0);


/* Check the various returned status values */
    if (!(status & 1))	sys$exit (status);


/* Was VMS Status OK from QIO? */

    if (!(gk_iosb.SCSI$W_VMS_STAT & 1))
    	sys$exit (gk_iosb.SCSI$W_VMS_STAT);

/* Yes, was SCSI Status OK from QIO? */

    if (gk_iosb.SCSI$B_IOSB_STS != SCSI$K_GOOD_STATUS)
    {
    	printf ("Bad SCSI status returned: %02.2x\n", gk_iosb.SCSI$B_IOSB_STS);
	sys$exit (1);
    }

/* The command succeeded. Display the SCSI data returned from the target */

    transfer_length = gk_iosb.SCSI$L_IOSB_TFR_CNT;
    printf ("SCSI inquiry data returned %lu bytes of data: ", transfer_length);
    for (i=0;  i<transfer_length;  i++)
    {
	    if (isprint (inquiry_data[i]))
		    printf ("%c", inquiry_data[i]);
	    else
		    printf (".");
    }
    printf ("\n");
}