Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
8.  Direct Memory Access (DMA) DMA Operations DMA Attributes ISA Bus Example  Previous   Contents   Next 
   
 

Managing DMA Resources

This section describes how to manage DMA resources.

Object Locking

Before allocating the DMA resources for a memory object, the object must be prevented from moving. Otherwise, the system can remove the object from memory while the device is writing to it, causing the data transfer to fail, and possibly corrupting the system. The process of preventing memory objects from moving during a DMA transfer is known as locking down the object.

The following object types do not require explicit locking:

  • Buffers coming from the file system through strategy(9E). These buffers are already locked by the file system.

  • Kernel memory allocated within the device driver, such as that allocated by ddi_dma_mem_alloc(9F).

For other objects (such as buffers from user space), physio(9F) or ddi_umem_lock(9F) must be used to lock down the objects. This is usually performed in the read(9E) or write(9E) routines of a character device driver. See "Data Transfer Methods" for an example.

Allocating a DMA Handle

A DMA handle is an opaque object that is used as a reference to subsequently allocated DMA resources. It is usually allocated in the driver's attach entry point using ddi_dma_alloc_handle(9F). ddi_dma_alloc_handle(9F) takes the device information referred to by dip and the device's DMA attributes described by a ddi_dma_attr(9S) structure as parameters. ddi_dma_alloc_handle(9F) has the following syntax:

int ddi_dma_alloc_handle(dev_info_t *dip,
    ddi_dma_attr_t *attr, int (*callback)(caddr_t),
    caddr_t arg, ddi_dma_handle_t *handlep);
dip

Pointer to the device's dev_info structure

attr

Pointer to a ddi_dma_attr(9S) structure as described in "DMA Attributes"

callback

Address of the callback function for handling resource allocation failures

arg

Argument to be passed to the callback function

handlep

Pointer to a DMA handle to store the returned handle

Allocating DMA Resources

Two interfaces allocate DMA resources:

  • ddi_dma_buf_bind_handle(9F) - Used with buf(9S) structures

  • ddi_dma_addr_bind_handle(9F) - Used with virtual addresses

DMA resources are usually allocated in the driver's xxstart() routine, if one exists. See "Asynchronous Data Transfers (Block Drivers)" for 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 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 the ddi_dma_addr_bind_handle(9F) or ddi_dma_buf_bind_handle(9F) man pages for a complete discussion of the allowed flags.

callback

Address of callback function for handling resource allocation failures. See the ddi_dma_addr_bind_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, 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 pointed to by bp.

Device Register Structure

DMA capable devices 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. */

DMA Callback Example

In Example 8-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 succeeded 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).


Example 8-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);
}

 
 
 
  Previous   Contents   Next