Writing Device Drivers for Oracle® Solaris 11.2

Exit Print View

Updated: September 2014
 
 

Allocating DMA Resources

Two interfaces allocate DMA resources:

DMA resources are usually allocated in the driver's xxstart() routine, if an xxstart() routine exists. See Asynchronous Data Transfers (Block Drivers) for a discussion of xxstart(). These two interfaces have the following syntax:

int ddi_dma_addr_bind_handle(ddi_dma_handle_t handle,
    struct as *as, caddr_t addr,
    size_t len, uint_t flags, int (*callback)(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 (*callback)(caddr_t), caddr_t arg,
    ddi_dma_cookie_t *cookiep, uint_t *ccountp);

The following arguments are common to both ddi_dma_addr_bind_handle(9F) and ddi_dma_buf_bind_handle(9F):

handle

DMA handle and the object for allocating resources.

flags

Set of flags that indicate 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 the ddi_dma_addr_bind_handle(9F) or ddi_dma_buf_bind_handle(9F) man page for a complete discussion of the available flags.

callback

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

arg

Argument to pass to the callback function.

cookiep

Pointer to the first DMA cookie for this object.

ccountp

Pointer to the number of DMA cookies for this object.

For ddi_dma_addr_bind_handle(9F), the object is described by an address range with the following parameters:

as

Pointer to an address space structure. The value of as must be NULL.

addr

Base kernel address of the object.

len

Length of the object in bytes.

For ddi_dma_buf_bind_handle(9F), the object is described by a buf(9S) structure pointed to by bp.

Device Register Structure

DMA-capable devices require more registers than were used in the previous examples.

The following fields are used in the device register structure to support DMA-capable device with no scatter-gather support:

uint32_t      dma_addr;      /* starting address for DMA */
uint32_t      dma_size;      /* amount of data to transfer */

The following fields are used in the device register structure to support DMA-capable devices with scatter-gather support:

struct sglentry {
    uint32_t    dma_addr;
    uint32_t    dma_size;
} sglist[SGLLEN];

caddr_t       iopb_addr;     /* When written, informs the device of the next */
                             /* command's parameter block address. */
                             /* When read after an interrupt, contains */
                             /* the address of the completed command. */

DMA Callback Example

In Example 9–1, xxstart() is used as the callback function. The per-device state structure is used as the argument to xxstart(). The xxstart() function attempts to start the command. If the command cannot be started because resources are not available, xxstart() is scheduled to be called later when resources are available.

Because xxstart() is used as a DMA callback, xxstart() must adhere to the following rules, which are imposed on DMA callbacks:

  • Resources cannot be assumed to be available. The callback must try to allocate resources again.

  • The callback must indicate to the system whether allocation succeeded. DDI_DMA_CALLBACK_RUNOUT should be returned if the callback fails to allocate resources, in which case xxstart() needs to be called again later. DDI_DMA_CALLBACK_DONE indicates success, so that no further callback is necessary.

Example 9-1  DMA Callback Example
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 (DDI_DMA_CALLBACK_RUNOUT);
    }
    xsp->busy = 1;
    regp = xsp->regp;
    if ( /* transfer is a read */ ) {
        flags = DDI_DMA_READ;
    } else {
        flags = DDI_DMA_WRITE;
    }
    mutex_exit(&xsp->mu);
    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 */
        mutex_enter(&xsp->mu);
        xsp->busy=0;
        mutex_exit(&xsp->mu);
        return (DDI_DMA_CALLBACK_RUNOUT);
    }
    /* Program the DMA engine. */
    return (DDI_DMA_CALLBACK_DONE);
}