JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Writing Device Drivers
search filter icon
search icon

Document Information

Preface

Part I Designing Device Drivers for the Solaris Platform

1.  Overview of Solaris Device Drivers

2.  Solaris Kernel and Device Tree

3.  Multithreading

4.  Properties

5.  Managing Events and Queueing Tasks

6.  Driver Autoconfiguration

7.  Device Access: Programmed I/O

8.  Interrupt Handlers

9.  Direct Memory Access (DMA)

DMA Model

Types of Device DMA

Bus-Master DMA

Third-Party DMA

First-Party DMA

Types of Host Platform DMA

DMA Software Components: Handles, Windows, and Cookies

DMA Operations

Performing Bus-Master DMA Transfers

Performing First-Party DMA Transfers

Performing Third-Party DMA Transfers

DMA Attributes

ddi_dma_attr Structure

SBus Example

ISA Bus Example

Managing DMA Resources

Object Locking

Allocating a DMA Handle

Allocating DMA Resources

Device Register Structure

DMA Callback Example

Determining Maximum Burst Sizes

Allocating Private DMA Buffers

Handling Resource Allocation Failures

Programming the DMA Engine

Freeing the DMA Resources

Freeing the DMA Handle

Canceling DMA Callbacks

Synchronizing Memory Objects

Cache

ddi_dma_sync() Function

DMA Windows

10.  Mapping Device and Kernel Memory

11.  Device Context Management

12.  Power Management

13.  Hardening Solaris Drivers

14.  Layered Driver Interface (LDI)

Part II Designing Specific Kinds of Device Drivers

15.  Drivers for Character Devices

16.  Drivers for Block Devices

17.  SCSI Target Drivers

18.  SCSI Host Bus Adapter Drivers

19.  Drivers for Network Devices

20.  USB Drivers

Part III Building a Device Driver

21.  Compiling, Loading, Packaging, and Testing Drivers

22.  Debugging, Testing, and Tuning Device Drivers

23.  Recommended Coding Practices

Part IV Appendixes

A.  Hardware Overview

B.  Summary of Solaris DDI/DKI Services

C.  Making a Device Driver 64-Bit Ready

D.  Console Frame Buffer Drivers

Index

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:

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:

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:

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 */
};