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). The latter 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_laddress;				/* unsigned 64-bit address */
 	uint32_t			dmac_address;				/* unsigned 32-bit address */
 	size_t			dmac_size;					/* transfer size */
 	u_int				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_laddress 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 third field in the cookie may be required by the driver. The driver should not perform any manipulations, such as logical or arithmetic, on the cookie.

For 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 */
 			return (0);
 	}
 	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_buf_bind_handle(9F) may 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.