当主机总线适配器驱动程序完成命令后,将调用包的完成回调例程。然后,驱动程序会将指向 scsi_pkt(9S) 结构的指针作为参数传递。对包进行解码后,完成例程将执行相应的操作。
示例 17–5 显示了一个简单的完成回调例程。该代码检查传输是否失败。如果存在失败情况,该例程将放弃运行,而不会重试命令。如果目标繁忙,则需要额外的代码以便在以后重新提交命令。
如果命令产生检查条件,则目标驱动程序需要发送请求检测命令,除非启用了自动请求检测。
否则,命令成功。在结束命令处理时,命令将销毁包并调用 biodone(9F)。
如果发生传输错误(如总线重置或奇偶校验问题),则目标驱动程序可以使用 scsi_transport(9F) 重新提交该包。重新提交之前,无需更改包中的任何值。
以下示例不会尝试重试未完成的命令。
通常,在中断上下文中会调用目标驱动程序的回调函数。因此,回调函数绝不应处于休眠状态。
static void xxcallback(struct scsi_pkt *pkt) { struct buf *bp; struct xxstate *xsp; minor_t instance; struct scsi_status *ssp; /* * Get a pointer to the buf(9S) structure for the command * and to the per-instance data structure. */ bp = (struct buf *)pkt->pkt_private; instance = getminor(bp->b_edev); xsp = ddi_get_soft_state(statep, instance); /* * Figure out why this callback routine was called */ if (pkt->pkt_reason != CMP_CMPLT) { bp->b_resid = bp->b_bcount; bioerror(bp, EIO); scsi_destroy_pkt(pkt); /* Release resources */ biodone(bp); /* Notify waiting threads */ ; } else { /* * Command completed, check status. * See scsi_status(9S) */ ssp = (struct scsi_status *)pkt->pkt_scbp; if (ssp->sts_busy) { /* error, target busy or reserved */ } else if (ssp->sts_chk) { /* Send a request sense command. */ } else { bp->b_resid = pkt->pkt_resid; /* Packet completed OK */ scsi_destroy_pkt(pkt); biodone(bp); } } }