Writing Device Drivers

SCSI Target Drivers

Hardware Configuration File

Because SCSI devices are not self-identifying, a hardware configuration file is required for a target driver (see driver.conf(4) and scsi(4) for details). A typical configuration file looks like this:

	name="xx" class="scsi" target=2 lun=0;

The system reads the file during autoconfiguration and uses the class property to identify the driver's possible parent. The system then attempts to attach the driver to any parent driver that is of class scsi. All host bus adapter drivers are of this class. Using the class property rather than the parent property enables the target driver to be attached to any host bus adapter driver that finds the expected device at the specified target and lun ids. The target driver is responsible for verifying this in its probe(9E) routine.

Declarations and Data Structures

Target drivers must include the header file <sys/scsi/scsi.h>.

SCSI target drivers must also include this declaration:

	char _depends_on[] = "misc/scsi";

scsi_device Structure

The host bus adapter driver allocates and initializes a scsi_device(9S) structure for the target driver before either the probe(9E) or attach(9E) routine is called. This structure stores information about each SCSI logical unit, including pointers to information areas that contain both generic and device- specific information. There is one scsi_device(9S) structure for each logical unit attached to the system. The target driver can retrieve a pointer to this structure by calling ddi_get_driver_private(9F).


Caution - Caution -

Because the host bus adapter driver uses the private field in the target device's dev_info structure, target drivers should not use ddi_set_driver_private(9F).


The scsi_device(9S) structure contains the following fields:

	
		struct scsi_address 				sd_address;			
 	dev_info_t								*sd_dev;			
 	kmutex_t									sd_mutex;
 	struct scsi_inquiry					*sd_inq;
 	struct scsi_extended_sense 		*sd_sense;
 	caddr_t									sd_private;						

sd_address is a data structure that is passed to the SCSI resource allocation routines.

sd_dev is a pointer to the target's dev_info structure.

sd_mutex is a mutex for use by the target driver. This is initialized by the host bus adapter driver and can be used by the target driver as a per-device mutex. Do not hold this mutex across a call to scsi_transport(9F) or scsi_poll(9F). See Chapter 4, Multithreading, for more information on mutexes.

sd_inq is a pointer for the target device's SCSI inquiry data. The scsi_probe(9F) routine allocates a buffer, fills it in, and attaches it to this field.

sd_sense is a pointer to a buffer to contain SCSI request sense data from the device. The target driver must allocate and manage this buffer itself; see "attach(9E)".

sd_private is a pointer field for use by the target driver. It is commonly used to store a pointer to a private target driver state structure.

scsi_pkt Structure

This structure contains the following fields:

		struct scsi_address		pkt_address;
 	opaque_t						pkt_private;
 	void							(*pkt_comp)(struct scsi_pkt *pkt);
 	u_int							pkt_flags;
 	int							pkt_time;
 	u_char						*pkt_scbp;
 	u_char						*pkt_cdbp;
 	ssize_t						pkt_resid;
 	u_int							pkt_state;
 	u_int							pkt_statistics;
 	u_char						pkt_reason;

pkt_address is the target device's address set by scsi_init_pkt(9F)

pkt_private is a place to store private data for the target driver. It is commonly used to save the buf(9S) pointer for the command.

pkt_comp is the address of the completion routine. The host bus adapter driver calls this routine when it has transported the command. This does not mean that the command succeeded; the target might have been busy or might not have responded before the time-out time elapsed (see the description for pkt_time field). The target driver must supply a valid value in this field, though it can be NULL if the driver does not want to be notified.


Note -

There are two different SCSI callback routines. The pkt_comp field identifies a completion callback routine, which is called when the host bus adapter completes its processing. There is also a resource callback routine, called when currently unavailable resources are likely to be available (as in scsi_init_pkt(9F)).


pkt_flags provides additional control information, for example, to transport the command without disconnect privileges (FLAG_NODISCON) or to disable callbacks (FLAG_NOINTR). See scsi_pkt(9S) for details.

pkt_time is a time-out value (in seconds). If the command is not completed within this time, the host bus adapter calls the completion routine with pkt_reason set to CMD_TIMEOUT. The target driver should set this field to longer than the maximum time the command might take. If the timeout is zero, no timeout is requested. Timeout starts when the command is transmitted on the SCSI bus.

pkt_scbp is a pointer to the SCSI status completion block; this is filled in by the host bus adapter driver.

pkt_cdbp is a pointer to the SCSI command descriptor block, the actual command to be sent to the target device. The host bus adapter driver does not interpret this field. The target driver must fill it in with a command that the target device can process.

pkt_resid is the residual of the operation. When allocating DMA resources for a command scsi_init_pkt(9F), pkt_resid indicates the number of bytes for which DMA resources could not be allocated because of DMA hardware scatter-gather or other device limitations. After command transport, pkt_resid indicates the number of data bytes not transferred; this is filled in by the host bus adapter driver before the completion routine is called.

pkt_state indicates the state of the command. The host bus adapter driver fills in this field as the command progresses. One bit is set in this field for each of the five following command states:

pkt_statistics contains transport-related statistics set by the host bus adapter driver.

pkt_reason gives the reason the completion routine was called. The main function of the completion routine is to decode this field and take the appropriate action. If the command completed--in other words, if there were no transport errors--this field is set to CMD_CMPLT; other values in this field indicate an error. After a command is completed, the target driver should examine the pkt_scbp field for a check condition status. See scsi_pkt(9S) for more information.

SCSI Additions to the State Structure

This section adds the following fields to the state structure. See "Software State Structure" for more information.

	
struct scsi_pkt					*rqs;			/* Request Sense packet */
struct buf							*rqsbuf;		/* buf for Request Sense */
struct scsi_pkt					*pkt;			/* packet for current command */
struct scsi_device				*sdp;			/* pointer to device's */
 														/* scsi_device(9S) structure. */

rqs is a pointer to a SCSI request sense command scsi_pkt(9S) structure, allocated in the attach(9E) routine. This packet is preallocated because the request sense command is small and may be used in time-critical areas of the driver (such as when handling errors).

Autoconfiguration

SCSI target drivers must implement the standard autoconfiguration routines _init(9E), _fini(9E), and _info(9E). See Chapter 5, Autoconfiguration, for more information.

probe(9E), attach(9E), and getinfo(9E) are also required, but they must perform SCSI (and SCSA) specific processing.

probe(9E)

SCSI target devices are not self-identifying, so target drivers must have a probe(9E) routine. This routine must determine whether the expected type of device is present and responding.

The general structure and return codes of the probe(9E) routine are the same as those of other device drivers. SCSI target drivers must use the scsi_probe(9F) routine in their probe(9E) entry point. scsi_probe(9F) sends a SCSI inquiry command to the device and returns a code indicating the result. If the SCSI inquiry command is successful, scsi_probe(9F) allocates a scsi_inquiry(9S) structure and fills it in with the device's inquiry data. Upon return from scsi_probe(9F), the sd_inq field of the scsi_device(9S) structure points to this scsi_inquiry(9S) structure.

Because probe(9E) must be stateless, the target driver must call scsi_unprobe(9F) before probe(9E) returns, even if scsi_probe(9F) fails.

Example 13-1 shows a typical probe(9E) routine. It retrieves its scsi_device(9S) structure from the private field of its dev_info structure. It also retrieves the device's SCSI target and logical unit numbers so that it can print them in messages. The probe(9E) routine then calls scsi_probe(9F) to verify that the expected device (a printer in this case) is present.

If scsi_probe(9F) succeeds, it has attached the device's SCSI inquiry data in a scsi_inquiry(9S) structure to the sd_inq field of the scsi_device(9S) structure. The driver can then determine if the device type is a printer (reported in the inq_dtype field). If it is, the type is reported with scsi_log(9F), using scsi_dname(9F) to convert the device type into a string.


Example 13-1 SCSI Target Driver probe(9E) Routine

static int
xxprobe(dev_info_t *dip)
{
	struct scsi_device *sdp;
	int rval, target, lun;
	/*
	 * Get a pointer to the scsi_device(9S) structure
	 */
	sdp = (struct scsi_device *)ddi_get_driver_private(dip);

	target = sdp->sd_address.a_target;
	lun = sdp->sd_address.a_lun;
	/*
	 * Call scsi_probe(9F) to send the Inquiry command. It will
	 * fill in the sd_inq field of the scsi_device structure.
	 */
	switch (scsi_probe(sdp, NULL_FUNC)) {
	case SCSIPROBE_FAILURE:
	case SCSIPROBE_NORESP:
	case SCSIPROBE_NOMEM:
	   	/*
		    * In these cases, device may be powered off,
		    * in which case we may be able to successfully
		    * probe it at some future time - referred to
		    * as `deferred attach'.
		    */
	    	rval = DDI_PROBE_PARTIAL;
	    	break;
	case SCSIPROBE_NONCCS:
	default:
	    	/*
		     * Device isn't of the type we can deal with,
		     * and/or it will never be usable.
		     */
	    	rval = DDI_PROBE_FAILURE;
	    	break;
	case SCSIPROBE_EXISTS:
	    	/*
		     * There is a device at the target/lun address. Check
		     * inq_dtype to make sure that it is the right device
		     * type. See scsi_inquiry(9S)for possible device types.
		     */
	    	switch (sdp->sd_inq->inq_dtype) {
		    case DTYPE_PRINTER:
			    scsi_log(sdp, "xx", SCSI_DEBUG,
				   "found %s device at target%d, lun%d\n",
				    scsi_dname((int)sdp->sd_inq->inq_dtype),
				    target, lun);
			    rval = DDI_PROBE_SUCCESS;
			    break;
	    	case DTYPE_NOTPRESENT:
	    	default:
			    rval = DDI_PROBE_FAILURE;
			    break; 	
	    	}	
	}
	scsi_unprobe(sdp);
	return (rval);
}

A more thorough probe(9E) routine could also check other fields of the scsi_inquiry(9S) structure as necessary to make sure that the device is of the type expected by a particular driver.

attach(9E)

After the probe(9E) routine has verified that the expected device is present, attach(9E) is called. This routine allocates and initializes any per-instance data, creates minor device node information, and restores the hardware state of a device when the device or the system has been suspended. See "attach()" for details of this. In addition to these steps, a SCSI target driver again calls scsi_probe(9F) to retrieve the device's inquiry data and also creates a SCSI request sense packet. If the attach is successful, the attach function should not call scsi_unprobe(9F).

Three routines are used to create the request sense packet: scsi_alloc_consistent_buf(9F), scsi_init_pkt(9F), and scsi_setup_cdb(9F). scsi_alloc_consistent_buf(9F) allocates a buffer suitable for consistent DMA and returns a pointer to a buf(9S) structure. The advantage of a consistent buffer is that no explicit synchronization of the data is required. In other words, the target driver can access the data after the callback. The sd_sense element of the device's scsi_device(9S) structure must be initialized with the address of the sense buffer. scsi_init_pkt(9F) creates and partially initializes a scsi_pkt(9S) structure. scsi_setup_cdb(9F) creates a SCSI command descriptor block, in this case creating a SCSI request sense command.

Note that since a SCSI device is not self-identifying and does not have a reg property, the driver must set the pm-hardware-state property to inform the framework that this device needs to be suspended and resumed.

Example 13-2 shows the SCSI target driver's attach(9E) routine.


Example 13-2 SCSI Target Driver attach(9E) Routine

static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	struct xxstate					*xsp;
	struct scsi_pkt					*rqpkt = NULL;
	struct scsi_device					*sdp;
	struct buf					*bp = NULL;
	int		instance;
	instance = ddi_get_instance(dip);
	switch (cmd) {
		   case DDI_ATTACH:
			  break;
	   	case DDI_PM_RESUME:
			  For information, see Chapter 8, Power Management	   	case DDI_RESUME:
			  For information, see Chapter 8, Power Management	   	default:
			  return (DDI_FAILURE);
	}
	allocate a state structure and initialize it
	...
	xsp = ddi_get_soft_state(statep, instance);
	sdp = (struct scsi_device *)ddi_get_driver_private(dip);
	/*
	 * Cross-link the state and scsi_device(9S) structures.
	 */
	sdp->sd_private = (caddr_t)xsp;
	xsp->sdp = sdp;
	call scsi_probe(9F) again to get and validate inquiry data
	/*
	 * Allocate a request sense buffer. The buf(9S) structure
	 * is set to NULL to tell the routine to allocate a new
	 * one. The callback function is set to NULL_FUNC to tell
	 * the routine to return failure immediately if no
	 * resources are available.
	 */
	bp = scsi_alloc_consistent_buf(&sdp->sd_address, NULL,
		SENSE_LENGTH, B_READ, NULL_FUNC, NULL);
	if (bp == NULL)
	    	goto failed;
	/*
	 * Create a Request Sense scsi_pkt(9S) structure.
	 */
	rqpkt = scsi_init_pkt(&sdp->sd_address, NULL, bp,
		CDB_GROUP0, 1, 0, PKT_CONSISTENT, NULL_FUNC, NULL);
	if (rqpkt == NULL)
	    	goto failed;
	/*
	 * scsi_alloc_consistent_buf(9F) returned a buf(9S)
structure.
	 * The actual buffer address is in b_un.b_addr.
	 */
	sdp->sd_sense = (struct scsi_extended_sense *)bp-
>b_un.b_addr;
	/*
	 * Create a Group0 CDB for the Request Sense command
	 */
	if (scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
	    	SCMD_REQUEST_SENSE, 0, SENSE__LENGTH, 0) == 0)
	     	goto failed;;
	/*
	 * Fill in the rest of the scsi_pkt structure.
	 * xxcallback() is the private command completion routine.
	 */
	rqpkt->pkt_comp = xxcallback;
	rqpkt->pkt_time = 30; /* 30 second command timeout */
	rqpkt->pkt_flags |= FLAG_SENSING;
	xsp->rqs = rqpkt;
	xsp->rqsbuf = bp;
	create minor nodes, report device, and do any other initialization
	/*
	 * Since the device does not have the 'reg' property,
	 * cpr will not call its DDI_SUSPEND/DDI_RESUME entries.
	 * The following code is to tell cpr that this device
	 * needs to be suspended and resumed.
	 */
    (void) ddi_prop_create(device, dip, DDI_PROP_CANSLEEP,
	 	"pm-hardware-state", (caddr_t)"needs-suspend-resume",
		strlen("needs-suspend-resume") + 1);
	xsp->open = 0;
	return (DDI_SUCCESS);
failed:
	if (bp)
	    	scsi_free_consistent_buf(bp);
	if (rqpkt)
	    	scsi_destroy_pkt(rqpkt);
	sdp->sd_private = (caddr_t)NULL;
	sdp->sd_sense = NULL;
	scsi_unprobe(sdp);
    free any other resources, such as the state structure
	return (DDI_FAILURE);
}

detach(9E)

The detach(9E) entry point is the inverse of attach(9E); it must free all resources that were allocated in attach(9E). If successful, the detach should call scsi_unprobe(9F). Example 13-3 shows a target driver detach(9E) routine.


Example 13-3 SCSI Target Driver detach(9E) Routine

static int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	struct xxstate *xsp;
	switch (cmd) {
	case DDI_DETACH:
	  	normal detach(9E) operations, such as getting a 
		pointer to the state structure
	  	...
	  	scsi_free_consistent_buf(xsp->rqsbuf);
	  	scsi_destroy_pkt(xsp->rqs);
	  	xsp->sdp->sd_private = (caddr_t)NULL;
	  	xsp->sdp->sd_sense = NULL;
	  	scsi_unprobe(xsp->sdp);
	   	remove minor nodes
	   	free resources, such as the state structure and properties
	   	remove power managed components
 	  	return (DDI_SUCCESS);
	case DDI_SUSPEND:
	  	For information, see Chapter 8, Power Management	case DDI_PM_SUSPEND:
		For information, see Chapter 8, Power Management	default:
		return (DDI_FAILURE);
	}
}

getinfo(9E)

The getinfo(9E) routine for SCSI target drivers is much the same as for other drivers; see "getinfo()" for more information on DDI_INFO_DEVT2INSTANCE case. However, in the DDI_INFO_DEVT2DEVINFO case of the getinfo(9E) routine, the target driver must return a pointer to its dev_info node. This pointer can be saved in the driver state structure or can be retrieved from the sd_dev field of the scsi_device(9S) structure. Example 13-4shows an alternative SCSI target driver getinfo(9E) code fragment.


Example 13-4 Alternative SCSI Target Driver getinfo(9E) Code Fragment

...
case DDI_INFO_DEVT2DEVINFO:
		dev = (dev_t)arg;
		instance = getminor(dev);
		xsp = ddi_get_soft_state(statep, instance);
		if (xsp == NULL)
			return (DDI_FAILURE);
		*result = (void *)xsp->sdp->sd_dev;
		return (DDI_SUCCESS);
...

Resource Allocation

To send a SCSI command to the device, the target driver must create and initialize a scsi_pkt(9S) structure and pass it to the host bus adapter driver.

scsi_init_pkt(9F)

The scsi_init_pkt(9F) routine allocates and zeros a scsi_pkt(9S) structure; it also sets pointers to pkt_private, *pkt_scbp, and *pkt_cdbp. Additionally, it provides a callback mechanism to handle the case where resources are not available. This structure contains the following fields:

	struct scsi_pkt *scsi_init_pkt(struct scsi_address *ap,
 		struct scsi_pkt *pktp, struct buf *bp, int cmdlen,
 		int statuslen, int privatelen, int flags,
 		int (*callback)(caddr_t), caddr_t arg)

ap is a pointer to a scsi_address structure. This is the sd_address field of the device's scsi_device(9S) structure.

pktp is a pointer to the scsi_pkt(9S) structure to be initialized. If this is set to NULL, a new packet is allocated.

bp is a pointer to a buf(9S) structure. If this is non-NULL and contains a valid byte count, DMA resources are allocated.

cmdlen is the length of the SCSI command descriptor block in bytes.

statuslen is the required length of the SCSI status completion block in bytes.

privatelen is the number of bytes to allocate for the pkt_private field. To store a pointer, specify the size of the pointer here (such as sizeof(struct xxstate *) when storing a pointer to the state structure).

flags is a set of flags. Possible bits include:

callback specifies the action to take if resources are not available. If set to NULL_FUNC, scsi_init_pkt(9F) returns immediately (returning NULL). If set to SLEEP_FUNC, it does not return until resources are available. Any other valid kernel address is interpreted as the address of a function to be called when resources are likely to be available.

arg is the parameter to pass to the callback function.

The scsi_init_pkt(9F) routine synchronizes the data prior to transport. If the driver needs to access the data after transport, the scsi_sync_pkt(9F) routine can be used to synchronize any cached data.

The scsi_destroy_pkt(9F) routine synchronizes any remaining cached data associated with the packet, if necessary, and then frees the packet and associated command, status, and target driver-private data areas. This routine should be called in the command completion routine.

If the target driver needs to resubmit the packet after changing the data, scsi_sync_pkt(9F) must be called before calling scsi_transport(9F). However, if the target driver does not need to access the data, there is no need to call scsi_sync_pkt(9F) after the transport.

scsi_alloc_consistent_buf(9F)

For most I/O requests, the data buffer passed to the driver entry points is not accessed directly by the driver, it is just passed on to scsi_init_pkt(9F). If a driver sends SCSI commands that operate on buffers the driver examines itself (such as the SCSI request sense command), the buffers should be DMA consistent. The scsi_alloc_consistent_buf(9F) routine allocates a buf(9S) structure and a data buffer suitable for DMA-consistent operations. The HBA will perform any necessary synchronization of the buffer before performing the command completion callback.


Caution - Caution -

scsi_alloc_consistent_buf(9F) uses scarce system resources; it should be used sparingly.


scsi_free_consistent_buf(9F) releases a buf(9S) structure and the associated data buffer allocated with scsi_alloc_consistent_buf(9F). See "attach(9E)" and "detach(9E)"for examples.