在 probe(9E) 例程验证预期设备是否存在后,将调用 attach(9E)。attach() 执行以下任务:
分配并初始化任何每实例数据。
创建从设备节点信息。
暂停设备或系统后恢复设备的硬件状态。有关详细信息,请参见attach() 入口点。
SCSI 目标驱动程序需要再次调用 scsi_probe(9F),以检索设备的查询数据。该驱动程序还必须创建 SCSI 请求检测包。如果连接成功,则 attach() 函数不应调用 scsi_unprobe(9F)。
以下三个例程可用于创建请求检测包:scsi_alloc_consistent_buf(9F)、scsi_init_pkt(9F) 和 scsi_setup_cdb(9F)。scsi_alloc_consistent_buf(9F) 分配适用于一致 DMA 的缓冲区 。然后,scsi_alloc_consistent_buf() 返回指向 buf(9S) 结构的指针。一致缓冲区的优点在于无需显式同步数据。换句话说,目标驱动程序可以在回调之后访问数据。必须使用检测缓冲区的地址初始化设备的 scsi_device(9S) 结构的 sd_sense 元素。scsi_init_pkt(9F) 创建并部分初始化 scsi_pkt(9S) 结构。scsi_setup_cdb(9F) 创建 SCSI 命令描述符块,此时是通过创建 SCSI 请求检测命令来实现。
请注意,SCSI 设备不是自标识设备,并且没有 reg 属性。因此,驱动程序必须设置 pm-hardware-state 属性。设置 pm-hardware-state 将会通知框架需要暂停该设备然后将其恢复。
以下示例给出了 SCSI 目标驱动程序的 attach() 例程。
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 the "Directory Memory Access (DMA)" */
/* chapter in this book. */
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);
}