Two interfaces allocate DMA resources:
ddi_dma_buf_bind_handle(9F) - Used with buffer structures.
ddi_dma_addr_bind_handle(9F) - Used with virtual addresses.
Table 7-2 lists the appropriate DMA resource allocation interfaces for different classes of DMA objects.
Table 7-2 DMA Resource Allocation Interfaces
Type of Object |
Resource Allocation Interface |
---|---|
Memory allocated within the driver using ddi_dma_mem_alloc(9F) |
ddi_dma_addr_bind_handle(9F) |
Requests from the file system through strategy(9E) |
ddi_dma_buf_bind_handle(9F) |
Memory in user space that has been locked down using physio(9F) |
ddi_dma_buf_bind_handle(9F) |
DMA resources are usually allocated in the driver's xxstart() routine, if one exists. See "Asynchronous Data Transfers" for discussion of 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 (*waitfp)(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 (*waitfp)(caddr_t), caddr_t arg, ddi_dma_cookie_t *cookiep, uint_t *ccountp);
ddi_dma_addr_bind_handle(9F) and ddi_dma_buf_bind_handle(9F) take the following arguments:
handle is a DMA handle and is the object to allocate resources for.
For ddi_dma_addr_bind_handle(9F), the object is described by an address range, where as is a pointer to an address space structure (this must be NULL), addr is the base kernel address of the object, and len is the length of the object in bytes.
For ddi_dma_buf_bind_handle(9F), the object is described by a buf(9S) structure pointer to by bp.
flags is a set of flags indicating the transfer direction and other attributes. DDI_DMA_READ indicates a data transfer from device to memory. DDI_DMA_WRITE indicates a data transfer from memory to device. See ddi_dma_addr_bind_handle(9F) or ddi_dma_buf_bind_handle(9F) for a complete discussion of the allowed flags.
waitfp 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.
cookiep is a pointer to the first DMA cookie for this object.
ccountp is a pointer to the number of DMA cookies for this object.
This section adds the following fields to the state structure. See "Software State Structure" for more information.
struct buf *bp; /* current transfer */ ddi_dma_handle_t handle; struct xxiopb *iopb_array; /* for I/O Parameter Blocks */ ddi_dma_handle_t iopb_handle;
Devices that do DMA have more registers than have been used in previous examples. This section adds the following fields to the device register structure to support DMA-capable device examples.
For DMA engines without scatter-gather support:
uint32_t dma_addr; /* starting address for DMA */ uint32_t dma_size; /* amount of data to transfer */
For DMA engines with scatter-gather support:
struct sglentry { uint32_t dma_addr; uint32_t dma_size; } sglist[SGLLEN];
caddr_t iopb_addr; /* When written informs device of the next */ /* command's parameter block address. */ /* When read after an interrupt,contains */ /* the address of the completed command. */
In Example 7-1, xxstart() is used as the callback function and the per-device state structure is given as its argument. xxstart() attempts to start the command. If the command cannot be started because resources are not available, xxstart() is scheduled to be called sometime later, when resources might be available.
Because xxstart() is used as a DMA callback, it must follow these rules imposed on DMA callbacks:
It must not assume that resources are available (it must try to allocate them again).
It must indicate to the system whether allocation succeed by returning DDI_DMA_CALLBACK_RUNOUT if it fails to allocate resources (and needs to be called again later) or DDI_DMA_CALLBACK_DONE indicating success (so no further callback is necessary).
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 (0); } xsp->busy = 1; mutex_exit(&xsp->mu); regp = xsp->regp; if (transfer is a read) { flags = DDI_DMA_READ; } else { flags = DDI_DMA_WRITE; } 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 */ return (DDI_DMA_CALLBACK_RUNOUT); } ... program the DMA engine ... return (DDI_DMA_CALLBACK_DONE); }