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


C    Sample Generic CAM Peripheral Driver

This chapter contains a sample generic CAM peripheral driver. There are two sample files: the first contains the cam_generic.h header file; the second contains the driver source file cam_generic.c.

Example C-1: The cam_generic.h Header File

=================================================================

 
/**************************************************************** ================================================================= * * * Copyright (c) 1990 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used * * and copied only in accordance with the terms of such * * license and with the inclusion of the above copyright * * notice. This software or any other copies thereof may not * * otherwise made available to any other person. No title to * * and ownership of the software is hereby transferred. * * * * * * The information in this software is subject to change * * without notice and should not be construed as a commitment * * by Digital Equipment Corporation. * * * * Digital assumes no responsibility for the use or reliability* * of its software on equipment which is not supplied by * * Digital. * * * ***************************************************************/
 
/* ----------------------------------------------------------- */
 
/* This file contains examples of a CAM generic driver's defines.
 
Modification History
 
Version Date Who Reason
 
*/
 
/* ----------------------------------------------------------- */
 
/* Include Files */ /* None */
 

 
/* ----------------------------------------------------------- */
 
/* Defines */
 

 
/* The following flags are used in the CGEN_SPECIFIC structure in member gen_state_flags. The state flags are used to determine and indicate certain states of the driver and the SCSI unit. */
 
#define CGEN_NOT_READY_STATE 0x00000001 /* Indicates that the unit was opened with the FNDELAY * flag and the unit had a failure during the open, but * was seen */ #define CGEN_UNIT_ATTEN_STATE 0x00000002 /* Indicates that a check condition occurred and the * sense key was UNIT ATTENTION. This usually indicates * that a media change has occurred, but it could * indicate power up or reset. Either way, current * settings are lost. */ #define CGEN_RESET_STATE 0x00000004 /* Indicates notification of a reset set condition * on the device or bus. */ #define CGEN_RESET_PENDING_STATE 0x00000008 /* * A reset is pending will be notified shortly */ #define CGEN_OPENED_STATE 0x00000010 /* * The unit is opened */ #define CGEN_XXX_STATE 0x00000020 /* * Sample state used in generic driver. */
 
/* ----------------------------------------------------------- */
 
/* The following flags are used in the CGEN_SPECIFIC structure in member gen_flags. The flags are used to determine and indicate certain conditions of the SCSI unit. */
 
#define CGEN_EOM 0x00000001 /* At End of Tape */ #define CGEN_OFFLINE 0x00000002 /* Indicates the device is returning DEVICE NOT READY * in response to a command. */ #define CGEN_WRT_PROT 0x00000004 /* Hardware write protected or opened read only */ #define CGEN_SOFTERR 0x00000008 /* Indicates that a soft error has been reported by the * SCSI unit. */ #define CGEN_HARDERR 0x00000010 /* Indicates a hard error has occurred. This flag can be * reported to the user process either through an ioctl * orby the buf struct being marked as EIO. */ #define CGEN_XXX 0x00000020 /* Sample flag used in generic driver. */ #define CGEN_YYY 0x00000040 /* Sample flag used in generic driver. */
 

 
/* ----------------------------------------------------------- */
 
/* Generic Structure Declarations */
 
/* Generic-Specific Structure */
 
typedef struct generic_specific { u_long gen_flags; /* flags - EOM, write locked */ u_long gen_state_flags; /* STATE - UNIT_ATTEN, RESET etc. */ u_long gen_resid; /* Last operation residual count */ }CGEN_SPECIFIC;
 
/* * Generic Action Structure * * The generic_action struct is passed down to the action * routines to be filled in based on success or failure of the * command. */ typedef struct generic_action { CCB_SCSIIO *ccb; /* CCB that is returned to caller*/ long ret_error; /* Error code if any*/ u_long fatal; /* Is this considered fatal?*/ u_long ccb_status; /* The CCB status code*/ u_long scsi_status; /* The SCSI error code*/ u_long chkcond_error; /* The check condition error*/ }CGEN_ACTION;
 
/* * CGEN_ACTION defines * action.fatal flags; */ #define ACT_FAILED 0x00000001 /* This action has failed */ #define ACT_RESOURCE 0x00000002 /* Resource problem (memory)*/ #define ACT_PARAMETER 0x00000004 /* Invalid parameter */ #define ACT_RETRY_EXCEDED 0x00000008 /* The retry operation count * has been exceeded */
 
/* * CGEN_REL_MEM will examine a SCSI I/O CCB to see if the data * buffer pointer is non NULL. If so, the macro will call * ccmn_rel_dbuf with the size of the buffer, to release the * memory back to the pools. */ #define CGEN_REL_MEM(ccb); { \ if(((CCB_SCSIIO *)(ccb))->cam_data_ptr != (u_char *)NULL ) { \ ccmn_rel_dbuf(((CCB_SCSIIO *)(ccb))->cam_data_ptr, \ ((CCB_SCSIIO *)(ccb))->cam_dxfer_len ); \ ((CCB_SCSIIO *)(ccb))->cam_data_ptr = (u_char *)NULL; \ ((CCB_SCSIIO *)(ccb))->cam_dxfer_len = (u_long)NULL; \ } \ }
 

 
/* * Maximum I/O size. */ #define CGEN_MAXPHYS (16 * (1024 * 1024)) /* 16 meg */
 
/* * Default time-out value for NON read/write operations * (rewind,space) */ #define CGEN_DEF_TIMEO 600
 
/* * 5-second time */ #define CGEN_TIME_5 5
 
/* * Whether to sleep in the work routines */ #define CGEN_SLEEP 0x00000000 #define CGEN_NOSLEEP 0x00000001
 
/* * Success and failure defines */ #define CGEN_SUCCESS 00 #define CGEN_FAIL -1
 
/* * Defines for return values from CGEN_ccb_chkcond */ #define CHK_SENSE_NOT_VALID 0x0001 /* Valid bit is not set in sense */ #define CHK_EOM 0x0002 /* End of media */ #define CHK_FILEMARK 0x0003 /* File mark detected */ #define CHK_ILI 0x0004 /* Incorrect length */ #define CHK_NOSENSE_BITS 0x0005 /* NOSENSE key and no bits */ #define CHK_SOFTERR 0x0006 /* Soft error reported */ #define CHK_NOT_READY 0x0007 /* Device is not ready */ #define CHK_HARDERR 0x0008 /* Device reported */ /* hard error */ #define CHK_UNIT_ATTEN 0x0009 /* Unit attention (ready?) */ #define CHK_DATA_PROT 0x000a /* Write protected */ #define CHK_CMD_ABORTED 0x000b /* Command has been aborted */ #define CHK_UNSUPPORTED 0x000c /* We don't handle them */ #define CHK_UNKNOWN_KEY 0x000d /* Bogus sense key */ #define CHK_CHK_NOSENSE 0x000e /* Sense Auto sense */ /* valid bit 0 */ #define CHK_INFORMATIONAL 0x000f /* Informational message.. */
 
/* * Clear the fields in the CCB which will be filled in on a retry * of the CCB. */ #define CGEN_CLEAR_CCB(ccb) { \ (ccb)->cam_ch.cam_status = 0; \ (ccb)->cam_scsi_status = 0; \ (ccb)->cam_resid = 0; \ }
 
#define CGEN_BTOL(ptr, long_val) { \ char *p = (char *)(ptr); \ union { \ unsigned char c[4]; \ unsigned long l; \ }tmp; \ tmp.c[3] = *p++; \ tmp.c[2] = *p++; \ tmp.c[1] = *p++; \ tmp.c[0] = *p++; \ (long_val) = tmp.l; \ }
 
#define CGEN_LOCK_OR_STATE(pd, ts, flags) { \ int ipl; \ PDRV_IPLSMP_LOCK( (pd), LK_RETRY, ipl ); \ (ts)->gen_state_flags |= (flags); \ PDRV_IPLSMP_UNLOCK( (pd), ipl ); \ }
 
#define CGEN_LOCK_OR_FLAGS(pd, ts, flags) { \ int ipl; \ PDRV_IPLSMP_LOCK( (pd), LK_RETRY, ipl ); \ (ts)->gen_flags |= (flags); \ PDRV_IPLSMP_UNLOCK( (pd), ipl ); \ }
 
#define CGEN_BERROR(buf , count, error ) { \ (buf)->b_resid = (count); \ (buf)->b_error = (error); \ (buf)->b_flags |= B_ERROR; \ }
 
#define CGEN_NULLCCB_ERR( act_ptr, pd, mod ) { \ int ipl; \ PDRV_IPLSMP_LOCK( (pd), LK_RETRY, ipl ); \ CAM_ERROR((mod), "NULL CCB returned", CAM_SOFTWARE, \ (CCB_HEADER *)NULL, (pd)->pd_dev, \ (u_char *)NULL); \ PDRV_IPLSMP_UNLOCK((pd), ipl); \ (act_ptr)->fatal |= (ACT_RESOURCE | ACT_FAILED); \ (act_ptr)->ret_error = ENOMEM; \ }
 
/* You should inplement your own error logging. */ #define LOG_ERR printf
 
=================================================================

The following file contains source code for a generic peripheral driver.

Example C-2: The cam_generic.c Source File

=================================================================

 
/**************************************************************** * * * Copyright (c) 1990 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * * * This software is furnished under a license and may be used * * and copied only in accordance with the terms of such * * license and with the inclusion of the above copyright * * notice. This software or any other copies thereof may not * * be provided or otherwise made available to any other person.* * No title to and ownership of the software is hereby * * transferred. * * * * The information in this software is subject to change * * without notice and should not be construed as a commitment * * by Digital Equipment Corporation. * * * * Digital assumes no responsibility for the use or * * reliability of its software on equipment which is not * * supplied by Digital. * * * ****************************************************************/
 
/* ----------------------------------------------------------- */
 
/* cam_generic.c Version 1.00 Aug. 05, 1991
 
This module is the upper layer (class) for a generic SCSI device driver. The module is an example of a device driver for the CAM interface only.
 
Modification History
 
Version Date Who Reason
 

 
*/
 
/* ----------------------------------------------------------- */
 
/* Include files. */
 
#include <sys/types.h> #include <sys/file.h> #include <sys/param.h> #include <sys/uio.h> #include <sys/time.h> #include <sys/buf.h> #include <sys/ioctl.h> #include <sys/mtio.h> #include <sys/errno.h> #include <io/common/devio.h> #include <io/common/devdriver.h> #include <io/common/iotypes.h> #include <io/cam/cam_debug.h> #include <io/cam/cam.h> #include <io/cam/dec_cam.h> #include <io/cam/scsi_status.h> #include <io/cam/scsi_all.h> #include <io/cam/pdrv.h> #include <io/cam/scsi_sequential.h> #include "cam_generic.h"
 

 

 
/* ----------------------------------------------------------- */
 
/* Local defines. */
 
void cgen_done(); void cgen_async(); void cgen_iodone(); void ccmn_minphys(); void cgen_strategy(); void cgen_ready(); void cgen_open_sel(); void cgen_mode_sns(); void cgen_minphys(); u_long cgen_ccb_chkcond();
 

 
/* ----------------------------------------------------------- */
 
/* External declarations. */ extern int lbolt; extern int nCAMBUS; extern void ccmn_init(); extern long ccmn_open_unit(); extern void ccmn_close_unit(); extern u_long ccmn_send_ccb(); extern void ccmn_rem_ccb(); extern void ccmn_abort_que(); extern void ccmn_term_que(); extern CCB_HEADER *ccmn_getccb(); extern void ccmn_rel_ccb(); extern CCB_SCSIIO *ccmn_io_ccb_bld(); extern CCB_GETDEV *ccmn_gdev_ccb_bld(); extern CCB_SETDEV *ccmn_sdev_ccb_bld(); extern CCB_SETASYNC *ccmn_sasy_ccb_bld(); extern CCB_RELSIM *ccmn_rsq_ccb_bld(); extern CCB_PATHINQ *ccmn_pinq_ccb_bld(); extern CCB_ABORT *ccmn_abort_ccb_bld(); extern CCB_TERMIO *ccmn_term_ccb_bld(); extern CCB_RESETDEV *ccmn_bdr_ccb_bld(); extern CCB_RESETBUS *ccmn_br_ccb_bld(); extern CCB_SCSIIO *ccmn_tur(); extern CCB_SCSIIO *ccmn_mode_select(); extern u_long ccmn_ccb_status(); extern struct buf *ccmn_get_bp(); extern void ccmn_rel_bp(); extern u_char *ccmn_get_dbuf(); extern void ccmn_rel_dbuf();
 
extern struct device *camdinfo[]; extern struct controller *camminfo[];
 
extern PDRV_UNIT_ELEM pdrv_unit_table[];
 

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

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine name cgen_slave * * This routine is called at boot. The main purposes of the * routine are to initialize the lower levels, to check the unit * number to make sure it falls within range, and to set the * device-configured bit for this device type at this * bus/target/lun. * * Call syntax * cgen_slave(attach) * struct device *attach Pointer to the device struct * caddr_t reg Virtual address of controller * DO NOT USE * * Implicit inputs * NONE * * Implicit outputs * NONE * * Return values * PROBE_FAILURE * PROBE_SUCCESS * */
 
int cgen_slave(attach, reg) struct device *attach; /* Pointer to device struct */ caddr_t reg; /* Virtual register address - unused */
 
{
 
/* * Local variables */ u_long unit; /* Unit number */
 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ dev_t dev; /* For the PRINTD statements */ static u_char module[] = "cgen_slave"; /* Module name */
 
/* * The UBA_UNIT_TO_DEV_UNIT macro assumes unit has bits * 0-2 = lun, bits 3-5 = target id, and 6-7 = bus num. */ dev = makedev(0, MAKEMINOR(UBA_UNIT_TO_DEV_UNIT(attach), 0)); unit = DEV_UNIT(dev);
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
/* * Call common initialization routine because we do not know * if the subsystem has been initialized */ ccmn_init();
 
if( unit > MAX_UNITS){ /* * Unit number is greater than maximum allowed. */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: Unit number too large %d\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, unit)); return(PROBE_FAILURE); } /* * Set the configured bit in the unit table with the device * type of your device - we will use sequential access * devices. */ pdrv_unit_table[unit].pu_config |= ( 1 << ALL_DTYPE_SEQUENTIAL);
 

 
/* * Call the common open unit routine to see if a device is * there. Shift the unit number left by 4 to move over the * device-specific bits such as density, no rewind, disk's * partition number, etc. */ unit = (unit << 4);
 
/* * ccmn_open_unit args = device number (major/minor pair); * SCSI device type; exclusive use flag if exclusive access * is desired; and the size of the device-specific structure. */ if( ccmn_open_unit( (dev_t)unit, ALL_DTYPE_SEQUENTIAL, CCMN_EXCLUSIVE, sizeof(CGEN_SPECIFIC)) != (long)NULL){ /* * Could not open unit. */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: ccmn_open_unit failed\n", ================================================================= DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module)); return(PROBE_FAILURE); }
 
/* * Close the unit. */ ccmn_close_unit((dev_t)unit);
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 

 
return(PROBE_SUCCESS); }
 

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine name cgen_attach * * This routine is called at boot to find out if there are any * devices at this BUS/TARGET/LUN. If a device is found for * our device type print out unit identification * * Call syntax * cgen_attach(attach) * struct device *attach Pointer to the uba struct * * Implicit inputs * NONE * * Implicit outputs * NONE * * Return values * PROBE_FAILURE * PROBE_SUCCESS * */
 
int cgen_attach(attach) struct device *attach; /* Pointer to device struct */ {
 
/* Local Varibles */
 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ dev_t dev; /* For the PRINTD statements */ u_long unit; static u_char module[] = "cgen_attach"; /* Module name */
 
/* * The UBA_UNIT_TO_DEV_UNIT macro assumes unit * has bits 0-2 = lun, bits 3-5 = target id, * and 6-7 = bus num. */ dev = makedev(0, MAKEMINOR(UBA_UNIT_TO_DEV_UNIT(attach), 0)); unit = DEV_UNIT(dev);
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 

 
/* * Determine whether a device exists at this address by * calling ccmn_open_unit which checks the Equipment Device * Table. */ if( ccmn_open_unit(dev, (u_long)ALL_DTYPE_SEQUENTIAL, CCMN_EXCLUSIVE, (u_long)sizeof(CGEN_SPECIFIC)) != 0L) { PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: ccmn_open_unit failed\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); return(PROBE_FAILURE); }
 
/* * Get the pointer to the PDRV_DEVICE structure */
 
if( (pdrv_dev = GET_PDRV_PTR(dev)) == (PDRV_DEVICE *)NULL) { ccmn_close_unit(dev); PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: No peripheral device structure allocated\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); return(PROBE_FAILURE); }
 
/* Output the identification string */ printf(" (%s %s)", pdrv_dev->pd_dev_desc->dd_dev_name, pdrv_dev->pd_dev_desc->dd_pv_name);
 
/* * Close the unit. */ ccmn_close_unit(dev);
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 
return(PROBE_SUCCESS); }
 

 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine name cgen_open * * This routine opens the unit. * For a flag of FNDELAY and all errors other than * reservation conflicts and memory resource conflicts, always * return success. This is based on the POSIX standard. * * First do a TEST UNIT READY command to see if the device is * ready to use. * * * * Call syntax * cgen_open( dev, flags ) * * Implicit inputs * Flags of CGEN_UNIT_ATTEN_STATE, CGEN_RESET_STATE from * last open. * * Implicit outputs * Flags of CGEN_NOT_READY_STATE, if the FNDELAY flag was * passed in this routine and the device had an error. * * Return values * CGEN_SUCCESS * EBUSY Device reserved by another initiator * ENOMEM Resource problem * EINVAL CCB problems * ENXIO Device path problems. * EIO Device check conditions * * TO DO: */
 

 
int cgen_open(dev, flags)
 

 
dev_t dev; /* Major/minor number pair */ int flags; /* Flags RDONLY, READ, WRITE, FNDELAY, etc. */
 
{
 
/* * LOCAL VARIABLES */
 

 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */
 
DEV_DESC *dev_desc; /* Device Descriptor Structure pointer */
 
MODESEL_TBL *modsel_tab; /* Pointer to Mode Select Table * structure to read for the open. */
 
CGEN_SPECIFIC *gen_spec; /* Generic-Specific Structure pointer */
 
CCB_SETASYNC *ccb_async; /* CAM SET ASYNCHRONOUS CALLBACK CCB */ CGEN_ACTION action; /* Generic Action Structure */
 

 
long ret_val; /* Return value from sub-routines */
 

 
u_long ready_time; /* Time it takes for this type * unit to become ready (seconds) */
 
u_long state_flags; /* Saved state */ long success; /* Test unit ready loop indicator */ long fndelay; /* Test unit ready loop indicator */ long fatal; /* Test unit ready loop indicator */
 
int i; /* For loop counter */ int s; /* For our saved IPL */ int s1; /* Throwaway IPL */ static u_char module[] = "cgen_open"; /* Module name */
 

 
/* *END OF LOCAL VARIABLES */
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
/* * Call peripherial driver common routine to * open the device. This routine locks the Peripheral * Device Unit Table and makes sure everything matches. * Arguments are dev; device_type (tape ,disk,scanner); * whether exclusive use or not; size of device-specific * struct (CGEN_SPECIFIC). * * Refer to ccmn_open_unit() for a full description. */
 
ret_val = ccmn_open_unit( dev, ALL_DTYPE_SEQUENTIAL, CCMN_EXCLUSIVE, sizeof(CGEN_SPECIFIC) );
 
if ( ret_val != NULL ){ /* * Return ERRNO based on return value: * EBUSY - Device is already opened exclusive use * EINVAL- Device types do not match * ENXIO - Device does not exist even after rescan. */
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: Dev failed ccmn_open_unit dev = %d\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, dev));
 
return( ret_val );
 
}
 
/* * Can now set up structure pointers */
 
/* * Get Peripherial Device Structure pointer */ if( (pdrv_dev = GET_PDRV_PTR(dev)) == (PDRV_DEVICE *)NULL){ /* * This should not happen - no PDRV_DEVICE struct. */ LOG_ERROR("Implement your error logging");
 
return(ENOMEM); }
 

 
/* * Get pointer to Device Descriptor Structure */ dev_desc = pdrv_dev->pd_dev_desc;
 
/* * Get pointer to Mode Select Table Structure */ modsel_tab = dev_desc->dd_modesel_tbl;
 

 
/* * Get pointer to Generic-Specific Structure */
 
if( (gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific) == (CGEN_SPECIFIC *)NULL){ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK( pdrv_dev, s ); return(ENOMEM); }
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: state flags = %X\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, gen_spec->gen_state_flags));
 

 
/* * Initialize state flags and regular flags * The flags of CGEN_UNIT_ATTEN_STATE and CGEN_RESET_STATE, * will be preserved across opens if the open has failed due * to device problems. */
 
PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); state_flags = gen_spec->gen_state_flags;
 
if((state_flags & (CGEN_UNIT_ATTEN_STATE | CGEN_RESET_STATE )) != NULL) {
 
/* * Flags for known state. */ gen_spec->gen_flags = CGEN_XXX; } else { /* * Do you want to save any flags set from * the last unit open. CGEN_XXX and CGEN_YYY are * example flags. */ gen_spec->gen_flags &= (CGEN_XXX | CGEN_YYY);
 
}
 
PDRV_IPLSMP_UNLOCK( pdrv_dev, s);
 

 
/* * Register for a SET ASYNCHRONOUS CALLBACK CCB * The events to notice are: * * Bus Device resets, SCSI_Attens, Asynchronous Event * Notifications (AEN), Bus Resets */ ccb_async = ccmn_sasy_ccb_bld( dev, (u_long)CAM_DIR_NONE, (AC_SENT_BDR | AC_SCSI_AEN | AC_BUS_RESET), cgen_async, (u_char *)NULL, NULL);
 
/* * This command is carried out immediately, so status should * be valid */ if( CAM_STATUS(ccb_async) != CAM_REQ_CMP ){ /* * The SET ASYNCHRONOUS CALLBACK CCB can not be * registered. If FNDELAY is set, continue. */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: Can't set async ccb status = %x\n", Cannot SET ASYNCHRONOUS CALLBACK CCB; status = DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, ccb_async->cam_ch.cam_status));
 
LOG_ERROR("Implement your error logging");
 

 
/* * Release the CCB */ ccmn_rel_ccb((CCB_HEADER *)ccb_async );
 
if((flags & FNDELAY) == NULL){ CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, state_flags); ccmn_close_unit(dev); return(EIO); } } /* end of if status != CAM_REQ_CMP for SET */ /* ASYNCHRONOUS CALLBACK CCB */
 
else {
 
ccmn_rel_ccb((CCB_HEADER *)ccb_async );
 
} /* end of else (SET ASYNCHRONOUS CALLBACK */ /* CCB status == CAM_REQ_CMP) */
 

 

 
/* * Everything is in place to start operations * Check the dev descriptor to get the device ready * time in seconds. If null, take the default of 45 seconds. */ ready_time = dev_desc->dd_ready_time;
 
if( ready_time == NULL){ ready_time = 45; }
 
/* * The following 3 variables are VERY important. They direct * actions at the bottom of the for loop that issues the TEST * UNIT READY command. If success is nonzero, then the TUR * succeeded with no errors. If fndelay is non zero the TUR * failed but the FNDELAY flag was set. Either way, get out * of for loop. If fatal is ever positive, then either the * unit is reserved to another initiator or there is a driver * problem. */
 
success = 0; fndelay = 0; fatal = 0;
 

 
/* * Start of the for loop that looks for the device to become * ready. Take into account the FNDELAY flag and SCSI BUSY * status. POSIX definition of the FNDELAY flags say don't * wait for the unit to become ready. If the unit is not * there or is reserved by another initiator, return failure; * else return success. SCSI BUSY status indicates that the * device is unable to accept the command at this time. */ for ( i = 0; i < ready_time; i++) { /* * Zero out action structure */ bzero( &action, sizeof(CGEN_ACTION));
 
/* * Issue a TEST UNIT READY command. The autosense feature * performs the REQUEST SENSE operation, if there is a * SCSI status of check condition. */
 
cgen_ready( pdrv_dev, &action, cgen_done, CGEN_SLEEP);
 
if( action.ccb == NULL ) { PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: TUR, CCB_IO = NULL\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); /* * Resource problem? If so, get out. */ if(( action.fatal & ACT_RESOURCE ) != NULL){ fatal++; break; } /* * Some other gross error */ else if(( flags & FNDELAY ) != NULL){ fndelay++; break;
 
} else { fatal++; break; }
 
}
 
/* * Check to see if this is a successfully completed CCB */ if(action.ccb_status == CAT_CMP) {
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: TUR, SUCCESS\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
success++;
 
} /* end if status == CAT_CMP */
 
else { /* * If the CCB status does not equal * CAT_CMP_ERR, then this open failed. */ if( action.ccb_status != CAT_CMP_ERR ){ fatal++; }
 
/* * The only error that will cause an open to fail with * the FNDELAY flag set is EBUSY (reservation * conflict) to return. * Check for Reservation conflict */ else if( action.scsi_status == SCSI_STAT_RESERVATION_CONFLICT ){ fatal++; }
 

 
/* * Check the device state for SCSI_STAT_BUSY. If the status * is BUSY, the device could be powering up or rewinding. * If the status is BUSY, then retry the TUR operation again. * If the status is not BUSY, a UNIT ATTENTION status may have * been seen. * The (( flags & FNDELAY ) != NULL) && ( i > 1 )) statement * covers this possibility. * The UNIT ATTENTION status is also a common condition * with power up, resets and cartridge changes. Just retry * the TUR operation again. *
 
*/
 
if( (action.scsi_status != SCSI_STAT_BUSY ) && (( flags & FNDELAY ) != NULL) && ( i > 1 )) { fndelay++; } }
 
/* * Check the release queue prior to releasing the CCB */ CHK_RELEASE_QUEUE(pdrv_dev, action.ccb);
 
/* * Release the CCB */ ccmn_rel_ccb((CCB_HEADER *)action.ccb );
 
/* * Check for POSIX nodelay or sucessful open or fatal error */ if((fndelay != NULL) || (success != NULL) ||(fatal != NULL)){ /* * Break out of for loop */ break; }
 

 
/* * No success, so sleep */
 
if( mpsleep( &lbolt, (PCATCH | (PZERO+1)), \ "Zzzzzz",0,(void *)0,0) ) { /* * Set interruptable sleeps. If * non zero comes back, a signal was delivered. * Restore the flags as they were at the start * of the for loop. */ CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, state_flags); ccmn_close_unit(dev); return( EINTR ); }
 

 
} /* end of TUR for loop */
 
/* * Check to see if fatal is set (reservation conflict) */ if ( fatal != NULL ){ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: TUR, FATAL\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, state_flags); ccmn_close_unit(dev); return(action.ret_error); } else if( fndelay != NULL ){ /* * Broke out of loop because of some failure * and the FNDELAY flag is set. * Set the flag indicating device has not gone through * the full online sequence. */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: TUR, FNDELAY\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, (state_flags | CGEN_NOT_READY_STATE));
 
return(CGEN_SUCCESS); } else if ( success == NULL){ /* * The TUR command never completed successfully and * FNDELAY flag WAS NOT set, so return the last error * value */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: TUR, NO_SUCCESS\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, state_flags ); ccmn_close_unit(dev); return(action.ret_error); }
 

 

 
/* * If there has been a reset or unit attention, do * as directed in the Mode Select Table. */
 
PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s );
 
/* * The if statement checks to see if there was a reset when * the unit was closed and if one has occurred while it was * opening. */ if(((state_flags & (CGEN_UNIT_ATTEN_STATE | CGEN_RESET_STATE)) != NULL ) || ((gen_spec->gen_state_flags & (CGEN_RESET_STATE | CGEN_UNIT_ATTEN_STATE)) != NULL)) {
 
/* * There was a unit attention or reset, so the * Mode Select Table page must be sent to the * device. */ PDRV_IPLSMP_UNLOCK( pdrv_dev, s );
 
/* * Read the Mode Select Table for this device, * passing the index into the Mode Select Table to send * to the device. */
 

 
if(modsel_tab != NULL) { /* * The Mode Select Table contains a pointer to the * page definition for this device. */ for( i = 0; (modsel_tab->ms_entry[i].ms_data != NULL) && ( i < MAX_OPEN_SELS); i++) { /* * Zero out the action structure */ bzero( &action, sizeof(CGEN_ACTION)); cgen_open_sel( pdrv_dev, &action, i, cgen_done, CGEN_SLEEP);
 
if(action.ccb == (CCB_SCSIIO *)NULL) { PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: MODSEL, CCB = NULL\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
if(( action.fatal & ACT_RESOURCE ) != NULL ){ /* * Could not get resources (ccb's) needed; */
 
CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, state_flags); ccmn_close_unit(dev); return(action.ret_error); /* driver/resource problem */ }
 
if( (flags & FNDELAY) == NULL ) { /* * Close the unit and return errno */ CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, state_flags); ccmn_close_unit(dev); return( action.ret_error ); }
 
/* * The FNDELAY flag is set; must return success */ else { CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, ( state_flags | CGEN_NOT_READY_STATE));
 
return(CGEN_SUCCESS);
 
}
 
}
 
/* * Check to see if the CCB completed successfully */ if(action.ccb_status == CAT_CMP){
 
/* * Release the CCB back to the pool */
 
ccmn_rel_ccb((CCB_HEADER *)action.ccb );
 
/* do next page if any */ continue; }
 
/* * The Mode Select for this page failed. * The only error that causes an open to fail with * the FNDELAY flag set is a SCSI statua of * RESERVATION CONFLICT. */
 
else { PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: MODSEL FAILED index = 0x%x\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, i));
 
/* * Check the release queue prior to releasing the CCB */ CHK_RELEASE_QUEUE(pdrv_dev, action.ccb);
 
/* * Release the ccb */ ccmn_rel_ccb((CCB_HEADER *)action.ccb );
 

 
/* * If the returned SCSI status is RESERVATION CONFLICT * or FNDELAY == NULL, then fail this open. */ if((action.scsi_status == SCSI_STAT_RESERVATION_CONFLICT) || ((flags & FNDELAY) == NULL) ){ /* * Close the unit and return EBUSY */ CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, state_flags); ccmn_close_unit(dev); return( action.ret_error ); }
 
/* * The FNDELAY flag is set; must return success */ else { CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, (state_flags | CGEN_NOT_READY_STATE));
 
return(CGEN_SUCCESS);
 
} } } /* end of for loop */ } /* End of if modsel != NULL */ } /* End of reset or unit attention */
 
/* * Unlock the struct */ else { PDRV_IPLSMP_UNLOCK( pdrv_dev, s ); }
 
/* * At this point, you can set up any other device * features you need. */
 
/* * Add your device-specific code here. */
 

 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 
return(CGEN_SUCCESS); } /* End of cgen_open() */
 

 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine name cgen_close * * This routine closes the unit. * * * Call syntax * cgen_close( dev, flags ) * * Implicit inputs * Flags of XXXX * * Implicit outputs * NONE * * Return values * CGEN_SUCCESS * ENOMEM Resource problem * * TO DO: */
 

 

 
int cgen_close(dev, flags)
 

 
dev_t dev; /* Major/minor number pair */ int flags; /* Flags RDONLY READ WRITE FNDELAY etc. */
 
{
 
/* * LOCAL VARIABLES */
 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */
 

 
CGEN_SPECIFIC *gen_spec; /* Generic-Specific Structure pointer */ CGEN_ACTION action; /* Generic Action Structure */
 

 
u_long s; /* For saved IPL */
 
static u_char module[] = "cgen_close"; /* Module name */
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
/* * Get Peripheral Device Structure pointer */ if( (pdrv_dev = GET_PDRV_PTR(dev)) == (PDRV_DEVICE *)NULL){ /* * This should not happen--no Peripheral Device Structure. */ LOG_ERROR("Implement your error logging"); return(ENOMEM); }
 

 

 
/* * Get device-specific structure pointer */
 
gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific; if( (gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific) == (CGEN_SPECIFIC *)NULL){ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK( pdrv_dev, s ); return(ENOMEM); }
 

 

 
/* * Check to see if a unit attention has been seen; if so, * close the unit. Since we can not determine what type * of device the driver is being written for, this is * only an example of UNIT ATTENTIONS and RESETS being * detected at the close of the device. */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s );
 
if((gen_spec->gen_state_flags & (CGEN_UNIT_ATTEN_STATE | CGEN_RESET_STATE)) != NULL ){
 
/* * Close unit */ PDRV_IPLSMP_UNLOCK( pdrv_dev, s ); ccmn_close_unit(dev); return(CGEN_SUCCESS);
 
}
 

 
/* * Do device-specific close steps. */
 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 
return( CGEN_SUCCESS );
 
} /* End of cgen_close() */
 

 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine cgen_read * * Functional Description: * This routine handles user processes' synchronous read * requests. This is a pass through function that gets a buf * struct allocated and then passes the work to cgen_strategy. * * Call syntax * cgen_read( dev, uio) * dev_t dev; Major/minor number pair * struct *uio Pointer to the uio struct * * Implicit inputs * NONE * * Implicit outputs * NONE * * Return values * Passes return from physio() * * TO DO: */
 

 

 

 
int cgen_read( dev, uio) dev_t dev; /* Major/minor number pair */ struct uio *uio; /* Pointer to the uio struct */
 
{ /* * Local variables */ int ret_val; /* Value to be returned */ struct buf *bp; /* Allocated buf struct */ static u_char module[] = "cgen_read"; /* Module name */
 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); /* * Allocate buf struct */ bp = ccmn_get_bp();
 
if( bp == NULL ){ LOG_ERROR("Implement your error logging"); return (ENOMEM); }
 

 
ret_val = physio(cgen_strategy, bp, dev, B_READ, cgen_minphys, uio);
 
/* * Release the buf struct */ ccmn_rel_bp( bp );
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 
return( ret_val );
 
} /* end of cgen_read */
 

 

 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine cgen_write * * Functional Description: * This routine handles synchronous write requests for user * processes. This is a pass through function, that gets a * buf struct and then passes the work to cgen_strategy * * Call syntax * cgen_write( dev, uio) * dev_t dev; Major/minor number pair * struct *uio; Pointer to the uio struct * * Implicit inputs * NONE * * Implicit outputs * NONE * * Return values * Passes return from physio() * * TO DO: */
 

 

 

 
int cgen_write( dev, uio) dev_t dev; /* Major/minor number pair */ struct uio *uio; /* Pointer to the uio struct */
 
{ /* * Local variables */ int ret_val; /* Value to be returned */ struct buf *bp; /* Allocated buf struct */ static u_char module[] = "cgen_write"; /* Module name */
 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
/* * Allocate buf struct */ bp = ccmn_get_bp();
 
if( bp == NULL ){ LOG_ERROR("Implement your error logging"); return (ENOMEM); }
 
ret_val = physio(cgen_strategy, bp, dev, B_WRITE, cgen_minphys, uio);
 
/* * Release the bp */ ccmn_rel_bp( bp );
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 
return( ret_val );
 
} /* end of cgen_write */
 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * This routine handles all I/O requests for user processes. * A number of checks based on whether the request * is synchronous or asynchronous are made. * * Call syntax * cgen_strategy( bp ) * struct buf *bp Pointer to the struct buf * * * Implicit inputs * In the bp, whether the request is a read or a write, * synchronous or asynchronous. * In the CGEN_SPECIFIC structure, various state conditions. * * Implicit outputs * None. * * Return values * * TO DO: * */
 

 
void cgen_strategy( bp )
 
struct buf *bp; /* Pointer to the buf struct */
 
{
 
/* * Local variables. */ PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */
 
DEV_DESC *dev_desc; /* Device Descriptor Structure pointer */
 
CGEN_SPECIFIC *gen_spec; /* Generic-Specific Structure pointer */
 
CCB_SCSIIO *ccb_io; /* SCSI I/O CCB pointer */
 
u_long ccb_flags; /* The flags to be set in the ccb */ SEQ_READ_CDB6 *rd_cdb; /* Pointer to CDB within the CCB for a read command. */ SEQ_WRITE_CDB6 *wt_cdb; /* Pointer to CDB within the CCB for a write command. */ u_long send_stat; /* Value send CCB routine returns */ static u_char module[] = "cgen_strategy"; /* Module name */
 
int s; /* Saved IPL */ u_char sense_size; /* The request sense size */ SEQ_WRITE_CDB6 *wt_cdb; /* Pointer to write CDB */
 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
/* * Get Peripheral Device Structure pointer */ pdrv_dev = GET_PDRV_PTR(bp->b_dev);
 
/* * Get Device Descriptor Structure pointer */ dev_desc = pdrv_dev->pd_dev_desc; /* * Get Generic-Specific Structure pointer */ gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific;
 
/* * Lock the structure now because throughout the routine we * examine flags within the CGEN_SPECIFIC structure and do * not want any other routine to be clearing or setting flags * while decisons on the flags are being made. */ PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s);
 

 
/* * Check to see if the device was opened with the FNDELAY * flag set and it was not ready. */ if(( gen_spec->gen_state_flags & CGEN_NOT_READY_STATE ) != NULL){ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: NOT ready state flags = %0xX\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, gen_spec->gen_state_flags));
 
/* * Do not allow I/O operations to the unit */ PDRV_IPLSMP_UNLOCK( pdrv_dev, s ); CGEN_BERROR(bp, bp->b_bcount, EINVAL); biodone( bp ); return; } /* * This section of code notices various state conditions and * handles according to device. */
 
if(( gen_spec->gen_state_flags & CGEN_XXX_STATE ) != NULL ){ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: CGEN_XXX_STATE: stateflags = 0x%X\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, gen_spec->gen_state_flags));
 
/* * Do not allow I/O operations to the unit */ PDRV_IPLSMP_UNLOCK( pdrv_dev, s ); CGEN_BERROR(bp, bp->b_bcount, EIO); biodone( bp ); return; }
 
/* * Build the CDB (SCSI Command ) * EXAMPLE of a sequential access device. */ /* * Check the buf structure flags to determine if the user * has requested a read or write operation. */ if(( bp->b_flags & B_READ ) != NULL){ rd_cdb = (SEQ_READ_CDB6 *)ccb_io->cam_cdb_io.cam_cdb_bytes;
 
rd_cdb->opcode = SEQ_READ_OP; rd_cdb->lun = 0; SEQTRANS_TO_READ6( bp->b_bcount, rd_cdb );
 
/* * Set the length of the CDB */ ccb_io->cam_cdb_len = sizeof(SEQ_READ_CDB6); } /* * Must be user write command. */ else { wt_cdb = (SEQ_WRITE_CDB6 *)ccb_io->cam_cdb_io.cam_cdb_bytes;
 
wt_cdb->opcode = SEQ_WRITE_OP;
 
wt_cdb->lun = 0;
 
SEQTRANS_TO_WRITE6( bp->b_bcount, wt_cdb );
 
/* * Set the length of the CDB */ ccb_io->cam_cdb_len = sizeof(SEQ_WRITE_CDB6); }
 

 
/* * Send it down to the XPT layer */
 
send_stat = ccmn_send_ccb( pdrv_dev, (CCB_HEADER *)ccb_io, NOT_RETRY);
 
/* * If the CCB is not in progress... */ if((send_stat & CAM_STATUS_MASK) != CAM_REQ_INPROG){ /* * The CCB has been returned and has not gone through * cgen_iodone. Call the CCB and return. */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: send status NOT inprog\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
PDRV_IPLSMP_UNLOCK(pdrv_dev, s); cgen_iodone(ccb_io); return; }
 
PDRV_IPLSMP_UNLOCK(pdrv_dev, s);
 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 
return;
 
} /* end of cgen_strategy */
 

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine name cgen_ioctl * * This routine handles specific requests for actions other * than read and write. * * Call syntax * cgen_ioctl( dev, cmd, data, flags ) * * Implicit inputs * flags of CGEN_XXX * * Implicit outputs * * * Return values * * TO DO: */
 
int cgen_ioctl( dev, cmd, data, flag ) dev_t dev; /* Major/minor number pair */ int cmd; /* The command we are doing */ caddr_t data; /* * Pointer to kernel's copy of user * request struct */ int flag; /* User flags */ {
 

 
/* * Local Variables */
 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ CGEN_SPECIFIC *gen_spec; /* Generic-Specific Structure pointer */ DEV_DESC *dev_desc; /* Device Descriptor Structure pointer */ SEQ_MODE_DATA6 *msdp; /* Mode sense data pointer */ CGEN_ACTION action; /* Generic Action Structure */ struct devget *devget; /* Device get ioctl */ struct device *device; /* Used for devget only */ struct controller *cont; /* Used for devget only */ long retries; /* The number of times to try to * do a mode sense for devget */ int s; /* Saved IPL */ /* Device unit number */ u_long ccb_status; /* CCB status */ u_long chk_status; /* Check condition status */ static u_char module[] = "cgen_ioctl"; /* Module name */
 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 

 
/* * Get pointers */ pdrv_dev = GET_PDRV_PTR(dev); if( pdrv_dev == (PDRV_DEVICE *)NULL) { /* * There is no Peripheral Device Structure */ PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK(pdrv_dev, s); return(ENXIO); } gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific; if( gen_spec == (CGEN_SPECIFIC *)NULL){ /* * No Generic-Specific Structure */ PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK(pdrv_dev, s); return(ENXIO); }
 
device = camdinfo[pdrv_dev->pd_log_unit];
 
cont = camminfo[device->ctlr_num];
 
dev_desc = pdrv_dev->pd_dev_desc;
 
/* * Look at command to determine next action. */ switch (cmd) {
 
case DEVIOCGET: /* device status */ devget = (struct devget *)data; bzero(devget,sizeof(struct devget)); devget->category = DEV_SCSI; devget->bus = DEV_SCSI; bcopy(DEV_SCSI_GEN, devget->interface, strlen(DEV_SCSI_GEN)); bcopy(dev_desc->dd_dev_name, devget->device, DEV_SIZE); devget->adpt_num = cont->slot; devget->nexus_num = 0; devget->bus_num = DEV_BUS_ID(dev); devget->ctlr_num = device->ctlr_num; devget->rctlr_num = 0; devget->slave_num = DEV_TARGET(dev) ; bcopy("generic", devget->dev_name, 6); devget->unit_num = ((pdrv_dev->pd_target << 3) | pdrv_dev->pd_lun);
 
PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s); devget->soft_count = pdrv_dev->pd_soft_err; devget->hard_count = pdrv_dev->pd_hard_err;
 
devget->stat = gen_spec->gen_flags; devget->category_stat = gen_spec->gen_flags; PDRV_IPLSMP_UNLOCK(pdrv_dev, s);
 
/* * Do a mode sense to check for write-locked drive. * The first SCSI mode sense command can fail due to * unit attention. */
 
retries = 0; do {
 
/* * Issue a mode sense command */ /* * Clear out Generic Action Structure */ bzero(&action, sizeof(CGEN_ACTION));
 
cgen_mode_sns( pdrv_dev, &action, cgen_done,SEQ_NO_PAGE, ALL_PCFM_CURRENT, CGEN_SLEEP);
 
if(action.ccb == (CCB_SCSIIO *)NULL) { PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: devget NULL CCB\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
/* Must return 0 for devget */ return(0); }
 
if(action.ccb_status == CAT_CMP ){
 
/* * GOOD Status. Fill in rest of devget struct */ msdp = (SEQ_MODE_DATA6 *)action.ccb->cam_data_ptr;
 
if( msdp->sel_head.wp != NULL ){ /* * DEVICE is write locked. */ devget->stat |= DEV_WRTLCK; } /* * Do you need to set up the device's specifics? * For tapes, need to look at the density field * returned in the MODE SENSE data. Implement * the specifics for your device. */
 
}
 
/* * Release the CCB and the memory used for the * mode sense data back to the system. */ if( action.ccb != (CCB_SCSIIO *)NULL) { CHK_RELEASE_QUEUE(pdrv_dev, action.ccb);
 
CGEN_REL_MEM( action.ccb );
 
ccmn_rel_ccb((CCB_HEADER *)action.ccb ); }
 
retries++;
 
} while( (retries < 3) && (action.ccb_status != CAT_CMP));
 
/* * Since this is a devget, always return success. */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module)); return(0); break;
 

 
default: return (ENXIO); break; } return (0);
 
} /* end of cgen_ioctl */
 

 

 
/* ----------------------------------------------------------- */ /* * Generic *done routines */
 

 
/* ----------------------------------------------------------- */ /* Function description. * * * Routine Name: cgen_done() * * Functional Description: * * Entry point for all NON-user I/O requests. * If the CCB does not contain a buf struct pointer in the * Peripheral Device Driver Working Set Structure, then * issue a wakeup system call on the address of the CCB. * * * * Call Syntax: * * cgen_done( ccb ) * * CCB_SCSIIO *ccb; * * * * * * Returns : * None */
 

 
void cgen_done (ccb) CCB_SCSIIO *ccb; /* SCSI I/O CCB pointer */ {
 
/* * Local variable */ PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */
 

 
int s; /* Saved IPL */ static u_char module[] = "cgen_done"; /* Module name */
 

 
pdrv_dev = (PDRV_DEVICE *)((PDRV_WS *)ccb->cam_pdrv_ptr)->pws_pdrv;
 

 
if( pdrv_dev == NULL ){ panic("cgen_done: NULL PDRV_DEVICE pointer"); return; } PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
/* * Remove from active lists */ ccmn_rem_ccb( pdrv_dev, ccb );
 
/* * To prevent race conditions on smp machines... */ PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s);
 
/* * Check to see if buf struct pointer is filled in. * It should not be for this routine. */ if( (struct buf *)ccb->cam_req_map == NULL){
 
/* * This is not an user I/O CCB */ wakeup(ccb); } else {
 
LOG_ERROR("Implement your error logging");
 
wakeup(ccb); } PDRV_IPLSMP_UNLOCK(pdrv_dev, s);
 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module)); return;
 
} /* end of cgen_done */
 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * Routine Name: cgen_iodone * * Functional description: * * This routine is called by lower levels when a user I/O * request has been acted on by the lower levels. * * Due the its buffered-mode operation, the target can * return good status without transferring the data to * media Notifcation of media error occurs sometime later. * * Side Effects: * Based on CAM status, the user buffer struct is modified * to reflect either successful completion of the I/O * transfer or error status. * * Flags are set in the CGEN_SPECIFIC structure to reflect * events detected. * * Call Syntax * cgen_iodone( ccb ) * CCB_SCSIIO * ccb; * * * Returns: * None * */
 
void cgen_iodone( ccb )
 
CCB_SCSIIO *ccb; /* SCSI I/O CCB pointer */
 
{ PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ CGEN_SPECIFIC *gen_spec; /* Generic-Specific Structure pointer */ struct buf *bp; /* User I/O buf struct pointer */ u_long ccb_status; /* Result of CCB status */ u_long chk_status; /* Result of check condition status */ int s; /* Saved IPL */ dev_t dev; /* Major/minor number pair */ static u_char module[] = "cgen_iodone"; /* Module name */
 

 
/* * Peripheral Device Structure and Generic-Specific Structure */ pdrv_dev = (PDRV_DEVICE *)((PDRV_WS *)ccb->cam_pdrv_ptr)->pws_pdrv;
 
gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific;
 
dev = pdrv_dev->pd_dev;
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
/* * Get user buf struct */ bp = (struct buf *)ccb->cam_req_map; if( bp == (struct buf *)NULL) { /* * There should be a buf struct if this routine is called. */
 
PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK( pdrv_dev, s); ccmn_rem_ccb( pdrv_dev, ccb ); /* * Issue a wakeup system call on this CCB */ wakeup( ccb ); return; } /* * Lock to prevent race conditions for asynchronous * I/O (nbuf I/O). */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s);
 

 

 
/* * Remove this CCB from the active list */ ccmn_rem_ccb( pdrv_dev, ccb );
 
/* Get completion status */
 
ccb_status = ccmn_ccb_status( (CCB_HEADER *)ccb );
 
/* * Save residual counts */ bp->b_resid = ccb->cam_resid; gen_spec->gen_resid = ccb->cam_resid;
 
switch( ccb_status ) {
 
case CAT_CMP:
 
/* * The cam_resid flag indicates the number * of bytes that were not transferred. * If anything but NULL, the device has problems. */ if( ccb->cam_resid != NULL){ LOG_ERROR("Implement your error logging"); PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: Status = CMP but resid not NULL\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
CGEN_BERROR(bp, ccb->cam_resid, EIO);
 
}
 
break;
 
case CAT_CMP_ERR:
 
/* * Had some sort of SCSI status other than GOOD, so * must look at each SCSI status type to determine * how to handle. */
 
/* * Reason is either a check * condition or reservation conflict. */ switch(ccb->cam_scsi_status) { default: case SCSI_STAT_GOOD: case SCSI_STAT_CONDITION_MET: case SCSI_STAT_INTERMEDIATE: case SCSI_STAT_INTER_COND_MET: case SCSI_STAT_COMMAND_TERMINATED: case SCSI_STAT_QUEUE_FULL: CGEN_BERROR(bp, ccb->cam_resid, EIO);
 

 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: default SCSI STATUS = 0x%x\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, ccb->cam_scsi_status));
 
LOG_ERROR("Implement your error logging");
 
break;
 

 
case SCSI_STAT_BUSY: CGEN_BERROR(bp, ccb->cam_resid, EIO); PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: device BUSY STATUS\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
LOG_ERROR("Implement your error logging");
 
break;
 
case SCSI_STAT_RESERVATION_CONFLICT: /* * This unit is reserved by another initiator. * This should not happen */ CGEN_BERROR(bp, ccb->cam_resid, EBUSY);
 

 
LOG_ERROR("Implement your error logging"); PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: Reservation conflict.\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module));
 
break;
 
case SCSI_STAT_CHECK_CONDITION:
 
/* * Call cgen_ccb_chkcond() * to handle the check condition */ chk_status = cgen_ccb_chkcond(ccb, pdrv_dev);
 
/* * Determine what to do. */ switch ( chk_status ) { /* * Look at common conditions first. * Note that the gen_spec->ts_resid is handled * in the check condition..... */ case CHK_EOM :
 
case CHK_FILEMARK:
 
case CHK_ILI:
 
case CHK_SOFTERR:
 
case CHK_INFORMATIONAL:
 
case CHK_CHK_NOSENSE:
 
case CHK_SENSE_NOT_VALID:
 
case CHK_NOSENSE_BITS:
 
case CHK_NOT_READY:
 
case CHK_HARDERR:
 
case CHK_UNIT_ATTEN:
 
case CHK_DATA_PROT:
 
case CHK_UNSUPPORTED:
 
case CHK_CMD_ABORTED:
 
case CHK_UNKNOWN_KEY:
 
default: break;
 
} /* end of switch for check condition */
 
break; /* end of scsi_status check condition */
 
} /* end of switch of SCSI status */
 
break;
 

 
case CAT_INPROG: case CAT_UNKNOWN: case CAT_CCB_ERR:
 
CGEN_BERROR( bp, bp->b_bcount, EIO);
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: CCB status: %s\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, cam_ccb_str((CCB_HEADER *)ccb))); break;
 
case CAT_RESET: case CAT_BUSY:
 
/* * Status should only be busy. * Don't have to abort the active queues.The CCBs that * are queued will be returned to use. This action is * defined in the CAM specification. * Don't error log this because the error log will fill * up with reset pending messages.... */ if( CAM_STATUS(ccb) == CAM_BUSY) { gen_spec->gen_state_flags |= CGEN_RESET_PENDING_STATE; } PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: CCB status: %s\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, cam_ccb_str((CCB_HEADER *)ccb)));
 
CGEN_BERROR( bp, ccb->cam_resid, EIO);
 
break;
 
case CAT_SCSI_BUSY: case CAT_BAD_AUTO: case CAT_DEVICE_ERR: /* * Error log this */ LOG_ERROR("Implement your error logging");
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: CCB status: %s\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, cam_ccb_str((CCB_HEADER *)ccb)));
 
CGEN_BERROR( bp, ccb->cam_resid, EIO);
 
break;
 

 
case CAT_NO_DEVICE: /* * Error log this. */
 
LOG_ERROR("Implement your error logging"); PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: CCB status: %s\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, cam_ccb_str((CCB_HEADER *)ccb)));
 
CGEN_BERROR(bp, ccb->cam_resid, ENXIO);
 
break;
 
case CAT_ABORT:
 
/* * Return is a result of walking the * active lists and aborting the ccb's */ PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: CCB status: %s\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, cam_ccb_str((CCB_HEADER *)ccb)));
 
if( CAM_STATUS( ccb ) == CAM_REQ_ABORTED ){ } else if( CAM_STATUS( ccb ) == CAM_UA_ABORT ){
 
} else if( CAM_STATUS( ccb ) == CAM_UA_TERMIO ){ } else if( CAM_STATUS( ccb ) == CAM_REQ_TERMIO ){ } else { } break;
 

 
default: /* * Error log this; should never get the default condition. */ LOG_ERROR("Implement your error logging"); PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: CCB status: %s\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), module, cam_ccb_str((CCB_HEADER *)ccb)));
 
CGEN_BERROR( bp, bp->b_bcount, EIO);
 
break;
 
} /* end switch on cam status */
 
/* * Unlock */ PDRV_IPLSMP_UNLOCK(pdrv_dev, s)
 
/* All flags are set; call iodone on this buf struct. */ iodone( bp );
 
/* * Do not attempt to release data buffers for user I/O, * because a system panic will result. */
 
/* * Check the release queue prior to releasing the CCB */ CHK_RELEASE_QUEUE(pdrv_dev, ccb);
 
/* * Release the CCB */ ccmn_rel_ccb((CCB_HEADER *)ccb );
 
PRINTD(DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(dev), DEV_TARGET(dev), DEV_LUN(dev),module));
 
return;
 
} /* end of cgen_iodone */
 

 

 

 
/* ----------------------------------------------------------- */ /* * Asynchronous notification routine. */
 
/* ----------------------------------------------------------- */ /* Function description. * This routine is called when an AEN, BDR, or Bus reset has * occurred. This routine sets CGEN_RESET_STATE and clears * CGEN_RESET_PEND_STATE for BDR's and Bus resets. For AEN's * set CGEN_UNIT_ATTEN_STATE. * * * Call syntax * cgen_async( opcode, path_id, target, lun, buf_ptr, data_cnt) * u_long opcode; Reason why called * u_char path_id; Bus number * u_char target; Target number * u_char lun; Logical unit number * cadd_t buf_ptr; Buffer address AEN's * u_char data_cnt; Number of bytes valid; * * Implicit inputs * NONE * * Implicit outputs * Setting and clearing of state flags * * Return values * NONE * * TO DO: * Recovery for unit. */
 
void cgen_async( opcode, path_id, target, lun, buf_ptr, data_cnt) u_long opcode; /* Reason called */ u_char path_id; /* Bus number */ u_char target; /* Target number */ u_char lun; /* Logical unit number */ caddr_t buf_ptr; /* Buffer address AEN's */ u_char data_cnt; /* Number of bytes valid; */
 
{
 
/* * Local Variables */
 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */
 
CGEN_SPECIFIC *gen_spec; /* Generic-Specific Structure pointer */
 
dev_t dev; /* Device number */ int s; /* Saved IPL */ static u_char module[] = "cgen_async"; /* Module name */
 
PRINTD(path_id, target, lun, (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", path_id, target, lun, module));
 
/* * Get device number */ dev = MAKE_DEV( path_id, target, lun );
 

 
pdrv_dev = GET_PDRV_PTR(dev);
 
/* * If pdrv_device == NUll, then the device has never been * opened and this section should not have been reached. */ if( pdrv_dev == (PDRV_DEVICE *)NULL){ LOG_ERROR("Implement your error logging"); PRINTD(path_id, target, lun, (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: pdrv_dev == 0\n", path_id, target, lun, module)); return; }
 
/* * If gen_spec == NUll, then the device has never been opened * and this section should not have been reached */
 
gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific;
 
if( gen_spec == (CGEN_SPECIFIC *)NULL){ PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK(pdrv_dev, s); return; }
 
/* * Find out why this section was reached */ if((opcode & AC_SENT_BDR ) != NULL){ PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s); gen_spec->gen_state_flags |= CGEN_RESET_STATE; gen_spec->gen_state_flags &= ~CGEN_RESET_PENDING_STATE; LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK(pdrv_dev, s); } if((opcode & AC_BUS_RESET ) != NULL){ PDRV_IPLSMP_LOCK(pdrv_dev, LK_RETRY, s); gen_spec->gen_state_flags |= CGEN_RESET_STATE; gen_spec->gen_state_flags &= ~CGEN_RESET_PENDING_STATE; LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK(pdrv_dev, s); } if((opcode & AC_SCSI_AEN ) != NULL){ CGEN_LOCK_OR_STATE(pdrv_dev, gen_spec, CGEN_UNIT_ATTEN_STATE); }
 
PRINTD(path_id, target, lun, (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", path_id, target, lun, module)); return;
 
} /* End of cgen_async() */
 

 
/* ----------------------------------------------------------- */
 
/* * Command Support Routines */
 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * This routine issues a SCSI TEST UNIT READY command * to the unit. * * The varible sleep for this version will always be TRUE. This * directs the code to sleep waiting for comand status. * * Call syntax * cgen_ready( pdrv_dev, action, done, sleep) * PDRV_DEVICE *pdrv_dev; * Peripheral Device Structure pointer * CGEN_ACTION *action; Generic Action Structure pointer * void (*done)(); Completion routine * u_long sleep; Whether to sleep * * Implicit inputs * NONE * * Implicit outputs * The various statuses into the caller's action struct. * * Return values * NONE * * TO DO: * */
 

 
void cgen_ready( pdrv_dev, action, done, sleep) PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ CGEN_ACTION *action; /* Generic Action Structure pointer */ void (*done)(); /* Completion routine */ u_long sleep; /* Whether to sleep */
 
{
 
/* * LOCAL variables */ DEV_DESC *dev_desc = pdrv_dev->pd_dev_desc; /* * Device Descriptor Structure pointer */
 
int s; /* Saved IPL */ int s1; /* Throwaway IPL */ u_char sense_size; /* Reguest sense buffer size */ static u_char module[] = "cgen_ready"; /* Module name */
 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
/* * See if the System administrator has set the request sense * size. This is for autosense. If there is an error, * the lower levels will do a request sense. */ sense_size = GET_SENSE_SIZE( pdrv_dev );
 
/* * Call the common routine to create the CCB for the test * unit ready. It will return a CCB that is already being * processed. */ action->act_ccb = ccmn_tur(pdrv_dev, sense_size,(u_long)CAM_DIR_NONE, done, (u_char)NULL, CGEN_TME_5);
 
/* * Check if CCB is NULL. If so, the generic macro fills out * the error logs and the action return values. */ if(action->act_ccb == (CCB_SCSIIO *)NULL){ CGEN_NULLCCB_ERR(action, pdrv_dev, module); return; }
 
/* * Check to see if sleep is not set */ if( sleep != CGEN_SLEEP){ return; }
 

 
/* * Check the CCB to make sure it is in progress before * going to sleep. Raise the IPL to block the * interrupt; the sleep will lower it. */
 
PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); while( CAM_STATUS( action->act_ccb) == CAM_REQ_INPROG ){ /* * Sleep on address of CCB, but NON interruptable */ PDRV_SMP_SLEEPUNLOCK( action->act_ccb, PRIBIO, pdrv_dev);
 
/* * Get the lock again */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s1 ); }
 

 

 
/* * At this point, the command has been sent down and * completed. Now check for status. */
 

 
action->act_ccb_status = ccmn_ccb_status((CCB_HEADER *)action->act_ccb);
 
switch( action->act_ccb_status ) {
 
case CAT_CMP:
 
/* * GOOD status; just return. */ break;
 
case CAT_CMP_ERR:
 
/* * Had a SCSI status other than good; * must look at each possible SCSI status to * determine our action. */
 

 
action->act_scsi_status = action->act_ccb->cam_scsi_status; switch(action->act_scsi_status) { default: case SCSI_STAT_GOOD: case SCSI_STAT_CONDITION_MET: case SCSI_STAT_BUSY: case SCSI_STAT_INTERMEDIATE: case SCSI_STAT_INTER_COND_MET: case SCSI_STAT_COMMAND_TERMINATED: case SCSI_STAT_QUEUE_FULL: case SCSI_STAT_RESERVATION_CONFLICT: case SCSI_STAT_CHECK_CONDITION:
 
/* Call cgen_ccb_chkcond() * to handle the check condition. */ action->act_chkcond_error = cgen_ccb_chkcond(action->act_ccb, pdrv_dev);
 
/* * Now determine what to do. */ switch ( action->act_chkcond_error ) {
 
case CHK_UNIT_ATTEN: case CHK_NOT_READY: case CHK_INFORMATIONAL: case CHK_SOFTERR: case CHK_EOM : case CHK_FILEMARK: case CHK_ILI: case CHK_CHK_NOSENSE: case CHK_SENSE_NOT_VALID: case CHK_NOSENSE_BITS: case CHK_HARDERR: case CHK_DATA_PROT: case CHK_UNSUPPORTED: case CHK_CMD_ABORTED: case CHK_UNKNOWN_KEY: default: break; } break; /* end of scsi_status check condition */
 
} /* end of switch of scsi status */
 
break; /* End of CAM_CMP_ERR */
 
case CAT_INPROG: case CAT_UNKNOWN: case CAT_CCB_ERR: case CAT_RESET: case CAT_BUSY: case CAT_SCSI_BUSY: case CAT_BAD_AUTO: case CAT_DEVICE_ERR: case CAT_NO_DEVICE: case CAT_ABORT: action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO; default: /* * Error log this; it should never occur. */ action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO; LOG_ERROR("Implement your error logging");
 
break;
 
} /* end switch on cam status */
 
/* * Now unlock */ PDRV_IPLSMP_UNLOCK(pdrv_dev, s);
 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
return;
 
} /* End of cgen_ready() */
 

 

 
/* ----------------------------------------------------------- */ /* Function description. * This routine runs down the Mode Select Table Structure for * this device, if one is defined. * * Call syntax * cgen_open_sel( pdrv_dev, action,index, done, sleep) * PDRV_DEVICE *pdrv_dev; Pointer to the Peripheral Device Structure * CGEN_ACTION *action; Generic Action Structure pointer * long ms_index; The index of the Mode Select Table * void (*done)(); Completion routine * u_long sleep; Whether to sleep * * Implicit inputs * NONE * * Implicit outputs * Return values of status of command placed in the action * struct. * * Return values * NONE * * TO DO: * No sleep and state step * Interrupted sleeps */
 

 
void cgen_open_sel( pdrv_dev, action, index, done, sleep) PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ CGEN_ACTION *action; /* Generic Action Structure pointer */ void (*done)(); /* Completion routine */ u_long sleep; /* Whether to sleep */
 
{
 
/* * Local Variables */ DEV_DESC *dev_desc = pdrv_dev->pd_dev_desc; /* * Device Descriptor Structure pointer */
 
MODESEL_TBL *mod_tbl = pdrv_dev->pd_dev_desc->dd_modesel_tbl; /* * Pointer to Mode Select Table Structure */
 

 
int s; /* Saved IPL */ int s1; /* Throwaway IPL */ u_char sense_size; /* Request sense buffer size */ static u_char module[] = "cgen_open_sel"; /* Module name */
 

 

 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
/* * Validate this Mode Select Table index */ if(( index >= MAX_OPEN_SELS)||(mod_tbl->ms_entry[index].ms_data == NULL)){
 
/* * The caller of this routine passed a invalid index. */
 
PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK( pdrv_dev, s );
 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: Data pointer 0 or excede OPEN_SELS\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
action->act_fatal |= (ACT_PARAMETER| ACT_FAILED); action->act_ret_error = EINVAL; return; }
 
/* * See if the System Administrator has set the request sense * size. This is for autosense. If there is an error, * the lower levels will do a request sense. */ sense_size = GET_SENSE_SIZE( pdrv_dev );
 
action->act_ccb = ccmn_mode_select( pdrv_dev, sense_size, (u_long)CAM_DIR_OUT, done, (u_char)NULL, CGEN_TIME_5, index);
 
/* * Check if CCB is NULL. If so, the macro fills out * the error logs and the action return values. */ if(action->act_ccb == (CCB_SCSIIO *)NULL){ CGEN_NULLCCB_ERR(action, pdrv_dev, module); return; }
 
/* * Check to see if sleep is set... */ if( sleep == CGEN_NOSLEEP ){ return; }
 

 
/* * Check the CCB to make sure it is in progress * before going to sleep. Raise the * IPL to block the interrupt, the sleep * will lower it. */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); while( CAM_STATUS( action->act_ccb) == CAM_REQ_INPROG ){ /* * Sleep NON interruptable on address of CCB */ PDRV_SMP_SLEEPUNLOCK( action->act_ccb, PRIBIO, pdrv_dev);
 
/* * Get the lock again */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s1 ); }
 
action->act_ccb_status = ccmn_ccb_status((CCB_HEADER *)action->act_ccb);
 
switch( action->act_ccb_status ) {
 
case CAT_CMP:
 
/* * GOOD status; just return. */ break;
 
case CAT_CMP_ERR:
 
/* * Had SCSI status other than GOOD; * must look at each possibile status and * determine what to do.. */
 

 
action->act_scsi_status = action->act_ccb->cam_scsi_status; switch(action->act_scsi_status) { default: case SCSI_STAT_GOOD: case SCSI_STAT_CONDITION_MET: case SCSI_STAT_BUSY: case SCSI_STAT_INTERMEDIATE: case SCSI_STAT_INTER_COND_MET: case SCSI_STAT_COMMAND_TERMINATED: case SCSI_STAT_QUEUE_FULL: case SCSI_STAT_RESERVATION_CONFLICT:
 
case SCSI_STAT_CHECK_CONDITION:
 
/* call cgen_ccb_chkcond() * to handle the check condition */ action->act_chkcond_error = cgen_ccb_chkcond(action->act_ccb, pdrv_dev);
 
/* * Now determine what to do. */ switch ( action->act_chkcond_error ) { /* * Look at conditions. */
 
case CHK_INFORMATIONAL: case CHK_SOFTERR: case CHK_EOM : case CHK_FILEMARK: case CHK_ILI: case CHK_CHK_NOSENSE: case CHK_SENSE_NOT_VALID: case CHK_NOSENSE_BITS: case CHK_NOT_READY: case CHK_HARDERR: case CHK_UNIT_ATTEN: case CHK_DATA_PROT: case CHK_UNSUPPORTED: case CHK_CMD_ABORTED: case CHK_UNKNOWN_KEY: default: break; } /* end of switch for check condition */
 
break; /* end of scsi_status check condition */
 
} /* end of switch of scsi status */
 
break; /* End of CAM_CMP_ERR */
 
case CAT_INPROG: case CAT_UNKNOWN: case CAT_CCB_ERR: case CAT_RESET: case CAT_BUSY: case CAT_SCSI_BUSY: case CAT_BAD_AUTO: case CAT_DEVICE_ERR: case CAT_NO_DEVICE: case CAT_ABORT: action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO; break; default: /* * Error log this; it should never occur */ action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO; LOG_ERROR("Implement your error logging");
 
break;
 
} /* end switch on cam status */
 
/* * Now unlock */ PDRV_IPLSMP_UNLOCK(pdrv_dev, s);
 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
return;
 
} /* End of cgen_open_sel() */
 

 

 
/* ----------------------------------------------------------- */ /* Function description. * * This routine issues a SCSI_MODE_SENSE command * to the unit. The CGEN_ACTION structure is filled in for the * the caller. The varible sleep directs the code to sleep * waiting for comand status. * * Call syntax * cgen_mode_sns ( pdrv_dev, action, done, page_code, page_cntl, sleep) * PDRV_DEVICE *pdrv_dev; Peripheral Device Structure pointer * CGEN_ACTION *action; Generic Action Structure pointer * void (*done)(); Completion routine * u_char page_code; The page we want * u_char page_cntl; The page control field * u_long sleep; Whether we sleep * * Implicit inputs * NONE * * Implicit outputs * CGEN_ACTION structure is filled in based on the CCB's * completion status. * * Return values * * TO DO: * No sleep and state step * Interrupted sleeps */
 

 
void cgen_mode_sns ( pdrv_dev, action, done, page_code, page_cntl, sleep) PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ CGEN_ACTION *action; /* Generic Action Structure pointer */ void (*done)(); /* Completion routine */ u_char page_code; /* The page wanted */ u_char page_cntl; /* The page control field */ u_long sleep; /* Whether to sleep */
 
{
 

 
/* * Local Variables */
 
DEV_DESC *dev_desc = pdrv_dev->pd_dev_desc; /* * Device Descriptor Structure pointer */ ALL_MODE_SENSE_CDB6 *mod_cdb; /* Mode sense CDB pointer */ u_char *data_buf; /* Data buffer pointer */ u_long data_buf_size; /* Size of the data buffer */ int s; /* Saved IPL */ int s1; /* Throwaway IPL */ u_char sense_size; /* Size of request sense buffer */ static u_char module[] = "cgen_mode_sns"; /* Module name */
 

 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module)); /* * Get data_buffer size for the mode sense command which will * use the 6-byte mode select CDB. The mode sense data will * have a 4-byte parameter header and an 8-byte descriptor. */ data_buf_size = sizeof(SEQ_MODE_HEAD6) + sizeof(SEQ_MODE_DESC);
 
/* * Now get the page size */ switch( page_code ) {
 
case SEQ_NO_PAGE: /* * The caller of the routine does not want any page data * for the device. Get only the mode parameter header and * mode descriptor. */ break; /* * Check on generic pages first. */
 
case ALL_PGM_DISCO_RECO: data_buf_size += sizeof( ALL_DISC_RECO_PG); break;
 
case ALL_PGM_PERIPH_DEVICE: data_buf_size += sizeof( ALL_PERIPH_DEV_PG); break;
 
case ALL_PGM_CONTROL_MODE: data_buf_size += sizeof( ALL_CONTROL_PG); break;
 
/* * Check on the sequential pages (tapes). */ case SEQ_PGM_ERR_RECOV: data_buf_size += sizeof( SEQ_ERR_RECOV_PG); break;
 
case SEQ_PGM_DEV_CONF: data_buf_size += sizeof( SEQ_DEV_CONF_PG); break;
 
case SEQ_PGM_PART1: data_buf_size += sizeof( SEQ_PART1_PG); break;
 
case SEQ_PGM_PART2: data_buf_size += sizeof( SEQ_PART1_PG); break;
 
case SEQ_PGM_PART3: data_buf_size += sizeof( SEQ_PART1_PG); break;
 
case SEQ_PGM_PART4: data_buf_size += sizeof( SEQ_PART1_PG); break;
 
default: /* * Invalid PAGE code. */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK( pdrv_dev, s );
 
action->act_fatal |= (ACT_PARAMETER | ACT_FAILED); action->act_ret_error = EINVAL; return; break;
 
} /* end switch */
 
if(( data_buf = ccmn_get_dbuf(data_buf_size)) == (u_char *)NULL){ /* * Log the error */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); LOG_ERROR("Implement your error logging"); PDRV_IPLSMP_UNLOCK( pdrv_dev, s );
 
action->act_fatal |= ( ACT_RESOURCE | ACT_FAILED); action->act_ret_error = ENOMEM; return; } /* * See if the System Administrator has set its request sense * size. This is for autosense. If there is an error, * the lower levels will do a request sense. */ sense_size = GET_SENSE_SIZE( pdrv_dev );
 
/* * Get a SCSI I/O CCB */ action->act_ccb = ccmn_io_ccb_bld( pdrv_dev->pd_dev,data_buf, data_buf_size, sense_size, (u_long)CAM_DIR_IN, done,(u_char)NULL, CGEN_TIME_5, (struct buf *)NULL);
 
/* * Check if CCB is NULL. If so, the macro * error logs it and fills out action return values. */ if(action->act_ccb == (CCB_SCSIIO *)NULL){ CGEN_NULLCCB_ERR(action, pdrv_dev, module); /* * Release data buffer */ ccmn_rel_dbuf( data_buf, data_buf_size); return; }
 
/* * Build 6-byte mode select command in the CDB. */ mod_cdb = (ALL_MODE_SENSE_CDB6 *) action->act_ccb->cam_cdb_io.cam_cdb_bytes; mod_cdb->opcode = ALL_MODE_SENSE6_OP; mod_cdb->lun = 0; mod_cdb->page_code = page_code; mod_cdb->pc = page_cntl; mod_cdb->alloc_len = data_buf_size;
 
/* * set CDB length */ action->act_ccb->cam_cdb_len = sizeof(ALL_MODE_SENSE_CDB6);
 

 
/* * Send the mode sense command down to the lower levels. */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s); ccmn_send_ccb( pdrv_dev, (CCB_HEADER *) action->act_ccb, NOT_RETRY); PDRV_IPLSMP_UNLOCK( pdrv_dev, s);
 
/* * Do we go to sleep. */ if( sleep == CGEN_NOSLEEP) { return; }
 
/* * Check the CCB to make sure it is in progress * before going to sleep. Raise the IPL to * block the interrupt; the sleep will lower it. */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s ); while( CAM_STATUS( action->act_ccb) == CAM_REQ_INPROG ){ /* * Sleep NON-interruptable on address of CCB */ PDRV_SMP_SLEEPUNLOCK( action->act_ccb, PRIBIO, pdrv_dev);
 
/* * Get the lock again */ PDRV_IPLSMP_LOCK( pdrv_dev, LK_RETRY, s1 ); }
 
action->act_ccb_status = ccmn_ccb_status((CCB_HEADER *)action->act_ccb);
 
switch( action->act_ccb_status ) {
 
case CAT_CMP:
 
/* * GOOD Status; just return. */ break;
 
case CAT_CMP_ERR:
 
/* * Received SCSI status other than GOOD * must look at each of the SCSI statuses to determine * our action. */
 

 
action->act_scsi_status = action->act_ccb->cam_scsi_status; switch(action->act_scsi_status) { default: case SCSI_STAT_GOOD: case SCSI_STAT_CONDITION_MET: case SCSI_STAT_BUSY: case SCSI_STAT_INTERMEDIATE: case SCSI_STAT_INTER_COND_MET: case SCSI_STAT_COMMAND_TERMINATED: case SCSI_STAT_QUEUE_FULL: LOG_ERROR("Implement your error logging"); action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO; break;
 
case SCSI_STAT_RESERVATION_CONFLICT: /* * This unit reserved by another * initiator this should not * happen */ LOG_ERROR("Implement your error logging"); action->act_fatal |= ACT_FAILED; action->act_ret_error = EBUSY; break;
 
case SCSI_STAT_CHECK_CONDITION:
 
/* * Call cgen_ccb_chkcond() * to handle the check condition */ action->act_chkcond_error = cgen_ccb_chkcond(action->act_ccb, pdrv_dev);
 
/* * Now determine what to do. */ switch ( action->act_chkcond_error ) { /* * Look at conditions. */
 
case CHK_INFORMATIONAL: LOG_ERROR("Implement your error logging"); break; case CHK_SOFTERR: LOG_ERROR("Implement your error logging"); break;
 
case CHK_EOM : case CHK_FILEMARK: case CHK_ILI: case CHK_CHK_NOSENSE: case CHK_SENSE_NOT_VALID: case CHK_NOSENSE_BITS: case CHK_NOT_READY: case CHK_HARDERR: case CHK_UNIT_ATTEN: case CHK_DATA_PROT: case CHK_UNSUPPORTED: case CHK_CMD_ABORTED: case CHK_UNKNOWN_KEY: default: LOG_ERROR("Implement your error logging"); action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO;
 
} /* end of switch for check condition */
 
break; /* end of scsi_status check condition */
 
} /* end of switch of scsi status */
 
break; /* End of CAM_CMP_ERR */
 
case CAT_INPROG: case CAT_UNKNOWN: case CAT_CCB_ERR: case CAT_RESET: case CAT_BUSY: case CAT_SCSI_BUSY: case CAT_BAD_AUTO: case CAT_DEVICE_ERR: case CAT_NO_DEVICE: case CAT_ABORT: action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO; /* * Error log this; should never get this error. */ LOG_ERROR("Implement your error logging"); break; default: /* * Error log this; should never get this error. */ action->act_fatal |= ACT_FAILED; action->act_ret_error = EIO; LOG_ERROR("Implement your error logging");
 
break;
 
} /* end switch on cam status */
 
/* * Now unlock */ PDRV_IPLSMP_UNLOCK(pdrv_dev, s);
 

 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
return;
 
}
 

 

 

 
/* ----------------------------------------------------------- */ /* * Error Checking Routines */
 

 
/* ----------------------------------------------------------- */ /* Function description. */
 
/* * cgen_ccb_chkcond() * * Routine Name : cgen_ccb_chkcond * * Functional Description: * * * This routine handles the sns_data (sense data) for the * GENERIC driver and returns the appropriate status to the * caller. The routine is called when a CCB_SCSIIO is * returned with a CAM_STATUS of CAM_REQ_CMP_ERR ( request * completed with error) and the cam_scsi_status equals * SCSI_CHECK_CONDITION. * NOTE... * This routine must be called with the device SMP LOCKED. * * Call Syntax: * * cgen_ccb_chkcond( ccb, pdrv_dev ) * PDRV_DEVICE *pdrv_dev; * CCB_SCSIIO *ccb; * * Return Values: * int: * * CHK_CHK_NOSENSE * The AUTO SENSE code, in the lower levels could * not get the request sense to complete without * error. Sense buffer not valid. * * CHK_SENSE_NOT_VALID * The valid bit in the sense buffer is not set; * sense data is useless. * * CHK_EOM * End of media detected. * * CHK_FILEMARK * Filemark detected. * * CHK_ILI * Incorrect length detected. * * CHK_NOSENSE_BITS * Sense key equals no sense, but there are * no bits set in byte 2 of sense data. * * CHK_SOFTERR * Soft error detected; corrected by the * unit. * * CHK_NOT_READY * The unit is not ready. * * CHK_HARDERR * The unit has detected a hard error. * * CHK_UNIT_ATTEN * The unit has either had a media change or * just powered up. * * CHK_DATA_PROT * The unit is write protected. * * CHK_UNSUPPORTED * A sense key that is unsupported * has been returned. * * CHK_CMD_ABORTED * The unit aborted this command. * * CHK_INFORMATIONAL * Unit is reporting an informational message. * * CHK_UNKNOWN_KEY * The unit has returned a sense key that * is not supported by the SCSI 2 spec. * */
 

 

 

 

 

 

 
u_long cgen_ccb_chkcond( ccb, pdrv_dev )
 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ CCB_SCSIIO *ccb; /* Pointer to SCSI I/O CCB that had the check condition */
 
{
 
/* * Local declarations */
 
/* Pointer to generic device-specific structure */ CGEN_SPECIFIC *gen_spec = (CGEN_SPECIFIC *)pdrv_dev->pd_specific;
 
/* Pointer to the sense data */ ALL_REQ_SNS_DATA *sns_data = (ALL_REQ_SNS_DATA *)ccb->cam_sense_ptr;
 
int ret_val; /* What we return */ int i; u_short asc_asq; /* The combined asc(MSB) and asq(LSB) */ static u_char module[] = "cgen_ccb_chkcond"; /* Module name */
 

 

 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: entry\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module));
 
/* * Check to see if there is valid sense data */ if(( ccb->cam_ch.cam_status & CAM_AUTOSNS_VALID) == NULL){ /* * Sense data is not valid, so return CHK_CHK_NOSENSE. */ return( CHK_CHK_NOSENSE ); } if( sns_data == NULL ) { panic("cgen_ccb_chkcond: CCB-AUTOSNS_VALID but data pointer = NULL"); return(CHK_CHK_NOSENSE); }
 
/* * Sense data is valid; find out why * and report it. */
 
PRINTD(DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), (CAMD_GENERIC ), ("[%d/%d/%d] %s: error_code, 0x%x sense_key 0x%x asc 0x%x asq 0x%x\n", DEV_BUS_ID(pdrv_dev->pd_dev), DEV_TARGET(pdrv_dev->pd_dev), DEV_LUN(pdrv_dev->pd_dev), module, sns_data->error_code, sns_data->sns_key, sns_data->asc, sns_data->asq));
 

 

 
/* * Make sure that the error code is valid. The only valid * error codes defined in SCSI 2 are 0x70 and 0x71 */ if(( sns_data->error_code != 0x70) && ( sns_data->error_code != 0x71 )){
 
return( CHK_SENSE_NOT_VALID ); }
 
/* * Get the sense key and check each case */
 
switch(sns_data->sns_key ){
 
case ALL_NO_SENSE: /* * Must look at the bit fields */ if( sns_data->filemark != NULL){ /* * Set flag */ gen_spec->gen_flags |= CGEN_TPMARK; BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_FILEMARK;
 
break; } else if( sns_data->eom != NULL){ /* * Set flag */ gen_spec->gen_flags |= CGEN_EOM; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_EOM;
 
break; } else if( sns_data->ili != NULL){ /* * Set flag */ gen_spec->gen_flags |= CGEN_SHRTREC; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_ILI;
 
break; } else { /* * Nothing is set, so more than likely an * informational warning has been sent. Make sure * that all the data went across. If it did not, * then the device has a problem. * * Check to see if there is a residual count. If * there is, fail it. ret_val == CHK_NOSENSE_BITS * else CHK_INFORMATIONAL */
 
CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid);
 
if(gen_spec->ts_resid != NULL){ ret_val = CHK_NOSENSE_BITS; } else { ret_val = CHK_INFORMATIONAL; }
 

 
break; }
 
case ALL_BLANK_CHECK: case ALL_VOL_OVERFLOW: /* * End of media, set the flag */ gen_spec->gen_flags |= CGEN_EOM; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_EOM;
 
break;
 
case ALL_RECOVER_ERR: /* * Soft error */ gen_spec->gen_flags |= CGEN_SOFTERR; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_SOFTERR;
 
break;
 
case ALL_NOT_READY: gen_spec->gen_flags |= CGEN_OFFLINE; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_NOT_READY;
 
break;
 
case ALL_MEDIUM_ERR: if( sns_data->eom != NULL){ /* * Set flag */ gen_spec->gen_flags |= CGEN_EOM; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_EOM;
 
break; } /* * Hard error on the device */ gen_spec->gen_flags |= CGEN_HARDERR; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_HARDERR;
 
break;
 
case ALL_HARDWARE_ERR: case ALL_ILLEGAL_REQ: case ALL_COPY_ABORT: case ALL_MISCOMPARE: /* * Hard error on the device */ gen_spec->gen_flags |= CGEN_HARDERR; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_HARDERR;
 
break;
 
case ALL_UNIT_ATTEN: /* * Unit has had a media change or has * been powered up. */ gen_spec->gen_state_flags |= CGEN_UNIT_ATTEN_STATE; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_UNIT_ATTEN;
 
break;
 
case ALL_DATA_PROTECT: /* * Unit is write protected */ gen_spec->gen_flags |= CGEN_WRT_PROT; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_DATA_PROT;
 
break;
 
case ALL_VENDOR_SPEC: case ALL_EQUAL: /* *These are not supported for this unit. */ ret_val = CHK_UNSUPPORTED; CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid);
 
break;
 
case ALL_ABORTED_CMD: CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_CMD_ABORTED;
 
break;
 
default: /* * Unknown sense key */ CGEN_BTOL(&sns_data->info_byte3, gen_spec->ts_resid); ret_val = CHK_UNKNOWN_KEY;
 
break; }
 
PRINTD(DEV_BUS_ID(pdrv_device->pd_dev), DEV_TARGET(pdrv_device->pd_dev), DEV_LUN(pdrv_device->pd_dev), (CAMD_GENERIC |CAMD_INOUT), ("[%d/%d/%d] %s: exit\n", DEV_BUS_ID(pdrv_device->pd_dev), DEV_TARGET(pdrv_device->pd_dev), DEV_LUN(pdrv_device->pd_dev), module));
 
/* * Return result of the checks. */
 

 
return(ret_val);
 

 
}
 

 

 

 
/**************************************************************** * * ROUTINE NAME: cgen_minphys() * * FUNCTIONAL DESCRIPTION: * This function compares the b_bcount field in the buf * structure with the maximum transfer limit for the device * (dd_max_record) in the Device Descriptor Structure. The * count is adjusted if it is greater than the limit. * * FORMAL PARAMETERS: * bp - Buf structure pointer. * * IMPLICIT INPUTS: * None. * * IMPLICIT OUTPUTS: * Modified b_bcount field of buf structure. * * RETURN VALUE: * None. * * SIDE EFFECTS: * None. * * ADDITIONAL INFORMATION: * None. * ***************************************************************/
 
void cgen_minphys(bp) struct buf *bp; {
 
PDRV_DEVICE *pdrv_dev; /* Peripheral Device Structure pointer */ DEV_DESC *dd; /* Device Descriptor Structure */
 

 
PRINTD(DEV_BUS_ID(bp->b_dev), DEV_TARGET(bp->b_dev), DEV_LUN(bp->b_dev), CAMD_GENERIC, ("[%d/%d/%d] cgen_minphys: entry bp=%xx bcount=%xx\n", DEV_BUS_ID(bp->b_dev), DEV_TARGET(bp->b_dev), DEV_LUN(bp->b_dev), bp, bp->b_bcount));
 
if ( (pdrv_dev = GET_PDRV_PTR(bp->b_dev)) == (PDRV_DEVICE *)NULL) { PRINTD(DEV_BUS_ID(bp->b_dev), DEV_TARGET(bp->b_dev), DEV_LUN(bp->b_dev), CAMD_GENERIC, ("[%d/%d/%d] cgen_minphys: No periheral device struct\n", DEV_BUS_ID(bp->b_dev), DEV_TARGET(bp->b_dev), DEV_LUN(bp->b_dev))); return; }
 
dd = pdrv_dev->pd_dev_desc;
 
/* * Get the maximun transfer size for this device. If b_bcount * is greater than maximum, then adjust it. */ if (bp->b_bcount > dd->dd_max_record ){ bp->b_bcount = dd->dd_max_record; } PRINTD(DEV_BUS_ID(bp->b_dev), DEV_TARGET(bp->b_dev), DEV_LUN(bp->b_dev), CAMD_GENERIC, ("[%d/%d/%d] cgen_minphys: exit - success\n", _BUS_ID(bp->b_dev), DEV_TARGET(bp->b_dev), DEV_LUN(bp->b_dev))); }