Writing Device Drivers
Previous Next

DMA Operations

The steps in a DMA transfer are similar among the types of DMA. The following sections present methods for performing DMA transfers.


Note - You do not need to ensure that the DMA object is locked in memory in block drivers for buffers that come from the file system. The file system has already locked the data in memory.


Performing Bus-Master DMA Transfers

The driver should perform the following steps for bus-master DMA.

  1. Describe the DMA attributes. This step enables the routines to ensure that the device is able to access the buffer.

  2. Allocate a DMA handle.

  3. Ensure that the DMA object is locked in memory. See the physio(9F) or ddi_umem_lock(9F) man page.

  4. Allocate DMA resources for the object.

  5. Program the DMA engine on the device.

  6. Start the engine.

  7. When the transfer is complete, continue the bus master operation.

  8. Perform any required object synchronizations.

  9. Release the DMA resources.

  10. Free the DMA handle.

Performing First-Party DMA Transfers

The driver should perform the following steps for first-party DMA.

  1. Allocate a DMA channel.

  2. Use ddi_dmae_1stparty(9F) to configure the channel.

  3. Ensure that the DMA object is locked in memory. See the physio(9F) or ddi_umem_lock(9F) man page.

  4. Allocate DMA resources for the object.

  5. Program the DMA engine on the device.

  6. Start the engine.

  7. When the transfer is complete, continue the bus-master operation.

  8. Perform any required object synchronizations.

  9. Release the DMA resources.

  10. Deallocate the DMA channel.

Performing Third-Party DMA Transfers

The driver should perform these steps for third-party DMA.

  1. Allocate a DMA channel.

  2. Retrieve the system's DMA engine attributes with ddi_dmae_getattr(9F).

  3. Lock the DMA object in memory. See the physio(9F) or ddi_umem_lock(9F) man page.

  4. Allocate DMA resources for the object.

  5. Use ddi_dmae_prog(9F) to program the system DMA engine to perform the transfer.

  6. Perform any required object synchronizations.

  7. Use ddi_dmae_stop(9F) to stop the DMA engine.

  8. Release the DMA resources.

  9. Deallocate the DMA channel.

Certain hardware platforms restrict DMA capabilities in a bus-specific way. Drivers should use ddi_slaveonly(9F) to determine whether the device is in a slot in which DMA is possible.

DMA Attributes

DMA attributes describe the attributes and limits of a DMA engine, which include:

  • Limits on addresses that the device can access

  • Maximum transfer count

  • Address alignment restrictions

A device driver must inform the system about any DMA engine limitations through the ddi_dma_attr(9S) structure. This action ensures that DMA resources that are allocated by the system can be accessed by the device's DMA engine. The system can impose additional restrictions on the device attributes, but the system never removes any of the driver-supplied restrictions.

ddi_dma_attr Structure

The DMA attribute structure has the following members:

typedef struct ddi_dma_attr {
    uint_t      dma_attr_version;       /* version number */
    uint64_t    dma_attr_addr_lo;       /* low DMA address range */
    uint64_t    dma_attr_addr_hi;       /* high DMA address range */
    uint64_t    dma_attr_count_max;     /* DMA counter register */
    uint64_t    dma_attr_align;         /* DMA address alignment */
    uint_t      dma_attr_burstsizes;    /* DMA burstsizes */
    uint32_t    dma_attr_minxfer;       /* min effective DMA size */
    uint64_t    dma_attr_maxxfer;       /* max DMA xfer size */
    uint64_t    dma_attr_seg;           /* segment boundary */
    int         dma_attr_sgllen;        /* s/g length */
    uint32_t    dma_attr_granular;      /* granularity of device */
    uint_t      dma_attr_flags;         /* Bus specific DMA flags */
} ddi_dma_attr_t;

where:

dma_attr_version

Version number of the attribute structure. dma_attr_version should be set to DMA_ATTR_V0.

dma_attr_addr_lo

Lowest bus address that the DMA engine can access.

dma_attr_addr_hi

Highest bus address that the DMA engine can access.

dma_attr_count_max

Specifies the maximum transfer count that the DMA engine can handle in one cookie. The limit is expressed as the maximum count minus one. This count is used as a bit mask, so the count must also be one less than a power of two.

dma_attr_align

Specifies alignment requirements when allocating memory from ddi_dma_mem_alloc(9F). An example of an alignment requirement is alignment on a page boundary. The dma_attr_align field is used only when allocating memory. This field is ignored during bind operations. For bind operations, the driver must ensure that the buffer is aligned appropriately.

dma_attr_burstsizes

Specifies the burst sizes that the device supports. A burst size is the amount of data the device can transfer before relinquishing the bus. This member is a binary encoding of burst sizes, which are assumed to be powers of two. For example, if the device is capable of doing 1-byte, 2-byte, 4-byte, and 16-byte bursts, this field should be set to 0x17. The system also uses this field to determine alignment restrictions.

dma_attr_minxfer

Minimum effective transfer size that the device can perform. This size also influences restrictions on alignment and on padding.

dma_attr_maxxfer

Describes the maximum number of bytes that the DMA engine can accommodate in one I/O command. This limitation is only significant if dma_attr_maxxfer is less than (dma_attr_count_max + 1) * dma_attr_sgllen.

dma_attr_seg

Upper bound of the DMA engine's address register. dma_attr_seg is often used where the upper 8 bits of an address register are a latch that contains a segment number. The lower 24 bits are used to address a segment. In this case, dma_attr_seg would be set to 0xFFFFFF, which prevents the system from crossing a 24-bit segment boundary when allocating resources for the object.

dma_attr_sgllen

Specifies the maximum number of entries in the scatter-gather list. dma_attr_sgllen is the number of cookies that the DMA engine can consume in one I/O request to the device. If the DMA engine has no scatter-gather list, this field should be set to 1.

dma_attr_granular

This field gives the granularity in bytes of the DMA transfer ability of the device. An example of how this value is used is to specify the sector size of a mass storage device. When a bind operation requires a partial mapping, this field is used to ensure that the sum of the sizes of the cookies in a DMA window is a whole multiple of granularity. However, if the device does not have a scatter-gather capability, it is impossible for the DDI to ensure the granularity. For this case, the value of the dma_attr_granular field should be 1.

dma_attr_flags

This field can be set to DDI_DMA_FORCE_PHYSICAL, which indicates that the system should return physical rather than virtual I/O addresses if the system supports both. If the system does not support physical DMA, the return value from ddi_dma_alloc_handle(9F) is DDI_DMA_BADATTR. In this case, the driver has to clear DDI_DMA_FORCE_PHYSICAL and retry the operation.

SBus Example

A DMA engine on an SBus in a SPARC machine has the following attributes:

  • Access to addresses ranging from 0xFF000000 to 0xFFFFFFFF only

  • 32-bit DMA counter register

  • Ability to handle byte-aligned transfers

  • Support for 1-byte, 2-byte, and 4-byte burst sizes

  • Minimum effective transfer size of 1 byte

  • 32-bit address register

  • No scatter-gather list

  • Operation on sectors only, for example, a disk

A DMA engine on an SBus in a SPARC machine has the following attribute structure:

static ddi_dma_attr_t attributes = {
    DMA_ATTR_V0,   /* Version number */
    0xFF000000,    /* low address */
    0xFFFFFFFF,    /* high address */
    0xFFFFFFFF,    /* counter register max */
    1,             /* byte alignment */
    0x7,           /* burst sizes: 0x1 | 0x2 | 0x4 */
    0x1,           /* minimum transfer size */
    0xFFFFFFFF,    /* max transfer size */
    0xFFFFFFFF,    /* address register max */
    1,             /* no scatter-gather */
    512,           /* device operates on sectors */
    0,             /* attr flag: set to 0 */
};
ISA Bus Example

A DMA engine on an ISA bus in an x86 machine has the following attributes:

  • Access to the first 16 megabytes of memory only

  • Inability to cross a 1-megabyte boundary in a single DMA transfer

  • 16-bit counter register

  • Ability to handle byte-aligned transfers

  • Support for 1-byte, 2-byte, and 4-byte burst sizes

  • Minimum effective transfer size of 1 byte

  • Ability to hold up to 17 scatter-gather transfers

  • Operation on sectors only, for example, a disk

A DMA engine on an ISA bus in an x86 machine has the following attribute structure:

static ddi_dma_attr_t attributes = {
    DMA_ATTR_V0,   /* Version number */
    0x00000000,    /* low address */
    0x00FFFFFF,    /* high address */
    0xFFFF,        /* counter register max */
    1,             /* byte alignment */
    0x7,           /* burst sizes */
    0x1,           /* minimum transfer size */
    0xFFFFFFFF,    /* max transfer size */
    0x000FFFFF,    /* address register max */
    17,            /* scatter-gather */
    512,           /* device operates on sectors */
    0,             /* attr flag: set to 0 */
};
Previous Next