第 1 部分针对 Oracle Solaris 平台设计设备驱动程序
9. 直接内存访问 (Direct Memory Access, DMA)
14. 分层驱动程序接口 (Layered Driver Interface, LDI)
要向设备发送 SCSI 命令,目标驱动程序必须创建并初始化 scsi_pkt(9S) 结构。然后,必须将该结构传递到主机总线适配器驱动程序。
scsi_init_pkt(9F) 例程分配 scsi_pkt(9S) 结构并将该结构调整归零。scsi_init_pkt() 还设置指向 pkt_private、*pkt_scbp 和 *pkt_cdbp 的指针。此外,scsi_init_pkt() 还提供回调机制来处理资源不可用的情况。该函数的语法如下:
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)
其中:
指向 scsi_address 结构的指针。ap 是设备的 scsi_device(9S) 结构的 sd_address 字段。
指向要初始化的 scsi_pkt(9S) 结构的指针。如果将该指针设置为 NULL,则会分配一个新包。
指向 buf(9S) 结构的指针。如果该指针为具有有效字节计数的非 null 值,则会分配 DMA 资源。
SCSI 命令描述符块的长度(以字节为单位)。
SCSI 状态完成块的必需长度(以字节为单位)。
要为 pkt_private 字段分配的字节数。
标志集:
PKT_CONSISTENT-如果 DMA 缓冲区是使用 scsi_alloc_consistent_buf(9F) 分配的,则必须设置该位。在这种情况下,主机总线适配器驱动程序将保证在执行目标驱动程序的命令完成回调之前正确同步数据传输。
PKT_DMA_PARTIAL-如果驱动程序接受部分 DMA 映射,则可以设置该位。如果设置了该位,scsi_init_pkt(9F) 将分配 DMA 资源并设置 DDI_DMA_PARTIAL 标志。可以返回 scsi_pkt(9S) 结构的 pkt_resid 字段的返回值可以是非零的剩余值。非零值表示 scsi_init_pkt(9F) 无法分配的 DMA 资源字节数。
指定资源不可用时要执行的操作。如果设置为 NULL_FUNC,scsi_init_pkt(9F) 将立即返回值 NULL。如果设置为 SLEEP_FUNC,则在资源可用之前,scsi_init_pkt() 不会返回。当资源可能可用时,会将任何其他有效的内核地址解释为要调用的函数的地址。
要传递给回调函数的参数。
传输之前,scsi_init_pkt() 例程将同步数据。如果驱动程序需要在传输后访问数据,则驱动程序应调用 scsi_sync_pkt(9F) 以刷新任何中间高速缓存。可以使用 scsi_sync_pkt() 例程来同步所有高速缓存的数据。
如果在更改数据之后目标驱动程序需要重新提交包,则必须在调用 scsi_transport(9F) 之前调用 scsi_sync_pkt(9F)。但是,如果目标驱动程序不需要访问数据,则在传输之后不需要调用 scsi_sync_pkt()。
如有必要,scsi_destroy_pkt(9F) 例程将同步与包关联的任何剩余高速缓存数据。然后,该例程会释放包以及关联的命令、状态和目标驱动程序专用的数据区。应在命令完成例程中调用该例程。
对于大多数 I/O 请求,驱动程序不直接访问传递到驱动程序入口点的数据缓冲区。该缓冲区仅传递到 scsi_init_pkt(9F)。如果某个驱动程序发送的 SCSI 命令是针对该驱动程序本身检查的缓冲区,那么这些缓冲区应该支持 DMA。SCSI 请求检测命令就是一个很好的示例。scsi_alloc_consistent_buf(9F) 例程分配 buf(9S) 结构和适用于 DMA 一致操作的数据缓冲区。HBA 首先会执行任何必需的缓冲区同步,然后再执行命令完成回调。
scsi_free_consistent_buf(9F) 释放 buf(9S) 结构和使用 scsi_alloc_consistent_buf(9F) 分配的关联数据缓冲区。有关示例,请参见attach() 入口点(SCSI 目标驱动程序)和detach() 入口点(SCSI 目标驱动程序)。