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() Entry Point (SCSI Target 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 "The attach() Entry Point" for details.) 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 14-2 shows the SCSI target driver's attach() routine.
Example 14-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_RESUME:
For information, see Chapter 9
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_update_string(device, dip,
"pm-hardware-state", "needs-suspend-resume");
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() Entry Point (SCSI Target Drivers)
The detach(9E) entry point is the inverse of attach(9E); it must free all resources that were allocated in attach(). If successful, the detach should call scsi_unprobe(9F). Example 14-3 shows a target driver detach() routine.
Example 14-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
return (DDI_SUCCESS);
case DDI_SUSPEND:
For information, see Chapter 9
default:
return (DDI_FAILURE);
}
}
|