Writing Device Drivers

Programming the DMA Engine

When the resources have been successfully allocated, the device must be programmed. Although programming a DMA engine is device specific, all DMA engines require a starting address and a transfer count. Device drivers retrieve these two values from the DMA cookie returned by a successful call from ddi_dma_addr_bind_handle(9F), ddi_dma_buf_bind_handle(9F), or ddi_dma_getwin(9F). These functions all return the first DMA cookie and a cookie count indicating whether the DMA object consists of more than one cookie. If the cookie count N is greater than 1, ddi_dma_nextcookie(9F) has to be called N-1 times to retrieve all the remaining cookies.

A cookie is of type ddi_dma_cookie(9S) and has the following fields:

uint64_t        _dmac_ll;       /* 64-bit DMA address */
uint32_t        _dmac_la[2];    /* 2 x 32-bit address */
size_t          dmac_size;      /* DMA cookie size */
uint_t          dmac_type;      /* bus specific type bits */

The dmac_laddress specifies a 64-bit I/O address appropriate for programming the device's DMA engine. If a device has a 64-bit DMA address register, a driver should use this field to program the DMA engine. The dmac_address field specifies a 32-bit I/O address that should be used for devices that have a 32-bit DMA address register. dmac_size contains the transfer count. Depending on the bus architecture, the dmac_type field in the cookie might be required by the driver. The driver should not perform any manipulations, such as logical or arithmetic, on the cookie.


Example 8-4 ddi_dma_cookie(9S) Example

ddi_dma_cookie_t                    cookie;

     if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp, flags, xxstart,
         (caddr_t)xsp, &cookie, &xsp->ccount) != DDI_DMA_MAPPED) {
             /* error handling */
      }
     sglp = regp->sglist;
     for (cnt = 1; cnt <= SGLLEN; cnt++, sglp++) {
         /* store the cookie parms into the S/G list */
         ddi_put32(xsp->access_hdl, &sglp->dma_size,
             (uint32_t)cookie.dmac_size);
         ddi_put32(xsp->access_hdl, &sglp->dma_addr,
             cookie.dmac_address);
         /* Check for end of cookie list */
         if (cnt == xsp->ccount)
             break;
         /* Get next DMA cookie */
         (void) ddi_dma_nextcookie(xsp->handle, &cookie);
     }
        /* start DMA transfer */
     ddi_put8(xsp->access_hdl, &regp->csr,
         ENABLE_INTERRUPTS | START_TRANSFER);


Note -

ddi_dma_addr_bind_handle(9F) and ddi_dma_buf_bind_handle(9F) can return more DMA cookies than fit into the scatter-gather list. In this case, the driver has to continue the transfer in the interrupt routine and reprogram the scatter-gather list with the remaining DMA cookies. You must handle sgllen cookies at a time.