一些设备驱动程序除了执行用户线程和内核请求的传送外,可能还需要为 DMA 传送分配内存。分配专用 DMA 缓冲区的一些示例包括设置用于与设备之间进行通信的共享内存以及分配中间传送缓冲区。使用 ddi_dma_mem_alloc(9F) 可为 DMA 传送分配内存。
int ddi_dma_mem_alloc(ddi_dma_handle_t handle, size_t length, ddi_device_acc_attr_t *accattrp, uint_t flags, int (*waitfp)(caddr_t), caddr_t arg, caddr_t *kaddrp, size_t *real_length, ddi_acc_handle_t *handlep);
其中:
DMA 句柄
所需分配的长度(以字节为单位)
指向设备访问特性结构的指针
数据传送模式标志。可能的值包括 DDI_DMA_CONSISTENT 和 DDI_DMA_STREAMING。
用于处理资源分配故障的回调函数的地址。请参见 ddi_dma_alloc_handle(9F) 手册页。
要传递给回调函数的参数
成功返回时包含已分配存储空间的地址的指针
分配的长度(以字节为单位)
指向数据访问句柄的指针
如果设备以不连续的方式进行访问,则应将 flags 参数设置为 DDI_DMA_CONSISTENT。由于会频繁应用于小型对象,因此使用 ddi_dma_sync(9F) 的同步步骤应尽可能为轻量步骤。这种访问类型通常称为一致访问。一致访问对用于设备与驱动程序之间通信的 I/O 参数块特别有用。
在 x86 平台上,物理上连续的 DMA 内存的分配有以下要求:
ddi_dma_attr(9S) 结构中分散/集中列表 dma_attr_sgllen 的长度必须设置为 1。
请勿指定 DDI_DMA_PARTIAL。DDI_DMA_PARTIAL 表示允许进行部分资源分配。
以下示例说明如何分配 IOPB 内存以及访问此内存必需的 DMA 资源。仍然必须分配 DMA 资源,并且必须将 DDI_DMA_CONSISTENT 标志传递给分配函数。
if (ddi_dma_mem_alloc(xsp->iopb_handle, size, &accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &xsp->iopb_array, &real_length, &xsp->acchandle) != DDI_SUCCESS) { /* error handling */ goto failure; } if (ddi_dma_addr_bind_handle(xsp->iopb_handle, NULL, xsp->iopb_array, real_length, DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_DMA_MAPPED) { /* error handling */ ddi_dma_mem_free(&xsp->acchandle); goto failure; }
对于顺序、单向、块大小和按块对齐的内存传送,flags 参数应设置为 DDI_DMA_STREAMING。这种访问类型通常称为流访问。
在某些情况下,使用 I/O 高速缓存可以加快 I/O 传送。I/O 高速缓存最少传送一个高速缓存行。ddi_dma_mem_alloc(9F) 例程会将 size 舍入为高速缓存行的倍数,以避免数据损坏。
ddi_dma_mem_alloc(9F) 函数将返回已分配的内存对象的实际大小。由于存在填充和对齐要求,实际大小可能会大于所请求的大小。ddi_dma_addr_bind_handle(9F) 函数要求使用实际长度。
使用 ddi_dma_mem_free(9F) 函数可以释放 ddi_dma_mem_alloc(9F) 分配的内存。
驱动程序必须确保缓冲区适当对齐。要求下限 DMA 缓冲区对齐的设备的驱动程序可能需要将数据复制到满足该要求的驱动程序中间缓冲区,然后将该中间缓冲区绑定到 DMA 的 DMA 句柄。使用 ddi_dma_mem_alloc(9F) 可分配驱动程序中间缓冲区。请务必使用 ddi_dma_mem_alloc(9F) 而非 kmem_alloc(9F) 来为要进行访问的设备分配内存。