[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


2    CAM User Agent Modules

This chapter describes the functions of the Digital UNIX User Agent SCSI device driver. It also describes the User Agent data structures and routines that the User Agent SCSI device driver uses.


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


2.1    User Agent Introduction

Device driver writers can use the Digital UNIX User Agent SCSI device driver to write an application program to build a CAM control block (CCB) request. The User Agent driver lets the user-process request pass through to the XPT layer for processing. This gives user processes access to the SCSI/CAM subsystem and to all types of SCSI/CAM peripheral devices attached to the system.

This is a simple method for passing the CCB's SCSI request to the devices that use the SIMs. The kernel does not have to be rebuilt if the device driver writer wants to change values within the CCBs.

The CCB contains all the information required to perform the request. The user process must first open the user agent driver using /dev/cam to obtain a file descriptor. The user process calls the User Agent SCSI device driver as a result of calling the ioctl system call. (See ioctl(2) for more information.) The User Agent ioctl routine, uagt_ioctl, is called through the device switch table. This table is indexed by the major device number of the User Agent driver specified in the file descriptor obtained from the open system call passed in the ioctl call. The ioctl commands supported by the User Agent SCSI device driver are:

A CCB is allocated in the kernel and the user process's CCB is copied to the kernel CCB. The User Agent SCSI device driver sleeps while waiting for the request to complete; then, all necessary cleanup is performed, and the user process is notified of the completion of the request. If a signal is caught, an ABORT CCB is issued to try to terminate the outstanding CCB for the user process.

The User Agent SCSI device driver allows multiple processes access to the XPT layer; therefore, there may be multiple processes sleeping on the User Agent. All CCBs passed through by the User Agent are queued at the SIM layer.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.2    User Agent Error Handling

The User Agent SCSI device driver performs limited error checking on the CCB pointed to in the UAGT_CAM_CCB structure passed from the user process. The User Agent driver verifies that the uagt_ccblen is not greater than the maximum length for a CCB, checks that the XPT function code is valid, and checks that the target ID and logical unit number (LUN) specified are within the range allowed. The User Agent does not issue a REQUEST SENSE command in response to a CHECK CONDITION status. Autosensing is assumed to be enabled in the cam_ch.cam_flags field of the SCSI I/O CCB. The application program is responsible for issuing a RELEASE SIM QUEUE CCB.

The following error codes are returned by the User Agent ioctl function:


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3    User Agent Data Structures

This section describes the data structures that the User Agent uses.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1    The UAGT_CAM_CCB Data Structure

The User Agent SCSI device driver uses the UAGT_CAM_CCB data structure with the UAGT_CAM_IO ioctl command to communicate with the user processes requesting access to the SCSI/CAM subsystem.

The user process fills in the pointers in the UAGT_CAM_CCB data structure and the structure is copied into kernel space. The User Agent then copies the user process's CCB into kernel space.

If necessary, the user data area and the sense data area are locked in memory. If any pointers in the UAGT_CAM_CCB structure are not needed with the requested CCB, the pointers must be set to NULL.

The CCB contains all the information necessary to execute the requested XPT function. The addresses in the CCB are used by the SIM and must be valid. The User Agent will not modify the corresponding pointers in the user's CCB.

The CCB definition is different for each of the following XPT functions supported by the User Agent SCSI device driver:

If the user process generates a signal, the User Agent creates an XPT_ABORT CCB to abort the outstanding I/O. The User Agent then waits for the completion of the I/O and notifies the user process when the aborted CCB is returned to the User Agent.

The UAGT_CAM_CCB structure is defined as follows:

typedef struct uagt_cam_ccb
{
    CCB_HEADER *uagt_ccb;     /* pointer to the users CCB */
    u_long uagt_ccblen;       /* length of the users CCB */
    u_char *uagt_buffer;      /* pointer for the data buffer */
    u_long uagt_buflen;       /* length of user request */
    u_char *uagt_snsbuf;      /* pointer for the sense buffer */
    u_long uagt_snslen;       /* length of user's sense buffer */
    CDB_UN *uagt_cdb;         /* ptr for a CDB if not in CCB */
    u_long uagt_cdblen;       /* CDB length if appropriate */
    u_long uagt_flags;        /* See below */
} UAGT_CAM_CCB;


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.1    The uagt_ccb Member

The uagt_ccb member contains a pointer to the user process's CCB that will be copied into kernel space.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.2    The uagt_ccblen Member

The uagt_ccblen member contains the length of the user process's CCB.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.3    The uagt_buffer Member

The uagt_buffer member contains a pointer to the user process's data buffer. Only the User Agent uses this member.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.4    The uagt_buflen Member

The uagt_buflen member contains the length of the user process's data buffer. Only the User Agent uses this member.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.5    The uagt_snsbuf Member

The uagt_snsbuf member contains a pointer to the user process's autosense data buffer. Only the User Agent uses this member.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.6    The uagt_snslen Member

The uagt_snslen member contains the length of the user process's autosense data buffer. Only the User Agent uses this member.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.7    The uagt_cdb Member

If the user process's CCB contains a pointer to a command descriptor block (CDB), then the uagt_cdb also contains a pointer to a CDB that is to be locked in memory. Only the User Agent driver uses this member and the uagt_cdblen member. The CCB must also contain valid pointers and counts.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.8    The uagt_cdblen Member

The uagt_cdblen contains the length of the CDB, if appropriate.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.1.9    The uagt_flags Member

The uagt_flags contains the UAGT_NO_INT_SLEEP bit, which, if set, indicates that the User Agent should not sleep at an interruptible priority.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.3.2    The UAGT_CAM_SCAN Data Structure

The User Agent SCSI device driver uses the UAGT_CAM_SCAN data structure to communicate with user-level programs that need to have access to the CAM subsystem. The structure is copied into kernel space as part of the ioctl system call from user space for the UAGT_CAM_SINGLE_SCAN and UAGT_CAM_FULL_SCAN commands. The user program fills in the pointers in this structure and the User Agent SCSI device driver correctly fills in the corresponding pointers in the CCB.

The UAGT_CAM_SCAN structure is defined as follows:

typedef struct uagt_cam_scan  {
        u_char  ucs_bus;                /* Bus id for scan */
        u_char  ucs_target;             /* Target id for scan */
        u_char  ucs_lun;                /* LUN for scan */
} UAGT_CAM_SCAN;


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.4    User Agent Routines

This section describes the User Agent routines that Digital supplies. Table 2-1 lists the name of each routine and gives a summary description of its function. The sections that follow contain a more detailed description of each User Agent routine. Descriptions of the routines with syntax information, in Digital UNIX reference page format, are included in alphabetical order in Appendix B.

Table 2-1: User Agent Routines

Routine Summary Description
uagt_open Handles the open of the User Agent driver.
uagt_close Handles the close of the User Agent driver.
uagt_ioctl Handles the ioctl system call for the User Agent driver.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.4.1    The uagt_open Routine

The uagt_open routine handles the open of the User Agent driver.

The character device special file name used for the open is /dev/cam.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.4.2    The uagt_close Routine

The uagt_close routine handles the close of the User Agent driver. For the last close operation for the driver, if any queues are frozen, a RELEASE SIM QUEUE CCB is sent to the XPT layer for each frozen queue detected by the User Agent.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.4.3    The uagt_ioctl Routine

The uagt_ioctl routine handles the ioctl system call for the User Agent driver. The ioctl commands supported are:

For SCSI I/O CCB requests, the user data area is locked before passing the CCB to the XPT. The User Agent sleeps while waiting for the I/O to complete and issues an ABORT CCB if a signal is caught while sleeping.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5    Sample User Agent Drivers

Two sample User Agent driver programs follow. The first sample program uses the User Agent driver to perform a SCSI INQUIRY command to a device on a selected nexus.

The second sample program is a scanner control program that sets up a scanner, reads scan line data from the device, and uses the User Agent driver to write the data to a file.

Both programs are included with the S/CA software and reside in the /usr/examples directory.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1    Sample User Agent Driver Inquiry Program

This section contains the User Agent sample inquiry application program, caminq.c, with annotations to the code. The user enters the string inq followed by the numbers identifying the bus, target, and LUN nexus to be checked for a valid device. If the device is valid, the INQUIRY data is displayed at the console. If the device is invalid, an error message appears.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.1    The Include Files and Definitions Section

This section describes the portion of the User Agent sample inquiry application program that lists the include files, local definitions, and data initialization for the program.

/* -------------------------------------------------------------- */
/* Include files needed for this program. */

#include <stdio.h> #include <sys/file.h> #include <sys/types.h> #include <sys/ioctl.h> #include <strings.h> #include <ctype.h> #include <io/common/iotypes.h> #include <io/cam/cam.h> /* CAM defines from the CAM document */ #include <io/cam/dec_cam.h>/* CAM defines for Digital CAM source files */ #include <io/cam/uagt.h> /* CAM defines for the UAgt driver */ #include <io/cam/scsi_all.h> /* CAM defines for ALL SCSI devices */

/* -------------------------------------------------------------- */
/* Local defines */

#define INQUIRY_LEN 36 /* general inquiry length */ [1]

/* -------------------------------------------------------------- */ /* Initialized and uninitialized data. */

u_char buf[ INQUIRY_LEN ]; [2]

  1. Defines a constant of 36 bytes for the length of the inquiry expected by the user from the SCSI device. [Return to example]

  2. Declares a global character array, buf, with a size of 36 bytes as defined by the INQUIRY_LEN constant. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.2    Main Program Section

This section describes the main program portion of the User Agent sample inquiry application program.

/* --------------------------------------------------------------- */
/* The main code path.  The CCB/CDB and UAGT_CAM_CCB are set up for
   an INQUIRY command to the Bus/Target/Lun selected by the command
   line arguments.  The returned INQUIRY data is displayed to the
   user if the status is valid.  If the returned status indicates
   an error, the error is reported instead of the INQUIRY data. */

main(argc, argv) int argc; char *argv[]; {

extern void print_inq_data(); [1] extern void print_ccb_status();

u_char id, targid, lun; /* from the command line */ int fd; /* unit number from the open */ [2]

UAGT_CAM_CCB ua_ccb; /* local uagt structure */ [3] CCB_SCSIIO ccb; /* local CCB */ [4] ALL_INQ_CDB *inq; /* pointer for the CDB */ [5]

/* Make sure that all the arguments are there. */ [6]

if (argc != 4) { printf("SCSI INQ bus target lun\n"); exit(); }

/* Convert the nexus information from the command line. */ [7]

id = atoi(argv[1]); targid = atoi(argv[2]); lun = atoi(argv[3]);

  1. Define routines that are used later in the program to print out the INQUIRY data or to print out the CAM status if there was an error. [Return to example]

  2. Is the file descriptor for the User Agent driver returned by the open system call, which executes in Section 2.5.1.3. [Return to example]

  3. Declares an uninitialized local data structure, ua_ccb, of the type UAGT_CAM_CCB, which is defined in the file /usr/sys/include/io/cam/uagt.h. This structure is copied from user space into kernel space as part of the ioctl system call. Section 2.5.1.7 describes this procedure. [Return to example]

  4. Declares an uninitialized local data structure, ccb, of the type CCB_SCSIIO , which is defined in the file /usr/sys/include/io/cam/cam.h. The members of this structure needed for the XPT_SCSI_IO request are filled in Section 2.5.1.4. The members of this structure needed for the INQUIRY command are filled in Section 2.5.1.5. [Return to example]

  5. Declares a pointer, inq, to a data structure, ALL_INQ_CDB, which is defined in the file /usr/sys/include/io/cam/scsi_all.h. This structure is filled in Section 2.5.1.5. [Return to example]

  6. Makes sure the user entered the correct number of arguments. The user should have entered the string inq, followed by three numeric characters representing the bus, target, and LUN to be checked for a valid status. [Return to example]

  7. Converts the numeric characters entered and assigns them, in order, to bus, target, and LUN. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.3    User Agent Open Section

This section describes the portion of the User Agent sample inquiry application program where the User Agent is opened.

/* Open the User Agent driver and report any errors. */

if ((fd = open("/dev/cam", O_RDWR, 0)) < 0 ) [1] { perror("Error on CAM UAgt Open:"); exit(1); }

  1. Attempts to open the User Agent device special file, /dev/cam, with the O_RDWR flag, which allows reading and writing. If the file descriptor returned by the open system call indicates that the open failed by returning a negative value, < 0, the program reports an error and exits. Otherwise, the program opens the device. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.4    Filling in XPT_SCSI_IO Request CCB_HEADER Fields

This section describes the portion of the User Agent sample inquiry application program where the members of the CCB_HEADER needed for an XPT_SCSI_IO request are filled in.

bzero((caddr_t)&ccb,sizeof(CCB_SCSIIO)) /* Clear the CCB structure */ /* Set up the CCB for an XPT_SCSI_IO request. The INQUIRY command will be sent to the device, instead of sending an XPT_GDEV_TYPE. */

/* Set up the CAM header for the XPT_SCSI_IO function. */

ccb.cam_ch.my_addr = (struct ccb_header *)&ccb; /* "Its" address */ [1] ccb.cam_ch.cam_ccb_len = sizeof(CCB_SCSIIO); /* a SCSI I/O CCB */ ccb.cam_ch.cam_func_code = XPT_SCSI_IO; /* the opcode */ ccb.cam_ch.cam_path_id = id; /* selected bus */ [2] ccb.cam_ch.cam_target_id = targid; /* selected target */ ccb.cam_ch.cam_target_lun = lun; /* selected lun */

/* The needed CAM flags are : CAM_DIR_IN - The data will come from the target, CAM_DIS_AUTOSENSE - Do not issue a REQUEST SENSE packet if there is an error. */

ccb.cam_ch.cam_flags = CAM_DIR_IN | CAM_DIS_AUTOSENSE; [3]

  1. Fills in some of the CCB_HEADER fields of the SCSI I/O CCB structure defined as ccb, for processing by the XPT layer. The structure was declared in Section 2.5.2.5. [Return to example]

  2. Assign the bus, target, and LUN to the corresponding fields in the CCB_HEADER structure. [Return to example]

  3. Sets the necessary CAM flags for the INQUIRY: CAM_DIR_IN, which specifies that the direction of the data is incoming; and CAM_DIS_AUTOSENSE, which disables the autosense feature. These flags are defined in /usr/sys/include/io/cam/cam.h. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.5    Filling in INQUIRY Command CCB_HEADER Fields

This section describes the portion of the User Agent sample inquiry application program where the members of the CCB_HEADER needed for the INQUIRY command are filled in. This is the structure that the User Agent driver passes to the XPT layer.

/* Set up the rest of the CCB for the INQUIRY command. */

ccb.cam_data_ptr = &buf[0]; /* where the data goes */ [1] ccb.cam_dxfer_len = INQUIRY_LEN; /* how much data */ ccb.cam_timeout = CAM_TIME_DEFAULT; /* use the default timeout */ [2] ccb.cam_cdb_len = sizeof( ALL_INQ_CDB ); /* how many bytes for inquiry */[3]

/* Use a local pointer to access the particular fields in the INQUIRY CDB. */

inq = (ALL_INQ_CDB *)&ccb.cam_cdb_io.cam_cdb_bytes[0]; [4]

inq->opcode = ALL_INQ_OP; /* inquiry command */ [5] inq->evpd = 0; /* no product data */ inq->lun = 0; /* not used in SCSI-2 */ inq->page = 0; /* no product pages */ inq->alloc_len = INQUIRY_LEN; /* for the buffer space */ inq->control = 0; /* no control flags */

  1. Sets the cam_data_ptr member of the SCSI I/O CCB structure to the address of the first element in the buf array, which is defined as 36 bytes in Section 2.5.1.1. [Return to example]

  2. Specifies using the default timeout, which is the value assigned to the CAM_TIME_DEFAULT constant. This constant is set in the /usr/sys/include/io/cam/cam.h file to indicate that the SIM layer's default timeout is to be used. The current value of the SIM layer's default timeout is 5 seconds. [Return to example]

  3. Sets the length of the CDB in the CCB to the length of an inquiry CDB. The inquiry CDB, ALL_INQ_CDB, which is defined in the /usr/sys/include/io/cam/scsi_all.h file, is 6 bytes. [Return to example]

  4. Assigns the inq pointer, which is type ALL_INQ_CDB, to the address of the cam_cdb_bytes member of the CDB_UN union. This union is defined in /usr/sys/include/io/cam/cam.h as the cam_cdb_io member of the SCSI I/O CCB structure. [Return to example]

  5. Use the inq pointer to access the fields of the cam_cdb_bytes array within the ccb structure as though it is an ALL_INQ_CDB structure. The ALL_INQ_CDB structure is defined in the /usr/sys/include/io/cam/scsi_all.h file. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.6    Filling in the UAGT_CAM_CCB Fields

This section describes the portion of the User Agent sample inquiry application program where the members of the UAGT_CAM_CCB structure are filled in for the ioctl call. This is the structure that is passed to the User Agent driver.

bzero((caddr_t)&ua_ccb,sizeof(UAGI_CAM_CCB))
                    /* Clear the ua_ccb structure */

/* Set up the fields for the User Agent Ioctl call. */

ua_ccb.uagt_ccb = (CCB_HEADER *)&ccb; /* where the CCB is */ [1] ua_ccb.uagt_ccblen = sizeof(CCB_SCSIIO); /* how many bytes to pull in */ [2] ua_ccb.uagt_buffer = &buf[0]; /* where the data goes */ [3] ua_ccb.uagt_buflen = INQUIRY_LEN; /* how much data */ [4]

ua_ccb.uagt_snsbuf = (u_char *)NULL; /* no Autosense data */ [5] ua_ccb.uagt_snslen = 0; /* no Autosense data */ ua_ccb.uagt_cdb = (CDB_UN *)NULL; /* CDB is in the CCB */ [6] ua_ccb.uagt_cdblen = 0; /* CDB is in the CCB */

  1. Initializes the uagt_ccb member of the ua_ccb structure with the address of the local CCB_HEADER structure, ccb. [Return to example]

  2. Sets the length of the uagt_ccblen member to the length of the SCSI I/O CCB structure that will be used for this call. [Return to example]

  3. Initializes the uagt_buffer member with the user space address of the array buf, which was allocated 36 bytes in Section 2.5.1.1. [Return to example]

  4. Initializes the uagt_buflen member with the value of the constant INQUIRY_LEN, which is the number of bytes of inquiry data that will be returned. [Return to example]

  5. Reflect that the autosense features are turned off in the CAM flags. [Return to example]

  6. Reflect that the CDB information is in the SCSI I/O CCB structure filled in Section 2.5.1.4. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.7    Sending the CCB to the CAM Subsystem

This section describes the portion of the User Agent sample inquiry application program where the ccb is sent to the CAM subsystem.

/* Send the CCB to the CAM subsystem using the User Agent driver, and report any errors. */

if( ioctl(fd, UAGT_CAM_IO, (caddr_t)&ua_ccb) < 0 ) [1] { perror("Error on CAM UAGT Ioctl:"); close(fd); /* close the CAM file */ [2] exit(1); }

/* If the CCB completed successfully, then print out the INQUIRY information; if not, report the error. */

if (ccb.cam_ch.cam_status != CAM_REQ_CMP) { print_ccb_status( &(ccb.cam_ch) ); /* report the error values */ [3] } else { print_inq_data( &buf[0] ); /* report the INQUIRY info */ [4] } }

  1. Uses the ioctl system call to pass the local UAGT_CAM_CCB structure, ua_ccb, to the User Agent driver. The arguments passed are the file descriptor returned by the open system call; the User Agent ioctl command, UAGT_CAM_IO, which is defined in the /usr/sys/include/io/cam/uagt.h file; and the contents of the ua_ccb structure. The User Agent driver copies in the SCSI I/O CCB and sends it to the XPT layer. When the I/O has completed, the User Agent returns to the application program, returning status within the ua_ccb structure. [Return to example]

  2. If the ioctl call fails, displays an error message, closes the device special file, /dev/cam, and exits. [Return to example]

  3. If the CAM status is anything other than CAM_REQ_CMP, indicating the request completed with an error, then an error message is printed indicating the CAM status returned. [Return to example]

  4. If the request is completed, the print_inq_data routine is called to display the INQUIRY data. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.8    Print INQUIRY Data Routine

This section of the User Agent sample inquiry application program converts the rest of the fields of inquiry data to a form that a user can read and sends it to the user's screen.

/* Define the type and qualifier string arrays as globals to allow for
   compile-time initialization of the information. */

caddr_t periph_type[] = { /* Peripheral Device Type */ "Direct-access", /* 00h */ "Sequential-access", /* 01h */ "Printer", /* 02h */ "Processor", /* 03h */ "Write-once", /* 04h */ "CD-ROM", /* 05h */ "Scanner", /* 06h */ "Optical memory", /* 07h */ "Medium changer", /* 08h */ "Communications", /* 09h */ "Graphics Arts" /* 0Ah */ }; /* Same as 0A */ /* 0Bh */ /* Reserved */ /* 0Ch - 1Eh */ /* Unknown */ /* 1Fh */

caddr_t periph_qual[] = { /* Peripheral Qualifier */ "Device supported, is (may be) connected", /* 000b */ "Device supported, is not connected", /* 001b */ "<Reserved qualifier>", /* 010b */ "No device supported for this Lun" /* 011b */ }; /* Vendor specific */ /* 1xxb */

/* -------------------------------------------------------------- */ /* Local routine to print out the INQUIRY data to the user. */

void print_inq_data( ip ) [1] ALL_INQ_DATA *ip; { char vendor_id[9]; [2] char prod_id[17]; char prod_rev_lvl[5]; caddr_t periph_type_ptr, periph_qual_ptr; int ptype;

/* Make local copies of the ASCII text, so that it can be NULL terminated for the printf() routine. */

strncpy(vendor_id, (caddr_t)ip->vid, 8); [3] vendor_id[8] = '\0'; strncpy(prod_id, (caddr_t)ip->pid, 16); prod_id[16] = '\0'; strncpy(prod_rev_lvl, (caddr_t)ip->revlevel, 4); prod_rev_lvl[4] = '\0';

/* Convert sparse device type and qualifier values into strings */

ptype = ip->dtype; [4] periph_type_ptr = "Reserved"; if (ptype == 0x1F) periph_type_ptr = "Unknown"; if (ptype == 0x0B) ptype = 0x0A; if (ptype <= 0x0A) periph_type_ptr = periph_type[ptype];

periph_qual_ptr = "<Vendor Specific qualifier>"; if (ip->pqual <= 3) periph_qual_ptr = periph_qual[ip->pqual];

printf("Periph Device Type = 0x%X = %s Device\n", [5] ip->dtype, periph_type_ptr); printf("Periph Qualifier = 0x%X = %s\n", ip->pqual, periph_qual_ptr); printf("Device Type Modifier = 0x%X\tRMB = 0x%X = Medium %s\n", ip->dmodify, ip->rmb, (ip->rmb?"is removable": "is not removable")); printf("ANSI Version = 0x%X\t\tECMA Version = 0x%X\n", ip->ansi, ip->ecma); printf("ISO Version = 0x%X\t\tAENC = 0x%X\tTrmIOP = 0x%X\n", ip->iso, ip->aenc, ip->trmiop); printf("Response Data Format = 0x%X\tAddit Length = 0x%d\n", ip->rdf, ip->addlen); printf("SftRe = 0x%X\tCmdQue = 0x%X\tLinked = 0x%X\tSync = 0x%X\n", ip->sftre, ip->cmdque, ip->linked, ip->sync); printf("Wbus16 = 0x%X\tWbus32 = 0x%X\tRelAdr = 0x%X\n", ip->wbus16, ip->wbus32, ip->reladdr); printf("Vendor Identification = %s\nProduct Identification = %s\n", vendor_id, prod_id ); printf("Product Revision Level = %s\n\n", prod_rev_lvl); fflush(stdout); [6] }

  1. Declares the print_inq_data function that prints out the INQUIRY data for a valid nexus. The function's argument, ip, is a pointer to the ALL_INQ_DATA structure defined in the /usr/sys/include/io/cam/scsi_all.h file. [Return to example]

  2. Declare three character arrays to contain the vendor ID, the product ID, and the product revision level to be displayed. Each array is declared with one extra byte to hold the NULL string terminator. [Return to example]

  3. Copies the ALL_INQ_DATA member, vid, into the local array vendor_id; the ALL_INQ_DATA member, pid, into the local array prod_id; and the ALL_INQ_DATA member, revlevel, into the local array, prod_rev_lvl. The arrays are passed to the standard C library function, strncpy, which copies the data and then terminates each string copy with a NULL so that it can be output to the printf function in the format desired. [Return to example]

  4. Converts the device type and qualifier values into human-readable words. The conversions are performed on defined and undefined numeric combinations. [Return to example]

  5. Decodes and displays the inquiry data as hexadecimal numbers and strings. [Return to example]

  6. Calls the standard C I/O function, fflush, to write out the data from the internal buffers. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.9    Print CAM Status Routine

This section describes the portion of the User Agent sample inquiry application program that defines the routine to print out the CAM status for an invalid nexus.

/* -------------------------------------------------------------- */
/* Local routines and data structure to report in text and Hex
form the returned CAM status. */

struct cam_statustable { [1] u_char cam_status; caddr_t status_msg; } cam_statustable[] = { [2] { CAM_REQ_INPROG, "CCB request is in progress" }, { CAM_REQ_CMP , "CCB request completed w/out error" }, { CAM_REQ_ABORTED, "CCB request aborted by the host" }, { CAM_UA_ABORT, "Unable to Abort CCB request" }, { CAM_REQ_CMP_ERR, "CCB request completed with an err" }, { CAM_BUSY, "CAM subsystem is busy" }, { CAM_REQ_INVALID, "CCB request is invalid" }, { CAM_PATH_INVALID, "Bus ID supplied is invalid" }, { CAM_DEV_NOT_THERE, "Device not installed/there" }, { CAM_UA_TERMIO, "Unable to Terminate I/O CCB req" }, { CAM_SEL_TIMEOUT, "Target selection timeout" }, { CAM_CMD_TIMEOUT, "Command timeout" }, { CAM_MSG_REJECT_REC, "Reject received" }, { CAM_SCSI_BUS_RESET, "Bus reset sent/received" }, { CAM_UNCOR_PARITY, "Parity error occurred" }, { CAM_AUTOSENSE_FAIL, "Request sense cmd fail" }, { CAM_NO_HBA, "No HBA detected Error" }, { CAM_DATA_RUN_ERR, "Overrun/underrun error" }, { CAM_UNEXP_BUSFREE, "BUS free" }, { CAM_SEQUENCE_FAIL, "Bus phase sequence failure" }, { CAM_CCB_LEN_ERR, "CCB length supplied is inadequate" }, { CAM_PROVIDE_FAIL, "To provide requ. capability" }, { CAM_BDR_SENT, "A SCSI BDR msg was sent to target" }, { CAM_REQ_TERMIO, "CCB request terminated by the host" }, { CAM_LUN_INVALID, "LUN supplied is invalid" }, { CAM_TID_INVALID, "Target ID supplied is invalid" }, { CAM_FUNC_NOTAVAIL, "Requested function is not available" }, { CAM_NO_NEXUS, "Nexus is not established" }, { CAM_IID_INVALID, "The initiator ID is invalid" }, { CAM_CDB_RECVD, "The SCSI CDB has been received" }, { CAM_SCSI_BUSY, "SCSI bus busy" } }; int cam_statusentrys = sizeof(cam_statustable) / \ sizeof(cam_statustable[0]); [3]

char * camstatus( cam_status ) [4] register u_char cam_status; { register struct cam_statustable *cst = cam_statustable; [5] register entrys;

for( entrys = 0; entrys < cam_statusentrys; cst++ ) { [6] if( cst->cam_status == cam_status ) { return( cst->status_msg ); } } return( "Unknown CAM Status" ); }

void print_ccb_status(cp) [7] CCB_HEADER *cp; { printf( "cam_status = 0x%X\t (%s%s%s)\n", cp->cam_status, ((cp->cam_status & CAM_AUTOSNS_VALID) ? "AutoSns Valid-" : ), ((cp->cam_status & CAM_SIM_QFRZN) ? "SIM Q Frozen-" : ), camstatus( cp->cam_status & CAM_STATUS_MASK )); fflush(stdout); [8] }

  1. Defines an array of structures. It is declared as a global array to allow compile-time initialization. Each structure element of the array contains two members: cam_status, (the CAM status code) and status_msg (a brief description of the meaning of the status code). The CAM status codes and messages are defined in the /usr/sys/include/io/cam/cam.h file. [Return to example]

  2. Initialize the CAM status array with the status values and their text equivalents. [Return to example]

  3. Declares an integer variable whose contents equal the size of the total CAM status array divided by the size of an individual array element. This integer is the number of the element in the array. [Return to example]

  4. Define a function that returns a pointer to a text string with the cam_status field of the CCB_HEADER as an argument. The cam_status member is declared as a register variable so that its values are stored in a machine register for efficiency. [Return to example]

  5. Declares a register structure pointer to point to each element of the CAM status array and initializes it to point to the beginning of the CAM status array. A local register variable, entrys, is used to traverse the CAM status array. [Return to example]

  6. Examines each element in the array, incrementing cst until a match between the status from the CCB and a status value in the array is found, in which case the address of the CAM status description string, status_msg, is returned. If all the elements are examined without a match, the "Unknown CAM Status" message address is returned. [Return to example]

  7. Define a routine that uses a pointer to the CCB_HEADER structure of the INQUIRY CCB and calls the C library routine, printf, to print out the hexadecimal value and the appropriate description of the CAM status returned. [Return to example]

  8. Calls the standard C I/O function, fflush, to write out the data from the internal buffers. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.10    Sample Output for a Valid Nexus

This section contains an example of the output of the User Agent sample inquiry application program when the user enters a valid nexus.



#
inq 0 0 0

Periph Device Type = 0x0 Periph Qualifier = 0x0 [1] Device Type Modifier = 0x0 RMB = 0x0 ANSI Version = 0x1 ECMA Version = 0x0 ISO Version = 0x0 AENC = 0x0 TrmIOP = 0x0 Response Data Format = 0x1 Addit Length = 0x31 SftRe = 0x0 CmdQue = 0x0 Linked = 0x0 Sync = 0x1 Wbus16 = 0x0 Wbus32 = 0x0 RelAdr = 0x0 Vendor ID = DEC [2] Product ID = RZ56 (C) DEC [3] Product Rev Level = 0300 [4]

  1. See the American National Standard for Information Systems, Small Computer Systems Interface - 2 (SCSI - 2), X3.131-199X for a description of each of the fields of the inquiry data returned. [Return to example]

  2. Shows the value of the vendor_id variable declared in the print_inq_data routine in Section 2.5.1.8 as a local copy of the text string. [Return to example]

  3. Shows the value of the prod_id variable declared in the print_inq_data routine in Section 2.5.1.8 as a local copy of the text string. [Return to example]

  4. Shows the value of the prod_rev_lvl variable declared in the print_inq_data routine in Section 2.5.1.8 as a local copy of the text string. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.11    Sample Output for an Invalid Nexus

This section contains an example of the output of the User Agent sample inquiry application program when the user enters an invalid nexus.



#
inq 0 2 0

cam_status = 0x4A (SIM Q Frozen-Target selection timeout) [1]

  1. Shows that the contents of the cam_status member of the CCB_HEADER structure returned was CAM_SIM_QFRZN, which indicates a lack of response from the specified nexus. See the cam_statustable in Section 2.5.1.9. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.1.12    Sample Shell Script

This section contains a sample C-shell script, caminq.csh, that compiles and executes the User Agent sample inquiry application program.

# cc -o caminq caminq.c

inq 0 6 0
inq 0 2 0
inq 0 5 0
inq 0 3 0


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2    Sample User Agent Scanner Driver Program

This section contains the User Agent sample scanner program, cscan.c, with annotations to the code. It also contains the cscan.h file, which defines the WINDOW_PARAM_BLOCK structure used in the program.

The cscan.c program assumes that the environment variable SCAN-NEXUS has been set. The sample C-shell script that follows, cscan.csh, compiles the program and sets SCAN-NEXUS to bus 1, target 3, and LUN 0:

# cc -o cscan cscan.c
# setenv SCAN-NEXUS "1 3 0"


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.1    Scanner Program Header File

This section describes the header file, cscan.h, that contains definitions of structures for the program to use.

/* cscan.h   Header file for cscan.c (CAM Scanner driver)  28-Oct-1991 */

/* Scanner Window Parameter Block definition; all multi-byte quantities are defined as unsigned bytes due to the need to store the values in swapped order. */

typedef struct { u_char rsvd1[6]; /* Reserved bytes in Header: Must Be Zero */ u_char WDBLen[2]; /* Number of Window Parameter bytes following */ [1] u_char WID; /* Window ID: Must Be Zero */ u_char rsvd2; /* Reserved bytes in Header: Must Be Zero */ u_char XRes[2]; /* X-axis resolution: MUST be same as YRes */ u_char YRes[2]; /* Y-axis resolution: MUST be same as XRes */ u_char UpLeftX[4]; /* Upper left X positon of scan window */ u_char UpLeftY[4]; /* Upper left Y positon of scan window */ u_char Width[4]; /* Scan width (Y-axis length) */ u_char Length[4]; /* Scan length (X-axis length) */ u_char Bright; /* Brightness: Must Be Zero */ u_char Thresh; /* Threshold: Must Be Zero */ u_char Contrast; /* Contrast: Must Be Zero */ u_char ImgTyp; /* Image type: 0 = bi-level mono; 2 = multi-level mono; 3 = bi-level full color; 5 = multi- level full color; others reserved */ u_char PixBits; /* Bits per pixel: 1 = bi-level; 4 = 16 shades; 8 = 256 shades; others reserved */ u_char HalfTone[2];/* Halftone Pattern: Must Be Zero */ u_char PadTyp:3; /* Padding type for non-byte pixels: MUST BE 1 */ u_char rsvd3:4; /* Reserved bits: Must Be Zero */ u_char RevImg:1; /* 0 = normal image; 1 = reverse image */ u_char BitOrder[2];/* Bit ordering: Must Be Zero */ u_char CompTyp; /* Compression type: Must Be Zero */ u_char CompArg; /* Compression argument: Must Be Zero */ u_char rsvd4[6]; /* Reserved: Must Be Zero */ u_char HdrSel; /* Header select (return with data): 0 = no header; 1 = return header with data; others reserved */ u_char ColorSel; /* Color select (selects color to use when doing a mono-color scan): 0 = default to Green; 1 = scan using Red; 2 = scan using Green; 3 = scan using Blue; others reserved */ u_char ImgCorr; /* Image data correction method: 0 = default to normal; 1 = soft image; 2 = enhance (low); 3 = enhance (high); others reserved */ u_char ThreshR; /* Threshold level, Red: 0 = default level */ u_char ThreshG; /* Threshold level, Green: 0 = default level */ u_char ThreshB; /* Threshold level, Blue: 0 = default level */ u_char ShtTyp:1; /* Sheet type: 0 = reflection; 1 = transparency */ u_char rsvd5:3; /* Reserved bits: Must Be Zero */ u_char ShtDen:4; /* Sheet density (transparency): 0 = normal; 1 = light; 2 = dark; others reserved */ }WINDOW_PARAM_BLOCK;

  1. The length (in bytes) of a single scan window descriptor. The first 48 bytes are defined in the American National Standard for Information Systems, Small Computer Systems Interface - 2 (SCSI - 2), X3.131-199X and the remaining bytes are vendor specific. The specific structure members used may depend on the scanner device. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.2    The Include Files Section

This section, which is the beginning of the cscan program, describes the portion of the User Agent sample scanner program that lists the include files for the program.

/* ------------------------------------------------------------------ */
/* Include files needed for this program. */

#include <stdio.h> #include <unistd.h> #include <sys/file.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/uio.h> #include <strings.h> #include <ctype.h> #include <math.h> #include <io/common/iotypes> #include <io/cam/cam.h> /* CAM defines from the CAM document */ #include <io/cam/dec_cam.h> /* CAM defines for Digital CAM source files */ #include <io/cam/uagt.h> /* CAM defines for the UAgt driver */ #include <io/cam/scsi_all.h> /* CAM defines for ALL SCSI devices */ #include "cscan.h" /* Scanner structure definitions */


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.3    CDB Setup Section

This section describes the portion of the User Agent sample scanner program that defines the CDBs for the program.

/* The Define Window Parameters CDB (10 bytes).  */

typedef struct { u_char opcode; /* 24 hex */ u_char : 5, /* 5 bits reserved */ lun : 3; /* logical unit number */ u_char : 8; /* Reserved byte */ u_char : 8; /* Reserved byte */ u_char : 8; /* Reserved byte */ u_char : 8; /* Reserved byte */ u_char param_len2; /* MSB parameter list length */ [1] u_char param_len1; /* parameter list length */ u_char param_len0; /* LSB parameter list length */ u_char control; /* The control byte */ }SCAN_DEF_WIN_CDB;

/* The Define Window Parameters op code */

#define SCAN_DEF_WIN_OP 0x24

/* The Read (data or gamma table) CDB (10 bytes). */

typedef struct { u_char opcode; /* 28 hex */ u_char : 5, /* 5 bits reserved */ lun : 3; /* logical unit number */ u_char tran_type; /* transfer data type: */ /* 0=data, 3=gamma */ [2] u_char : 8; /* Reserved byte */ u_char tran_id1; /* MSB transfer identification */ [3] u_char tran_id2; /* LSB trans id: */ /* 0 =data, 1/2/3= gamma */ u_char param_len2; /* MSB parameter list length */ u_char param_len1; /* parameter list length */ u_char param_len0; /* LSB parameter list length */ u_char control; /* The control byte */ }SCAN_READ_CDB;

/* The Read (data or gamma table) op code */

#define SCAN_READ_OP 0x28

  1. Specify the number of bytes sent during the DATAOUT phase. The parameters are usually mode parameters, diagnostic parameters, and log parameters that are sent to a target. If set to 0 (zero), no data is to be transferred. [Return to example]

  2. The types of data that are to be read. The choices are image data scan lines or gamma correction table data. [Return to example]

  3. These two bytes are used with the transfer type byte to indicate that the data to be read is image scan lines, 0 (zero), or one of the following types of gamma correction table data: red, 1; green, 2; or blue, 3. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.4    Definitions Section

This section describes the portion of the User Agent sample scanner program that specifies the local definitions and initializes data.

/* ------------------------------------------------------------- */
/* Local defines */
#define SENSE_LEN	18	/* max sense length from scanner */ [1]

/* ------------------------------------------------------------- */ /* Initialized and uninitialized data. */

u_char sense[ SENSE_LEN ]; [2]

  1. Defines a constant of 18 bytes for the length of the sense data from the scanner. [Return to example]

  2. Declares a character array, sense, with a size of 18 bytes as defined by the SENSE_LEN constant. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.5    Main Program Section

This section describes the main program portion of the User Agent sample scanner program.

/* ----------------------------------------------------------------- */
/* The main code path.  The CCB/CDB and UAGT_CAM_CCB are set up for the
   DEFINE WINDOW PARAMETERS and READ commands to the Bus/Target/LUN. */

UAGT_CAM_CCB ua_ccb_sim_rel; /* uagt structure */ [1] /* for the RELEASE SIMQUE CCB */ CCB_RELSIM ccb_sim_rel; /* RELEASE SIMQUE CCB */ [2] UAGT_CAM_CCB ua_ccb_reset_dev; /* uagt structure */ [3] /* for the RESET DEVICE CCB */ CCB_RESETDEV ccb_reset_dev; /* RESET DEVICE CCB */ [4]

UAGT_CAM_CCB ua_ccb; /* uagt structure */ [5] /* for the SCSI I/O CCB */ CCB_SCSIIO ccb; /* SCSI I/O CCB */ [6]

main(argc, argv,envp) int argc; char *argv[]; char *envp[]; {

/*------------------------------------------------------------------ */ /* Local variables and structures */

extern void clear_mem(); [7] extern void swap_short_store(); extern void swap_long_store();

u_char id, targid, lun; /* from envir variable SCAN-NEXUS */ [8] char *cp; int nexus;

int fd; /* unit number for the CAM open */ [9] int od; /* unit number for the file open */ [10] char FileHead[200]; /* buffer for file header info */ int i, n; u_char *bp; /* general usage byte pointer */ int retry_cnt; /* error retry counter */ int reset_flag; /* flag to indicate reset tried */

double Xwid, Ylen; /* scan area in inches */ [11] u_short WXYRes; /* variables for window calculations */ u_long WWidth, WLength, WinPix, LineBytes, TotalBytes; [12] u_char WHdrSel; [13]

SCAN_DEF_WIN_CDB *win; /* pointer for window def CDB */ [14] SCAN_READ_CDB *read; /* pointer for read CDB */ [15]

WINDOW_PARAM_BLOCK Window; /* parameter block, window def */ [16]

u_char ReadData[ 400*12*3 ]; /* Max bytes/line */ [17] u_char *RDRp, *RDGp, *RDBp; /* Red, Green, Blue pointers */ u_char WriteData[ 400*12*3 ]; /* Max bytes/line */ [18] u_char *WDp; /* WriteData pointer */

  1. Declares a global data structure, ua_ccb_sim_rel, to be used with the RELEASE SIM QUEUE CCB for the UAGT_CAM_IO ioctl command. [Return to example]

  2. Declares a global data structure, ccb_sim_rel, of the type CCB_RELSIM, which is defined in the file /usr/sys/include/io/cam/cam.h. [Return to example]

  3. Declares a global data structure, ua_ccb_reset_dev, to be used for the BUS DEVICE RESET CCB for the UAGT_CAM_IO ioctl command. [Return to example]

  4. Declares a global data structure, ccb_reset_dev, of the type CCB_RESETDEV, which is defined in the file /usr/sys/include/io/cam/cam.h. [Return to example]

  5. Declares a global data structure, ua_ccb, of the type UAGT_CAM_CCB, which is defined in the file /usr/sys/include/io/cam/uagt.h. This structure is copied from user space into kernel space as part of the ioctl system call for the UAGT_CAM_IO ioctl command. [Return to example]

  6. Declares a global data structure, ccb, of the type CCB_SCSIIO, which is defined in the file /usr/sys/include/io/cam/cam.h. [Return to example]

  7. Declare routines that are used later in the program. The routines are defined in Section 2.5.2.14. [Return to example]

  8. The bus, target, and LUN are specified in octal digits in the SCAN-NEXUS environment variable. The value for the LUN should be 0 (zero). [Return to example]

  9. The file descriptor for the User Agent driver returned by the open system call, which executes in Section 2.5.2.7. [Return to example]

  10. The file descriptor for the output file returned by the open system call, which executes in Section 2.5.2.7. [Return to example]

  11. Real values to contain the X and Y dimensions of the scan window. [Return to example]

  12. Variables to hold calculated information about the scan window. [Return to example]

  13. Variable to hold the flag bytes that indicate whether the window header is to be returned with the data. The value of the variable is stored in the HdrSel member of the WINDOW_PARAM_BLOCK structure and is set to 1. The WINDOW_PARAM_BLOCK is defined in Section 2.5.2.1. [Return to example]

  14. Declares a pointer to the data structure SCAN_DEF_WIN_CDB, which is defined in Section 2.5.2.3. [Return to example]

  15. Declares a pointer to the data structure SCAN_READ_CDB, which is defined in Section 2.5.2.3. [Return to example]

  16. Declares an uninitialized local data structure, Window, of the type WINDOW_PARAM_BLOCK, which is defined in Section 2.5.2.1. [Return to example]

  17. Declares an array to contain a scan line of the maximum size that can be read, which is 14,400 bytes. This array is used to read a scan line from the scanner. [Return to example]

  18. Declares an array large enough to contain the maximum-size scan line, which is 14,400 bytes. This array is used to write the scan line, converted to 3-byte pixels, to the output file. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.6    Nexus Conversion Section

This section describes the portion of the User Agent sample scanner program where the nexus information contained in the SCAN-NEXUS environment variable is converted to the values for bus, target, and LUN.

/* Find the environment variable SCAN-NEXUS. If not found, return
   error message.  If found, convert the nexus information from the
   variable to bus, target ID and LUN values.  Return an error
   message if any of the values are not octal digits. */

nexus = 0; /* Reset valid data flag */ for (i=0; envp[i] != NULL; i++) { cp = envp[i]; [1] if (strncmp(cp,"SCAN-NEXUS=",11) == 0) /* Find environment variable */ { nexus = -1; /* Set tentative flag */ cp += 11; /* Advance to data */ if (*cp < '0' || *cp > '7') break; [2] id = (u_char)(*cp++) - (u_char)('0'); if (*cp++ != ' ') break; if (*cp < '0' || *cp > '7') break; targid = (u_char)(*cp++) - (u_char)('0'); if (*cp++ != ' ') break; if (*cp < '0' || *cp > '7') break; lun = (u_char)(*cp) - (u_char)('0'); nexus = 1; /* Set good data flag */ } } if (nexus == -1) [3] { printf("Invalid SCAN-NEXUS; set to octal digits 'bus target lun'\n"); exit(1); } if (nexus == 0) [4] { printf("Set environment variable SCAN-NEXUS to 'bus target lun' (octal\ digits)\n\n"); exit(1); } printf("Scanner nexus set to: bus %d, target %d, LUN %d\n\n",id, \ targid, lun); [5]

  1. Scans through all of the environment variables passed to the program by the system, looking for the variable SCAN-NEXUS. [Return to example]

  2. Checks to make sure SCAN-NEXUS contains octal digits for bus, target, and LUN. [Return to example]

  3. Appears if the digits are not octal. [Return to example]

  4. Appears if SCAN-NEXUS is not set. [Return to example]

  5. Displays the values for bus, target, and LUN. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.7    Parameter Assignment Section

This section describes the portion of the User Agent sample scanner program that assigns the parameters entered by the user on the command line to the appropriate variables and opens the necessary files.

/* Make sure that the correct number of arguments are present.
   If not, return an error message with usage information. */

if (argc != 5) { [1] printf("Usage is: cscan XYres Xwid Ylen out_file\n"); printf(" XYres is integer pix/inch; Xwid & Ylen are real \ inches\n\n"); exit(); }

/* Convert the parameter information from the command line. */

WXYRes = atoi(argv[1]); /* X & Y resolution */ Xwid = atof(argv[2]); /* X width in inches */ Ylen = atof(argv[3]); /* Y length in inches */

/* Verify that the X & Y resolution is one of the legal values */

switch (WXYRes) [2] { case 25: case 150: case 200: case 300: case 400: break; default: printf("Illegal X & Y resolution; must be 25, 150, 200, \ 300, 400\n"); exit(1); }

/* Verify that the X width is positive and less than 11.69 inches */ [3]

if (Xwid < 0 || Xwid > 11.69) { printf("X width must be positive and less than 11.69 inches\n"); exit(1); }

/* Verify that the Y length is positive and less than 17.00 inches */

if (Ylen < 0 || Ylen > 17.00) { printf("Y length must be positive and less than 17.00 inches\n"); exit(1); }

/* Open the output file ("truncating" it if it exists) and report */ /* any errors. */ [4]

if ((od = open(argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 ) { perror("Error on Output File Open"); exit(1); }

/* Open the User Agent driver and report any errors. */

if ((fd = open("/dev/cam", O_RDWR, 0)) < 0 ) { perror("Error on CAM UAgt Open"); exit(1); }

  1. The user enters the X and Y scan resolutions in pixels per inch, the width (X) and length (Y) of the scan area in inches, and the name of the output file on the command line. [Return to example]

  2. Checks for the legal scan resolutions the user can enter. [Return to example]

  3. Check that the user entered legal values for X and Y. [Return to example]

  4. Open the User Agent driver and the output file. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.8    Data Structure Setup Section

This section describes the portion of the User Agent sample scanner program that sets up the data structures for the XPT_REL_SIMQ and XPT_RESET_DEV commands.

/* - Begin static setups of SIMQ Release and Device Reset structures - */

/* Set up the CCB for an XPT_REL_SIMQ request. */

/* Set up the CAM header for the XPT_REL_SIMQ function. */

ccb_sim_rel.cam_ch.my_addr = (struct ccb_header *)&ccb_sim_rel; /* "Its" address */ [1] ccb_sim_rel.cam_ch.cam_ccb_len = sizeof(CCB_RELSIM); /* a SIMQ release */ ccb_sim_rel.cam_ch.cam_func_code = XPT_REL_SIMQ; /* the opcode */ ccb_sim_rel.cam_ch.cam_path_id = id; /* selected bus */ ccb_sim_rel.cam_ch.cam_target_id = targid; /* selected target */ ccb_sim_rel.cam_ch.cam_target_lun = lun; /* selected lun */

/* The needed CAM flags are: CAM_DIR_NONE - No data */ /* will be transferred. */

ccb_sim_rel.cam_ch.cam_flags = CAM_DIR_NONE;

/* Set up the fields for the User Agent Ioctl call. */

ua_ccb_sim_rel.uagt_ccb = (CCB_HEADER *)&ccb_sim_rel; /* where the CCB is */ [2] ua_ccb_sim_rel.uagt_ccblen = sizeof(CCB_RELSIM); /* bytes in CCB */ ua_ccb_sim_rel.uagt_buffer = (u_char *)NULL; /* no data */ ua_ccb_sim_rel.uagt_buflen = 0; /* no data */ ua_ccb_sim_rel.uagt_snsbuf = (u_char *)NULL; /* no Autosense data */ ua_ccb_sim_rel.uagt_snslen = 0; /* no Autosense data */ ua_ccb_sim_rel.uagt_cdb = (CDB_UN *)NULL; /* CDB is in the CCB */ ua_ccb_sim_rel.uagt_cdblen = 0; /* CDB is in the CCB */

/* Set up the CCB for an XPT_RESET_DEV request. */

/* Set up the CAM header for the XPT_RESET_DEV function. */

ccb_reset_dev.cam_ch.my_addr = (struct ccb_header *)&ccb_reset_dev; /* "Its" address */ [3] ccb_reset_dev.cam_ch.cam_ccb_len = sizeof(CCB_RESETDEV); /* a SCSI I/O CCB */ ccb_reset_dev.cam_ch.cam_func_code = XPT_RESET_DEV; /* the opcode */ ccb_reset_dev.cam_ch.cam_path_id = id; /* selected bus */ ccb_reset_dev.cam_ch.cam_target_id = targid; /* selected target */ ccb_reset_dev.cam_ch.cam_target_lun = lun; /* selected lun */

/* The needed CAM flags are: CAM_DIR_NONE - No data will */ /* be transferred. */

ccb_reset_dev.cam_ch.cam_flags = CAM_DIR_NONE;

/* Set up the fields for the User Agent Ioctl call. */

ua_ccb_reset_dev.uagt_ccb = (CCB_HEADER *)&ccb_reset_dev; /* where the CCB is */ [4] ua_ccb_reset_dev.uagt_ccblen = sizeof(CCB_RESETDEV); /* bytes in CCB */ ua_ccb_reset_dev.uagt_buffer = (u_char *)NULL; /* no data */ ua_ccb_reset_dev.uagt_buflen = 0; /* no data */ ua_ccb_reset_dev.uagt_snsbuf = (u_char *)NULL; /* no Autosense data */ ua_ccb_reset_dev.uagt_snslen = 0; /* no Autosense data */ ua_ccb_reset_dev.uagt_cdb = (CDB_UN *)NULL; /* CDB is in the CCB */ ua_ccb_reset_dev.uagt_cdblen = 0; /* CDB is in the CCB */ /* -- End of static setups of SIMQ Release and Device */ /* Reset structures -- */

  1. Fills in some of the CCB_HEADER fields of the CCB_RELSIM structure defined as ccb_sim_rel for the XPT_REL_SIMQ command. The structure was declared in Section 2.5.1.2. [Return to example]

  2. Fills in the UAGT_CAM_CCB structure defined as ua_ccb_sim_rel for the UAGT_CAM_IO ioctl command. The structure was declared in Section 2.5.1.2. [Return to example]

  3. Fills in some of the CCB_HEADER fields of the CCB_RESETDEV structure defined as ccb_reset_dev for the XPT_RESET_DEV command. The structure was declared in Section 2.5.1.2. [Return to example]

  4. Fills in the UAGT_CAM_IO structure defined as ua_ccb_reset_dev for the UAGT_CAM_IO ioctl command. The structure was declared in Section 2.5.1.2. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.9    Window Parameters Setup Section

This section describes the portion of the User Agent sample inquiry application program that fills in the scan window parameters and sends a SCSI SET WINDOW PARAMETERS command to the scanner.

/* Fill in window parameters for scanner and send DEFINE WINDOW */
/* PARAMETERS command to the scanner.  Note that the X&Y resolution */
/* and the X width and Y length are specified on the command line. */

WWidth = Xwid*(double)WXYRes; /* X width inches to pixels */ [1] WLength = Ylen*(double)WXYRes; /* Y length inches to lines */ WHdrSel = 0; /* Don't return header */ #ifdef NO_HEADER_FOR_NOW WHdrSel = 1; /* Return header w. data */ #endif

WinPix = WWidth*WLength; /* Pixels in window */ [2] LineBytes = WWidth*3; /* Full color, 8-bit pixels */ TotalBytes = WHdrSel*256 + WinPix*3; /* Full color, 8-bit pixels */

printf("Window parameters:\n"); [3] printf(" Width = %6d pixels/line, Length = %6d lines; Total = %10d pixels\n", WWidth, WLength, WinPix); printf(" Bytes/line = %6d; Total bytes/image = %10d\n", LineBytes, TotalBytes);

/* Fill in window parameters for scanner and send DEFINE WINDOW PARAMETERS */ /* command to the scanner. */

clear_mem(&Window, sizeof(Window)); /* Clear whole DWP block */ [4] swap_short_store(&Window.WDBLen[0], 0x2F); /* REQUIRED length */ [5] swap_short_store(&Window.XRes[0], WXYRes); /* X and Y MUST BE THE SAME */ swap_short_store(&Window.YRes[0], WXYRes); /* X and Y MUST BE THE SAME */ /* Upper Left X & Y left at zero */ swap_long_store(&Window.Width[0], WWidth); swap_long_store(&Window.Length[0], WLength); Window.ImgTyp = 5; /* Multi-level full color */ [6] Window.PixBits = 8; /* 8-bit pixels */ [7] Window.PadTyp = 1; /* REQUIRED value */ [8] Window.RevImg = 1; /* Reverse == 0,0,0 = black */ [9] Window.HdrSel = WHdrSel; /* Set return header control */ [10] /* All other values left at zero */

/* Display current contents of bytes in window parameter block */ [11]

printf("Window Parameter block (in hex):\n"); for( i=0, bp=(u_char *)&Window; i < sizeof(Window); i++, bp++) { printf("%.2x ", *bp); if (i == 7) printf("\n"); if (i == 8+21) printf("\n"); } printf("\n\n" );

  1. Converts the X and Y values entered from the command line in inches into pixels. The value of WXYRes is an int; however, the values of Xwid and Ylen are floating-point values. To perform the calculations to determine the values of WWidth, the number of pixels per line, and WLength, the number of scan lines, the value of WXYRes must be converted to a real number. For example, if the value entered for X were 4.5 and the resolution selected were 300, WWidth would equal 1,350 pixels per line. If the value entered for Y were 3.5, the result would be 1,050 scan lines. [Return to example]

  2. Calculates the number of bytes in the scan window based on the total number of pixels. For example, using the previous figures, the calculation would yield 1,417,500 pixels as the value of WinPix. To calculate the number of bytes per line, WWidth is multiplied by 3, which is the number of bytes per pixel. The total number of bytes in the scan window, using the figures in the example, would be 4,252,500 bytes. [Return to example]

  3. Display the results of the calculations. [Return to example]

  4. Calls the clear_mem function to set the local WINDOW_PARAM_BLOCK structure, Window, to 0s (zeroes) in preparation for storing the byte values in swapped order. The WINDOW_PARAM_BLOCK structure was defined in Section 2.5.2.1. The clear_mem function is defined in Section 2.5.2.14. [Return to example]

  5. Calls the functions that put the bytes of short and long integer values into big-endian storage. The functions are defined in Section 2.5.2.14. [Return to example]

  6. Sets the image type for the scanner. The setting of 5 means multilevel, full color. [Return to example]

  7. Sets the number of bits per pixel. The setting of 8 means 256 shades. [Return to example]

  8. Sets the padding type for nonbyte pixels. The setting of 1 means pad with 0 (zero). [Return to example]

  9. Sets the reverse image. The setting of 1 means white pixels are indicated by 1 (one) and black pixels are indicated by 0 (zero). [Return to example]

  10. Sets the selection for returning a header with the data. The setting of WHdrSel was set to 0 (do not include the header). [Return to example]

  11. Displays the contents of the bytes in the window parameter block. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.10    CCB Setup for the DEFINE WINDOW Command

This section describes the portion of the User Agent sample scanner program where the fields of the CCB_HEADER needed for an XPT_SCSI_IO request are filled in.

/* Set up the CCB for an XPT_SCSI_IO request.  The DEFINE WINDOW
   PARAMETERS command will be sent to the device.  */

/* Set up the CAM header for the XPT_SCSI_IO function. */

ccb.cam_ch.my_addr = (struct ccb_header *)&ccb; /* "Its" address */ [1] ccb.cam_ch.cam_ccb_len = sizeof(CCB_SCSIIO); /* a SCSI I/O CCB */ ccb.cam_ch.cam_func_code = XPT_SCSI_IO; /* the opcode */ ccb.cam_ch.cam_path_id = id; /* selected bus */ ccb.cam_ch.cam_target_id = targid; /* selected target */ ccb.cam_ch.cam_target_lun = lun; /* selected lun */

/* The needed CAM flags are: CAM_DIR_OUT - The data will go to the target. */

ccb.cam_ch.cam_flags = CAM_DIR_OUT;

/* Set up the rest of the CCB for the DEFINE WINDOW PARAMETERS command. */

ccb.cam_data_ptr = (u_char *)&Window; /* where the parameters are */ [2] ccb.cam_dxfer_len = sizeof(Window); /* how much data */ [3] ccb.cam_timeout = CAM_TIME_DEFAULT; /* use the default timeout */ [4] ccb.cam_cdb_len = sizeof(SCAN_DEF_WIN_CDB); /* how many bytes for cdb */ [5] ccb.cam_sense_ptr = &sense[0]; /* Autosense data area */ ccb.cam_sense_len = SENSE_LEN; /* Autosense data length */

/* Use a local pointer to access the fields in the DEFINE WINDOW PARAMETERS CDB. */

win = (SCAN_DEF_WIN_CDB *)&ccb.cam_cdb_io.cam_cdb_bytes[0]; [6]

clear_mem(win,sizeof(SCAN_DEF_WIN_CDB)); /* clear all bits in CDB */ [7] win->opcode = SCAN_DEF_WIN_OP; /* define window command */ [8] win->lun = lun; /* lun on target */ win->param_len0 = sizeof(Window); /* for the buffer space */ win->param_len1 = 0; win->param_len2 = 0; win->control = 0; /* no control flags */

/* Set up the fields for the User Agent Ioctl call. */ [9]

ua_ccb.uagt_ccb = (CCB_HEADER *)&ccb; /* where the CCB is */ [10] ua_ccb.uagt_ccblen = sizeof(CCB_SCSIIO); /* how many bytes to gather */ [11] ua_ccb.uagt_buffer = (u_char *)&Window; /* where the parameters are */ [12] ua_ccb.uagt_buflen = sizeof(Window); /* how much data */ [13]

ua_ccb.uagt_snsbuf = &sense[0]; /* Autosense data area */ [14] ua_ccb.uagt_snslen = SENSE_LEN; /* Autosense data length */ ua_ccb.uagt_cdb = (CDB_UN *)NULL; /* CDB is in the CCB */ [15] ua_ccb.uagt_cdblen = 0; /* CDB is in the CCB */

  1. Fills in some of the CCB_HEADER fields of the SCSI I/O CCB structure defined as ccb, for processing by the XPT layer. The structure was declared in Section 2.5.1.2. [Return to example]

  2. Assigns the cam_data_ptr member of the local CCB_SCSIIO data structure, ccb, to the address of the Window parameter block. The Window parameter block structure was filled in Section 2.5.2.9. [Return to example]

  3. Sets the data transfer length to the length of the Window structure. [Return to example]

  4. Specifies using the default timeout, which is the value assigned to the CAM_TIME_DEFAULT constant. This constant is set in the /usr/sys/include/io/cam/cam.h file to indicate that the SIM layer's default timeout is to be used. The current value of the SIM layer's default timeout is 5 seconds. [Return to example]

  5. Sets the length of the cam_cdblen member to the length of the SCAN_DEF_WIN_CDB structure. [Return to example]

  6. Assigns the win pointer, which is type SCAN_DEF_WIN_CDB, to the address of the cam_cdb_bytes member of the CDB_UN union. This union is defined in /usr/sys/include/io/cam/cam.h as the cam_cdb_io member of the SCSI I/O CCB structure. [Return to example]

  7. Calls the clear_mem function to clear the local SCAN_DEF_WIN_CDB structure in preparation for storing the values needed for the DEFINE WINDOW operation. The SCAN_DEF_WIN_CDB structure is defined in Section 2.5.2.3. The clear_mem function is defined in Section 2.5.2.14. [Return to example]

  8. Use the win pointer to access the bytes of the cam_cdb_bytes array as though it is a SCAN_DEF_WIN_CDB structure. The SCAN_DEF_WIN_CDB structure is defined in Section 2.5.2.3. [Return to example]

  9. Assigns the program address of the CCB into the CCB pointer member and the program address of the Window parameter block into the data pointer member of the ua_ccb structure of type UAGT_CAM_CCB, as defined in the /usr/sys/include/io/cam/uagt.h file. This structure is copied from user space into kernel space as part of the ioctl system call that is executed in Section 2.5.2.11. This structure was declared in Section 2.5.2.3. [Return to example]

  10. Initializes the uagt_ccb member of the ua_ccb structure with the address of the local CCB_SCSIIO structure, ccb. [Return to example]

  11. Sets the length of the uagt_ccblen member to the length of the SCSI I/O CCB structure that will be used for this call. [Return to example]

  12. Initializes the uagt_buffer member with the user space address of the Window parameter block. [Return to example]

  13. Initializes the uagt_buflen member with the number of bytes in the Window parameter block. [Return to example]

  14. Reflect that the autosense features are turned on in the CAM flags. [Return to example]

  15. Reflect that the CDB information is in the SCSI I/O CCB structure filled in Section 2.5.1.2. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.11    Error Checking Section

This section describes the portion of the User Agent sample scanner program that attempts to set the window parameters and recover from possible scanner errors.

/* Send the CCB to the CAM subsystem using the User Agent driver.
   If an error occurs, report it and attempt corrective action. */

retry_cnt = 10; /* initialize retry counter */ reset_flag = 0; /* initialize reset flag */

retry_SWP: printf("Attempt to Set Window Parameters\n"); if( ioctl(fd, UAGT_CAM_IO, (caddr_t)&ua_ccb) < 0 ) [1] { perror("Error on CAM UAgt Ioctl to Define Window Parameters"); close(fd); /* close the CAM file */ exit(1); }

/* If the CCB did not complete successfully then report the error. */

if ((ccb.cam_ch.cam_status & CAM_STATUS_MASK) != CAM_REQ_CMP) { print_ccb_status("CAM UAgt Define Window Ioctl", &(ccb.cam_ch) ); /* report the error values */ printf(" cam_scsi_status = 0x%.2X\n", ccb.cam_scsi_status); [2]

/* 1st check if the SIM Queue is frozen. If it is, release it. */

if (ccb.cam_ch.cam_status & CAM_SIM_QFRZN) { printf("Attempt to release SIM Queue\n"); if( ioctl(fd, UAGT_CAM_IO, (caddr_t)&ua_ccb_sim_rel) < 0 ) { [3] perror("Error on CAM UAgt Release Sim Queue Ioctl"); close(fd); /* close the CAM file */ exit(1); }

/* If the Release Sim Q CCB did not complete successfully then report the error and exit. */

print_ccb_status("CAM UAgt Release SIM Queue Ioctl", &(ccb_sim_rel.cam_ch) ); /* report the error values */

if (ccb_sim_rel.cam_ch.cam_status != CAM_REQ_CMP) { print_ccb_status("CAM UAgt Release SIM Queue Ioctl", &(ccb_sim_rel.cam_ch) ); /* report the error values */ [4] close(fd); /* close the CAM file */ exit(1); } }

/* Next, if we haven't done one yet, attempt a device reset to clear any device error. */

if (reset_flag++ == 0) { printf("Attempt to Reset the scanner\n"); if( ioctl(fd, UAGT_CAM_IO, (caddr_t)&ua_ccb_reset_dev) < 0 ) { [5] perror("Error on CAM UAgt Device Reset Ioctl"); close(fd); /* close the CAM file */ exit(1); }

/* If the Reset Device CCB did not complete successfully then report the error and exit. */

print_ccb_status("CAM UAgt Device Reset Ioctl", &(ccb_reset_dev.cam_ch) ); /* report the error values */

if (ccb_reset_dev.cam_ch.cam_status != CAM_REQ_CMP) { [6] print_ccb_status("CAM UAgt Device Reset Ioctl", &(ccb_reset_dev.cam_ch) ); /* report the error values */ close(fd); /* close the CAM file */ exit(1); }

/* Wait the 28 seconds that the scanner takes to come back to life after a reset; no use to do anything else. */

printf("Scanner was reset. Wait 28 Seconds for it to recover...\n"); sleep(28); }

/* Last, count if all retries are used up. If not, try the SWP again. If so, give up and exit. */

printf("Retry counter value = %d\n",retry_cnt); if (retry_cnt-- > 0) goto retry_SWP;

close(fd); /* close the CAM file */ exit(1); } else {

/* Output status information on success for debugging. */

print_ccb_status("CAM UAgt SET WINDOW PARAMETERS Ioctl", &(ccb.cam_ch) ); /* report the error values */ printf(" cam_scsi_status = 0x%.2X\n", ccb.cam_scsi_status); printf("\nWindow parameter set up successful\n"); }

/* Output header information (magic number, informational comment, X and Y dimensions and maximum pixel values) to the data file and display it for the user. */

sprintf(FileHead,"P6\n\# X&Y resolution = %d dpi, %d pixels/line, \ %d lines", [7] WXYRes,WWidth,WLength); sprintf(strchr(FileHead,NULL),"\n%d %d 255\n",WWidth,WLength); write(od,FileHead,strlen(FileHead)); printf("File header data --\n%s\n",FileHead);

  1. Attempts to set the window parameters. This line uses the ioctl system call to pass the local UAGT_CAM_CCB structure, ua_ccb, to the User Agent driver. The arguments passed are the file descriptor returned by the open system call; the User Agent ioctl command, UAGT_CAM_IO, which is defined in the /usr/sys/include/io/cam/uagt.h file; and the contents of the ua_ccb structure. The User Agent driver copies in the SCSI I/O CCB and sends it to the XPT layer. When the I/O has completed, the User Agent returns to the application program, returning status within the ua_ccb structure. [Return to example]

  2. If the CAM status is anything other than CAM_REQ_CMP, indicating the request has completed, an error message is printed indicating the CAM status returned. [Return to example]

  3. Attempts to clear the SIM queue if it is frozen. This line uses the ioctl system call to pass the local UAGT_CAM_CCB structure, ua_ccb_sim_rel, to the User Agent driver. The arguments passed are the file descriptor returned by the open system call; the User Agent ioctl command, UAGT_CAM_IO, which is defined in the /usr/sys/include/io/cam/uagt.h file; and the contents of the ua_ccb_sim_rel structure. The User Agent driver copies in the SCSI I/O CCB and sends it to the XPT layer. When the operation has completed, the User Agent returns to the application program, returning status within the ua_ccb structure. [Return to example]

  4. If the CAM status is anything other than CAM_REQ_CMP, indicating the request has completed, an error message is printed indicating the CAM status returned. An error message is displayed and the program exits. [Return to example]

  5. Attempts a device reset. This line uses the ioctl system call to pass the local UAGT_CAM_CCB structure, ua_ccb_reset_dev, to the User Agent driver. The arguments passed are the file descriptor returned by the open system call; the User Agent ioctl command, UAGT_CAM_IO, which is defined in the /usr/sys/include/io/cam/uagt.h file; and the contents of the ua_ccb_reset_dev structure. The User Agent driver copies in the SCSI I/O CCB and sends it to the XPT layer. When the operation has completed, the User Agent returns to the application program, returning status within the ua_ccb structure. [Return to example]

  6. If the CAM status is anything other than CAM_REQ_CMP, indicating the request has completed, an error message is printed indicating the CAM status returned. An error message is displayed and the program exits. [Return to example]

  7. If the scan window parameters were set up successfully, a portable pixmap P6 file is created. This section displays the X and Y resolutions in dots per inch, pixels per line, and number of lines, taking the values that were generated from the code in Section 2.5.2.9. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.12    CCB Setup for the READ Command

This section describes the portion of the User Agent sample scanner application program that sets up the CCBs for a READ command.

/* Set up the CCB for an XPT_SCSI_IO request.  The READ (data) command
     will be sent to the device.  */

/* Set up the CAM header for the XPT_SCSI_IO function. */

ccb.cam_ch.my_addr = (struct ccb_header *)&ccb; /* "Its" address */ [1] ccb.cam_ch.cam_ccb_len = sizeof(CCB_SCSIIO); /* a SCSI I/O CCB */ ccb.cam_ch.cam_func_code = XPT_SCSI_IO; /* the opcode */ ccb.cam_ch.cam_path_id = id; /* selected bus */ ccb.cam_ch.cam_target_id = targid; /* selected target */ ccb.cam_ch.cam_target_lun = lun; /* selected lun */

/* The needed CAM flags are: CAM_DIR_IN - The data will come from the target. */

ccb.cam_ch.cam_flags = CAM_DIR_IN;

/* Set up the rest of the CCB for the READ command. */

ccb.cam_data_ptr = (u_char *)ReadData; /* where the data goes */ [2] ccb.cam_dxfer_len = LineBytes; /* how much data */ ccb.cam_timeout = 100; /* use timeout of 100Sec */ ccb.cam_cdb_len = sizeof( SCAN_READ_CDB ); /* how many bytes for read */ [3] ccb.cam_sense_ptr = &sense[0]; /* Autosense data area */ ccb.cam_sense_len = SENSE_LEN; /* Autosense data length */

/* Use a local pointer to access the fields in the DEFINE WINDOW PARAMETERS CDB. */

read = (SCAN_READ_CDB *)&ccb.cam_cdb_io.cam_cdb_bytes[0]; [4]

clear_mem(read,sizeof(SCAN_READ_CDB)); /* clear all bits in CDB */ [5] read->opcode = SCAN_READ_OP; /* define window command */ read->lun = lun; /* lun on target */ read->param_len0 = LineBytes&255; /* for the buffer space */ read->param_len1 = (LineBytes>>8)&255; read->param_len2 = (LineBytes>>16)&255; read->control = 0; /* no control flags */

/* Set up the fields for the User Agent Ioctl call. */

ua_ccb.uagt_ccb = (CCB_HEADER *)&ccb; /* where the CCB is */ [6] ua_ccb.uagt_ccblen = sizeof(CCB_SCSIIO); /* how many bytes to pull in */ [7] ua_ccb.uagt_buffer = ReadData; /* where the data goes */ [8] ua_ccb.uagt_buflen = LineBytes; /* how much data */ [9]

ua_ccb.uagt_snsbuf = &sense[0]; /* Autosense data area */ [10] ua_ccb.uagt_snslen = SENSE_LEN; /* Autosense data length */ ua_ccb.uagt_cdb = (CDB_UN *)NULL; /* CDB is in the CCB */ [11] ua_ccb.uagt_cdblen = 0; /* CDB is in the CCB */

n = TotalBytes + strlen(FileHead); printf("Total bytes in file = %12d.\n", n);

printf("\nRead data from scanner and write to file\n");

  1. Fills in some of the CCB_HEADER fields of the SCSI I/O CCB structure defined as ccb for processing by the XPT layer. The structure was declared in Section 2.5.1.2. [Return to example]

  2. Sets the cam_data_ptr to the address of the ReadData array defined in Section 2.5.1.2. [Return to example]

  3. Sets the data transfer length to the length of the SCAN_READ_CDB structure. [Return to example]

  4. Sets the read pointer, which is type SCAN_READ_CDB, to the address of the cam_cdb_len member of the CDB_UN union. This union is defined in /usr/sys/include/io/cam/cam.h as the cam_cdb_io member of the SCSI I/O CCB structure. [Return to example]

  5. Calls the clear_mem function to clear the local SCAN_READ_CDB structure, read, in preparation for storing the values needed for the READ operation. The SCAN_READ_CDB structure was defined in Section 2.5.2.3. The clear_mem function is defined in Section 2.5.2.14. [Return to example]

  6. Use the read pointer to access the bytes of the cam_cdb_bytes array as though they are in a SCAN_DEF_WIN_CDB structure. The SCAN_READ_CDB structure is defined in Section 2.5.2.3. [Return to example]

  7. Sets the length of the uagt_ccblen member to the length of the SCSI I/O CCB structure that will be used for this call. [Return to example]

  8. Sets the uagt_buffer member of the ua_ccb structure. [Return to example]

  9. Sets the size of the data buffer to the number of bytes contained in the buffer pointed to by the cam_data_ptr member of the ccb structure. [Return to example]

  10. Reflect that the autosense features are turned on in the CAM flags. [Return to example]

  11. Reflect that the CDB information is in the SCSI I/O CCB structure filled in Section 2.5.1.2. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


2.5.2.13    Read and Write Loop Section

This section describes the portion of the program where the data is read, reformatted, and placed in the output buffer.

/* ****************** Beginning of read/write loop ***************** */

for (i=0; i<WLength; i++) {

printf(" Read scanner line number %8d\r",i); fflush(stdout); [1]

/* Send the CCB to the CAM subsystem via the User Agent driver, and report any errors. */

if( ioctl(fd, UAGT_CAM_IO, (caddr_t)&ua_ccb) < 0 ) [2] { perror("\nError on CAM UAgt Ioctl to Read data line"); close(fd); /* close the CAM file */ exit(1); }

/* If the CCB completed successfully then print out the data read, if not report the error. */

if (ccb.cam_ch.cam_status != CAM_REQ_CMP) { printf("\n"); print_ccb_status("CAM UAgt Read data line Ioctl", &(ccb.cam_ch) ); /* report the error values */ printf(" cam_scsi_status = 0x%.2X\n", ccb.cam_scsi_status); close(fd); /* close the CAM file */ exit(1); } else { #ifdef CUT_FOR_NOW printf(" Data line read successfully\n"); #endif

/* Re-format the data from blocks of R, G and B data to tuples of (R,G,B) data for the data file. Set up pointers to the beginning of each of the blocks of the Red, the Green and the Blue data bytes and another pointer to the output buffer. Then loop, collecting one each of Red, Green and Blue, putting each into the output data buffer. */ [3]

RDRp = ReadData; /* Red bytes are first */ RDGp = RDRp + WWidth; /* Green bytes are next */ RDBp = RDGp + WWidth; /* Blue bytes are last */ WDp = WriteData;

for (n = 0 ; n < WWidth; n++) { *WDp++ = *RDRp++; *WDp++ = *RDGp++; *WDp++ = *RDBp++; }

/* Now write the re-formatted data to the output file. */

write(od,WriteData,LineBytes); /* write data to file */ } } /* ****************** End of read/write loop ***************** */ printf("\nSuccessful read and write to file\n"); close(fd); /* close the CAM file */ close(od); /* close the output file */ }

  1. Calls the standard C I/O function, fflush, to force the scan line number to the user's display. [Return to example]

  2. Attempts to read a scan line. This line uses the ioctl system call to pass the local UAGT_CAM_CCB structure, ua_ccb, to the User Agent driver. The arguments passed are the file descriptor returned by the open system call; the User Agent ioctl command, UAGT_CAM_IO, which is defined in the /usr/sys/include/io/cam/uagt.h file; and the contents of the ua_ccb structure. The User Agent driver copies in the SCSI I/O CCB and sends it to the XPT layer. When the I/O has completed, the User Agent returns to the application program, returning status within the ua_ccb structure. [Return to example]

  3. The scan line read in contains all the red bytes, then all the green bytes, then all the blue bytes, in sequence. This section of code reformats the bytes into pixels for the output file by placing a red byte, then a green byte, then a blue byte together on the output file scan line. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


2.5.2.14    Local Function Definition Section

This section describes the portion of the User Agent sample scanner program that defines functions used within the program.

/* Local routines and data structure to report in text and Hex form the
returned CAM status. */
struct cam_statustable { [1]
        u_char  cam_status;
        caddr_t status_msg;
} cam_statustable[] = {
    {   CAM_REQ_INPROG,         "CCB request is in progress" 		},
    {   CAM_REQ_CMP ,           "CCB request completed w/out error" },
    {   CAM_REQ_ABORTED,        "CCB request aborted by the host"   },
    {   CAM_UA_ABORT,           "Unable to Abort CCB request" 	     },
    {   CAM_REQ_CMP_ERR,        "CCB request completed with an err" },
    {   CAM_BUSY,               "CAM subsystem is busy"	     	 	},
    {   CAM_REQ_INVALID,        "CCB request is invalid"  		},
    {   CAM_PATH_INVALID,       "Bus ID supplied is invalid" 	  	},
    {   CAM_DEV_NOT_THERE,      "Device not installed/there" 		},
    {   CAM_UA_TERMIO,          "Unable to Terminate I/O CCB req"    },
    {   CAM_SEL_TIMEOUT,        "Target selection timeout" 		},
    {   CAM_CMD_TIMEOUT,        "Command timeout" 			},
    {   CAM_MSG_REJECT_REC,     "Reject received" 			},
    {   CAM_SCSI_BUS_RESET,     "Bus reset sent/received" 		},
    {   CAM_UNCOR_PARITY,       "Parity error occurred"			},
    {   CAM_AUTOSENSE_FAIL,     "Request sense cmd fail" 		},
    {   CAM_NO_HBA,             "No HBA detected Error"     		},
    {   CAM_DATA_RUN_ERR,       "Overrun/underrun error" 		},
    {   CAM_UNEXP_BUSFREE,      "BUS free" 			},
    {   CAM_SEQUENCE_FAIL,      "Bus phase sequence failure" 		},
    {   CAM_CCB_LEN_ERR,        "CCB length supplied is inadequate"  },
    {   CAM_PROVIDE_FAIL,       "To provide requ. capability" 	     },
    {   CAM_BDR_SENT,           "A SCSI BDR msg was sent to target"  },
    {   CAM_REQ_TERMIO,         "CCB request terminated by the host" },
    {   CAM_LUN_INVALID,        "LUN supplied is invalid"		},
    {   CAM_TID_INVALID,        "Target ID supplied is invalid"	},
    {   CAM_FUNC_NOTAVAIL,      "Requested function is not available" },
    {   CAM_NO_NEXUS,           "Nexus is not established"	 	},
    {   CAM_IID_INVALID,        "The initiator ID is invalid"  	 },
    {   CAM_CDB_RECVD,          "The SCSI CDB has been received"      },
    {   CAM_SCSI_BUSY,          "SCSI bus busy"				 }
};
int cam_statusentrys = sizeof(cam_statustable) /
sizeof(cam_statustable[0]);
char * camstatus( cam_status )
register u_char cam_status;
{
        register struct cam_statustable *cst = cam_statustable;
        register entrys;
        for( entrys = 0; entrys < cam_statusentrys; cst++ ) {
                if( cst->cam_status == cam_status ) {
                        return( cst->status_msg );
                }
        }
        return( "Unknown CAM Status" );
}

void print_ccb_status(id_string,cp) [2] char *id_string; CCB_HEADER *cp; { register i;

printf("Status from %s\n",id_string); printf(" cam_status = 0x%.2X (%s%s%s)\n", cp->cam_status, ((cp->cam_status & CAM_AUTOSNS_VALID) ? "AutoSns Valid-" : ), ((cp->cam_status & CAM_SIM_QFRZN) ? "SIM Q Frozen-" : ), camstatus( cp->cam_status & CAM_STATUS_MASK ));

if (cp->cam_status & CAM_AUTOSNS_VALID) { printf("AutoSense Data (in hex):\n "); for( i=0; i < SENSE_LEN; i++) printf("%.2X ", sense[i]); printf("\n" ); }

fflush(stdout); }

void clear_mem(bp,n) /* Clear n bytes of memory beginning at bp */ [3] u_char *bp; int n; { register i; register u_char *ptr; for(i=0, ptr=bp; i<n; i++, ptr++) *ptr = 0; }

void swap_short_store(bp,val) /* Store short into byte-reversed storage */ [4] u_char *bp; u_short val; { u_short temp; register u_char *ptr; ptr = bp; /* Copy pointer */ *(bp++) = (u_char)(val>>8); /* Store high byte first */ *bp = (u_char)val; /* Then store low byte */ }

void swap_long_store(bp,val) /* Store long into byte-reversed storage */ [5] u_char *bp; u_long val; { *(bp++) = (u_char)(val>>24); /* Store high byte first */ *(bp++) = (u_char)(val>>16); *(bp++) = (u_char)(val>>8); *bp = (u_char)val; /* Store low byte last */ }

  1. This function is described in Section 2.5.1.9. [Return to example]

  2. Prints out the CCB status. [Return to example]

  3. Clears out all the bits in an area of memory, such as a structure or an array, to be sure all are set to 0 (zero) and that there is no extraneous data before executing a SCSI/CAM command. [Return to example]

  4. Puts the bytes of a short (16-bit) integer value into big-endian storage to conform with SCSI byte ordering. [Return to example]

  5. Puts the bytes of a long (32-bit) integer value into byte-reversed storage to conform with SCSI byte ordering. [Return to example]