Writing Device Drivers

Allocating Private DMA Buffers

Some device drivers may need to allocate memory for DMA transfers to or from a device, in addition to 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. ddi_dma_mem_alloc(9F) is provided for allocating 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 xfermodes,
 		int (*callback)(void *), void *arg, caddr_t *kaddrp,
 		size_t *real_length, ddi_acc_handle_t *handlep);

handle is a DMA handle.

length is the length in bytes of the desired allocation.

accattrp is a pointer to a device access attribute structure .

xfermodes are data transfer mode flags.

callback is the address of callback function for handling resource allocation failures. See ddi_dma_alloc_handle(9F).

arg is the argument to pass to the callback function.

kaddrp is a pointer (on a successful return) that contains the address of the allocated storage.

real_length is the length in bytes that was allocated.

handlep is a pointer to a data access handle.

xfermodes 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 x86 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 7-6 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 7-6 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;
}

xfermodes 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.