编写设备驱动程序

分配专用 DMA 缓冲区

一些设备驱动程序除了执行用户线程和内核请求的传送外,可能还需要为 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);

其中:

handle

DMA 句柄

length

所需分配的长度(以字节为单位)

accattrp

指向设备访问特性结构的指针

flags

数据传送模式标志。可能的值包括 DDI_DMA_CONSISTENTDDI_DMA_STREAMING

waitfp

用于处理资源分配故障的回调函数的地址。请参见 ddi_dma_alloc_handle(9F) 手册页。

arg

要传递给回调函数的参数

kaddrp

成功返回时包含已分配存储空间的地址的指针

real_length

分配的长度(以字节为单位)

handlep

指向数据访问句柄的指针

如果设备以不连续的方式进行访问,则应将 flags 参数设置为 DDI_DMA_CONSISTENT。由于会频繁应用于小型对象,因此使用 ddi_dma_sync(9F) 的同步步骤应尽可能为轻量步骤。这种访问类型通常称为一致访问。一致访问对用于设备与驱动程序之间通信的 I/O 参数块特别有用。

在 x86 平台上,物理上连续的 DMA 内存的分配有以下要求:

以下示例说明如何分配 IOPB 内存以及访问此内存必需的 DMA 资源。仍然必须分配 DMA 资源,并且必须将 DDI_DMA_CONSISTENT 标志传递给分配函数。


示例 9–3 使用 ddi_dma_mem_alloc(9F)

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) 来为要进行访问的设备分配内存。