Writing Device Drivers


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:
	   	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,
	if (bp == NULL)
	    	goto failed;
	 * Create a Request Sense scsi_pkt(9S) structure.
	rqpkt = scsi_init_pkt(&sdp->sd_address, NULL, bp,
	if (rqpkt == NULL)
	    	goto failed;
	 * scsi_alloc_consistent_buf(9F) returned a buf(9S)
	 * The actual buffer address is in b_un.b_addr.
	sdp->sd_sense = (struct scsi_extended_sense *)bp-
	 * Create a Group0 CDB for the Request Sense command
	if (scsi_setup_cdb((union scsi_cdb *)rqpkt->pkt_cdbp,
	     	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);
	if (bp)
	if (rqpkt)
	sdp->sd_private = (caddr_t)NULL;
	sdp->sd_sense = NULL;
    free any other resources, such as the state structure
	return (DDI_FAILURE);