Part I Designing Device Drivers for the Solaris Platform
1. Overview of Solaris Device Drivers
2. Solaris Kernel and Device Tree
5. Managing Events and Queueing Tasks
7. Device Access: Programmed I/O
DMA Software Components: Handles, Windows, and Cookies
Performing Bus-Master DMA Transfers
Performing First-Party DMA Transfers
Determining Maximum Burst Sizes
Allocating Private DMA Buffers
Handling Resource Allocation Failures
10. Mapping Device and Kernel Memory
14. Layered Driver Interface (LDI)
Part II Designing Specific Kinds of Device Drivers
15. Drivers for Character Devices
18. SCSI Host Bus Adapter Drivers
19. Drivers for Network Devices
Part III Building a Device Driver
21. Compiling, Loading, Packaging, and Testing Drivers
22. Debugging, Testing, and Tuning Device Drivers
23. Recommended Coding Practices
B. Summary of Solaris DDI/DKI Services
C. Making a Device Driver 64-Bit Ready
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.
The driver should perform the following steps for bus-master DMA.
Describe the DMA attributes. This step enables the routines to ensure that the device is able to access the buffer.
Allocate a DMA handle.
Ensure that the DMA object is locked in memory. See the physio(9F) or ddi_umem_lock(9F) man page.
Allocate DMA resources for the object.
Program the DMA engine on the device.
Start the engine.
When the transfer is complete, continue the bus master operation.
Perform any required object synchronizations.
Release the DMA resources.
Free the DMA handle.
The driver should perform the following steps for first-party DMA.
Allocate a DMA channel.
Use ddi_dmae_1stparty(9F) to configure the channel.
Ensure that the DMA object is locked in memory. See the physio(9F) or ddi_umem_lock(9F) man page.
Allocate DMA resources for the object.
Program the DMA engine on the device.
Start the engine.
When the transfer is complete, continue the bus-master operation.
Perform any required object synchronizations.
Release the DMA resources.
Deallocate the DMA channel.
The driver should perform these steps for third-party DMA.
Allocate a DMA channel.
Retrieve the system's DMA engine attributes with ddi_dmae_getattr(9F).
Lock the DMA object in memory. See the physio(9F) or ddi_umem_lock(9F) man page.
Allocate DMA resources for the object.
Use ddi_dmae_prog(9F) to program the system DMA engine to perform the transfer.
Perform any required object synchronizations.
Use ddi_dmae_stop(9F) to stop the DMA engine.
Release the DMA resources.
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 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.
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:
Version number of the attribute structure. dma_attr_version should be set to DMA_ATTR_V0.
Lowest bus address that the DMA engine can access.
Highest bus address that the DMA engine can access.
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.
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.
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.
Minimum effective transfer size that the device can perform. This size also influences restrictions on alignment and on padding.
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.
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.
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.
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.
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.
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 */ };
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 */ };