This driver is simplified and does not do as much error checking as a real driver would do. Also, this example uses a global SCSI request structure that does not work in real drivers, since multiple reads or writes would overwrite a command in progress.
#include "sys/param.h" #include "sys/types.h" #include "sys/user.h" #include "sys/buf.h" #include "sys/errno.h" #include "sys/cmn_err.h" #include "sys/cred.h" #include "sys/ddi.h" #include "sys/systm.h" #include "sys/scsi.h" int sdk_devflag = 0; #define ADAPT 0 /* SCSI host adapter */ #define TARGET 7 /* the disk will have target ID #7 */ #define LU 0 /* and logical unit #0 */ #define TIMEOUT (30*HZ)/* wait 30 secs for SCSI device to respond */ #define DIRECTACCESS 0 /* First byte of inqry cmnd */ unchar scsi_read[] = {0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unchar scsi_write[] = {0x2a, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int sdk_inuse = 0; int sdk_driver; struct scsi_target_info *sdk_info; struct scsi_request sdk_req; u_char sdk_sensebuf[SCSI_SENSE_LEN]; /* SCSI_SENSE_LEN from scsi.h */ /* forward definitions*/ int sdk_strategy(struct buf *bp); void sdk_notify(struct scsi_request *req); /* * sdk_open - Open the SCSI device exclusively. * * Issue a SCSI inquiry command upon device and ensure * it is a direct access device. */ int sdk_open(dev_t *devp, int flag, int otyp, cred_t *crp) { if (sdk_inuse) return EBUSY; /* Get driver number */ sdk_driver = scsi_driver_table[ADAPT]; /* * Call through scsi_info to get inquiry data and to * find out if a device is at the address we want. */ sdk_info = (*scsi_info[sdk_driver])(ADAPT, TARGET, LU); if (sdk_info == NULL) return ENODEV; /* * Is it a direct access device? We could check the * entire inquiry buffer to ensure it is actually the * correct device. */ if (sdk_info->si_inq[0] != DIRECTACCESS) return ENXIO; /* * It's a direct access device (disk drive). Initialize * the connection to the host adapter driver. */ if ((*scsi_alloc[sdk_driver]) (ADAPT, TARGET, LU, 1, NULL) == 0) return EBUSY; /* * We have successfully allocated a connection between * sdk and the host adapter driver. Initialize the * scsi_request structure, and mark the driver as being * in use. */ sdk_inuse = 1; bzero(&sdk_req, sizeof(sdk_req)); sdk_req.sr_ctlr = ADAPT; sdk_req.sr_target = TARGET; sdk_req.sr_lun = LU; sdk_req.sr_timeout = TIMEOUT; sdk_req.sr_sense = sdk_sensebuf; sdk_req.sr_senselen = sizeof(sdk_sensebuf); sdk_req.sr_notify = sdk_notify; return 0; } /* sdk_close - close the device and free the subchannel. */ int sdk_close(dev_t dev, int flag, int otyp, cred_t *crp) { (*scsi_free[sdk_driver])(ADAPT, TARGET, LU, NULL); sdk_inuse = 0; return 0; } /* * sdk_read - read from the SCSI device, ensuring an even * block count and a word-aligned address. */ sdk_read(dev_t dev, uio_t *uiop, cred_t *crp) /* * sdk_write - write to the SCSI device, ensuring an even * block count and a word-aligned address. */ sdk_write(dev_t dev, uio_t *uiop, cred_t *crp) /* * sdk_strategy - do the dirty work of the I/O. * Use either the SCSI read or write command as * appropriate. Modify the block number and block counts * within the command buffer. Simply return here; * physio( ) will wait for an iodone( ). */ int sdk_strategy(struct buf *bp) { int blkno, blkcount; /* Prime the subchannel communication block. */ blkno = bp->b_blkno; blkcount = BTOBB(bp->b_bcount); sdk_req.sr_command = bp->b_flags & B_READ ? scsi_read : scsi_write; sdk_req.sr_command[2] = (char)(blkno>>24); sdk_req.sr_command[3] = (char)(blkno>>16); sdk_req.sr_command[4] = (char)(blkno>>8); sdk_req.sr_command[5] = (char) blkno; sdk_req.sr_command[7] = (char)(blkcount>>8); sdk_req.sr_command[8] = (char) blkcount; sdk_req.sr_cmdlen = SC_CLASS1_SZ; sdk_req.sr_flags = bp->b_flags & B_READ ? SRF_DIR_IN : 0; if (BP_ISMAPPED(bp)) { sdk_req.sr_buffer = bp->b_dmaaddr; sdk_req.sr_buflen = bp->b_bcount; sdk_req.sr_flags |= SRF_MAP; } else { sdk_req.sr_buffer = NULL; sdk_req.sr_buflen = bp->b_bcount; sdk_req.sr_flags = SRF_MAPBP; } sdk_req.sr_bp = bp; /* required for SRF_MAPBP, but a * convenience in all cases */ /* Perform the SCSI operation. */ (*scsi_command[sdk_driver])(&sdk_req); } /* * sdk_notify - SCSI command completion notification routine * * Simply check for errors and wake up physio( ) with * an iodone( ) on the buffer. * Note that a more robust driver would be more thorough * about error handling by retrying errors, giving more * information about error types, etc. */ void sdk_notify(struct scsi_request *req) { register struct buf *bp = req->sr_bp; if ((req->sr_status != SC_GOOD) || (req->sr_scsi_status != ST_GOOD) || (req->sr_sensegotten < 0)) { cmn_err(CE_NOTE, "sdk: Error: driver stat 0x%x, scsi stat 0x%x" " sensegotten %d\n", req->sr_status, req->sr_scsi_status, req->sr_sensegotten); bioerror(bp, EIO); } else if (req->sr_sensegotten > 0) { cmn_err(CE_NOTE, "sdk: Error: sensekey 0x%x\n", sdk_sensebuf[2] & 0x0F); bioerror(bp, EIO); } bp->b_resid = req->sr_resid; biodone(bp); }