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.
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.
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:
This section describes the data structures that the User Agent uses.
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;
The uagt_ccb member contains a pointer to the user process's CCB that will be copied into kernel space.
The uagt_ccblen member contains the length of the user process's CCB.
The uagt_buffer member contains a pointer to the user process's data buffer. Only the User Agent uses this member.
The uagt_buflen member contains the length of the user process's data buffer. Only the User Agent uses this member.
The uagt_snsbuf member contains a pointer to the user process's autosense data buffer. Only the User Agent uses this member.
The uagt_snslen member contains the length of the user process's autosense data buffer. Only the User Agent uses this 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.
The uagt_cdblen contains the length of the CDB, if appropriate.
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.
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;
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.
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. |
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.
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.
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.
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.
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.
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]
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]);
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); }
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]
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 */
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 */
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] } }
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] }
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] }
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]
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]
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
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"
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;
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 */
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
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]
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 */
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]
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); }
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 -- */
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" );
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 */
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);
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");
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 */ }
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 */ }