以下两个接口用于分配 DMA 资源:
ddi_dma_buf_bind_handle(9F)-与 buf(9S) 结构结合使用
ddi_dma_addr_bind_handle(9F)-与虚拟地址结合使用
如果存在驱动程序的 xxstart() 例程,则 DMA 资源通常在 xxstart() 例程中分配。有关 xxstart() 的讨论,请参见异步数据传输(块驱动程序)。这两个接口的语法如下:
int ddi_dma_addr_bind_handle(ddi_dma_handle_t handle, struct as *as, caddr_t addr, size_t len, uint_t flags, int (*callback)(caddr_t), caddr_t arg, ddi_dma_cookie_t *cookiep, uint_t *ccountp); int ddi_dma_buf_bind_handle(ddi_dma_handle_t handle, struct buf *bp, uint_t flags, int (*callback)(caddr_t), caddr_t arg, ddi_dma_cookie_t *cookiep, uint_t *ccountp);
以下参数对于 ddi_dma_addr_bind_handle(9F) 和 ddi_dma_buf_bind_handle(9F) 是通用的:
DMA 句柄和用于分配资源的对象。
表示传送方向和其他特性的标志集。DDI_DMA_READ 表示从设备向内存传送数据。DDI_DMA_WRITE 表示从内存向设备传送数据。有关可用标志的完整讨论,请参见 ddi_dma_addr_bind_handle(9F) 或 ddi_dma_buf_bind_handle(9F) 手册页。
用于处理资源分配故障的回调函数的地址。请参见 ddi_dma_alloc_handle(9F) 手册页。
要传递给回调函数的参数。
指向此对象的第一个 DMA cookie 的指针。
指向此对象的 DMA cookie 数的指针。
对于 ddi_dma_addr_bind_handle(9F),对象通过包含以下参数的地址范围进行描述:
指向地址空间结构的指针。as 的值必须为 NULL。
对象的基本内核地址。
对象长度(以字节为单位)。
对于 ddi_dma_buf_bind_handle(9F),对象通过 bp 所指向的 buf(9S) 结构进行描述。
对于具有 DMA 功能的设备,要使用的寄存器比前面示例中所用寄存器多。
设备寄存器结构中使用以下字段来支持具有 DMA 功能但不支持分散/集中的设备:
uint32_t dma_addr; /* starting address for DMA */ uint32_t dma_size; /* amount of data to transfer */
设备寄存器结构中使用以下字段来支持具有 DMA 功能并支持分散/集中的设备:
struct sglentry { uint32_t dma_addr; uint32_t dma_size; } sglist[SGLLEN]; caddr_t iopb_addr; /* When written, informs the device of the next */ /* command's parameter block address. */ /* When read after an interrupt, contains */ /* the address of the completed command. */
在示例 9–1 中,xxstart() 用作回调函数。特定设备状态结构用作 xxstart() 的参数。xxstart() 函数将尝试启动命令。如果由于资源不可用而无法启动该命令,则会安排以后在资源可用时调用 xxstart()。
由于 xxstart() 用作 DMA 回调,因此 xxstart() 必须遵守以下规则,DMA 回调中将强制执行这些规则:
不能假定资源可用。回调必须尝试再次分配资源。
回调必须向系统指明分配是否成功。如果回调未能分配资源,则应返回 DDI_DMA_CALLBACK_RUNOUT,在此情况下需要以后再次调用 xxstart()。DDI_DMA_CALLBACK_DONE 表示回调成功,因此不需要再进行回调。
static int xxstart(caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; struct device_reg *regp; int flags; mutex_enter(&xsp->mu); if (xsp->busy) { /* transfer in progress */ mutex_exit(&xsp->mu); return (DDI_DMA_CALLBACK_RUNOUT); } xsp->busy = 1; regp = xsp->regp; if ( /* transfer is a read */ ) { flags = DDI_DMA_READ; } else { flags = DDI_DMA_WRITE; } mutex_exit(&xsp->mu); if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp,flags, xxstart, (caddr_t)xsp, &cookie, &ccount) != DDI_DMA_MAPPED) { /* really should check all return values in a switch */ mutex_enter(&xsp->mu); xsp->busy=0; mutex_exit(&xsp->mu); return (DDI_DMA_CALLBACK_RUNOUT); } /* Program the DMA engine. */ return (DDI_DMA_CALLBACK_DONE); }