Writing Device Drivers

Allocating Private DMA Buffers

Some device drivers might need to allocate memory for DMA transfers to or from a device, besides doing transfers requested by user threads and the kernel. Examples of this are setting up shared memory for communication with the device and allocating intermediate transfer buffers. Use ddi_dma_mem_alloc(9F) to allocate memory for DMA transfers.

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 handle

length

Length in bytes of the desired allocation

accattrp

Pointer to a device access attribute structure

flags

Data transfer mode flags; possible values are: DDI_DMA_CONSISTENT and DDI_DMA_STREAMING

waitfp

Address of callback function for handling resource allocation failures. See the ddi_dma_mem_alloc(9F) man page

arg

Argument to pass to the callback function

kaddrp

Pointer (on a successful return) that contains the address of the allocated storage

real_length

Length in bytes that was allocated

handlep

Pointer to a data access handle

flags should be set to DDI_DMA_CONSISTENT if the device accesses in a nonsequential fashion, or if synchronization steps using ddi_dma_sync(9F) should be as lightweight as possible (because of frequent use on small objects). This type of access is commonly known as consistent access. I/O parameter blocks that are used for communication between a device and the driver are set up this way.

On the IA platform, to allocate memory for DMA using physically contiguous pages, set the length of the scatter/gather list dma_attr_sgllen in the ddi_dma_attr(9S) structure to 1, and do not specify DDI_DMA_PARTIAL which would otherwise permit partial resource allocation.

Example 8–3 shows how to allocate IOPB memory and the necessary DMA resources to access it. DMA resources must still be allocated, and the DDI_DMA_CONSISTENT flag must be passed to the allocation function.


Example 8–3 Using 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 should be set to DDI_DMA_STREAMING if the device is doing sequential, unidirectional, block-sized and block-aligned transfers to or from memory. This type of access is commonly known as streaming access.

For example, if an I/O transfer can be sped up by using an I/O cache, which at a minimum transfers (flushes) one cache line, ddi_dma_mem_alloc(9F) will round the size to a multiple of the cache line to avoid data corruption.

ddi_dma_mem_alloc(9F) returns the actual size of the allocated memory object. Because of padding and alignment requirements, the actual size might be larger than the requested size. ddi_dma_addr_bind_handle(9F) requires the actual length.

ddi_dma_mem_free(9F) is used to free the memory allocated by ddi_dma_mem_alloc(9F).


Note –

If the memory is not properly aligned, the transfer will succeed but the system will choose a different (and possibly less efficient) transfer mode that requires fewer restrictions. For this reason, ddi_dma_mem_alloc(9F) is preferred over kmem_alloc(9F) when allocating memory for the device to access.