DMA Slave Transfer¶
DMA slave transfers move data between system memory and a peripheral endpoint such as a UART, SPI controller, I2C controller, or audio interface.
After a DMA channel has been requested and its peripheral-side parameters have been configured with dmaengine_slave_config(), the DMA client prepares individual transactions using DMA-mapped memory.
A typical one-shot slave DMA transaction combines:
Peripheral Configuration
+
DMA-Mapped Memory
+
Transfer Length
+
Transfer Direction
+
Transaction Flags
↓
DMA Transaction Descriptor
The DMA controller driver translates this generic transaction into controller-specific hardware descriptors.
Slave DMA Lifecycle¶
A DMA client normally separates long-lived channel setup from per-transfer operations.
Channel Setup¶
Channel setup is typically performed during driver initialization:
The slave configuration describes the peripheral side of the DMA channel, including:
- Peripheral FIFO or register address.
- Transfer width.
- Burst configuration.
- Other controller-supported slave parameters.
See DMA Slave Configuration for the configuration model.
Per-Transfer Lifecycle¶
A one-shot streaming DMA transaction typically follows:
CPU Buffer
↓
DMA Mapping
↓
DMA Address
↓
Prepare Descriptor
↓
Set Callback
↓
Submit
↓
Issue Pending
↓
DMA Execution
↓
Completion
↓
Unmap
↓
CPU Buffer
The following diagram summarizes the lifecycle and its cleanup boundaries.
DMA Mapping¶
A normal CPU virtual address must not be passed directly to DMA Engine as if it were a DMA address.
For a streaming DMA mapping, the client first maps the buffer with the DMA mapping API.
TX example:
dma_addr_t dma_addr;
dma_addr = dma_map_single(dev, buf, len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr))
return -EIO;
RX example:
dma_addr = dma_map_single(dev, buf, len, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, dma_addr))
return -EIO;
The returned dma_addr_t is the DMA-visible address used when preparing the DMA Engine transaction.
Conceptually:
DMA mapping is a separate operation from DMA transaction preparation.
Two Different DMA Directions¶
Slave DMA involves two direction abstractions that must not be confused.
| Transfer | DMA Mapping API | DMA Engine API |
|---|---|---|
| TX | DMA_TO_DEVICE |
DMA_MEM_TO_DEV |
| RX | DMA_FROM_DEVICE |
DMA_DEV_TO_MEM |
DMA Mapping Direction¶
The DMA mapping API uses:
These values describe how the device accesses system memory.
For example:
This information is used by the DMA mapping layer for DMA address translation, ownership, and architecture-specific cache handling.
DMA Engine Direction¶
DMA Engine uses:
These values describe the topology of the DMA transaction.
For TX:
For RX:
The two enum families describe related operations at different abstraction layers.
Preparing a Single-Buffer Transfer¶
For a DMA-mapped contiguous DMA address range, a client can use:
A typical TX preparation is:
desc = dmaengine_prep_slave_single(chan,
dma_addr,
len,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
if (!desc) {
/* Handle preparation failure. */
}
The important parameters are:
| Parameter | Purpose |
|---|---|
chan |
DMA channel used for the transaction |
dma_addr |
DMA-mapped memory address |
len |
Transfer length |
direction |
DMA_MEM_TO_DEV or DMA_DEV_TO_MEM |
flags |
Descriptor preparation flags |
The memory address must already be suitable for DMA access.
dmaengine_prep_slave_single() does not perform dma_map_single() on behalf of the client.
Single Is a Client-Side Representation¶
dmaengine_prep_slave_single() is implemented as a convenience helper around the slave scatter-gather provider interface.
Conceptually:
Therefore, single means:
The DMA client describes the memory side as one DMA address range.
It does not mean:
- One physical page.
- One hardware transfer block.
- One controller descriptor.
- One hardware LLI.
The DMA controller driver may split the transaction further according to hardware limitations.
For example:
Client
One 64 KiB DMA Range
│
▼
One SG Entry
│
▼
DMA Controller Driver
│
├── HW Descriptor 0
├── HW Descriptor 1
├── HW Descriptor 2
└── HW Descriptor 3
The exact split is controller-specific.
Scatter-Gather Slave Transfer¶
When the memory side consists of multiple DMA segments, the client can use:
Conceptually:
Each mapped SG entry provides DMA-visible address and length information.
The provider consumes information equivalent to:
A software SG entry must not be confused with a hardware descriptor.
A provider may:
- Combine information from the SG list and slave configuration.
- Split an SG entry because of maximum hardware block sizes.
- Generate multiple hardware descriptors.
- Link hardware descriptors using controller-specific mechanisms.
DMA Mapping and SG Segmentation¶
DMA mapping may change how memory is represented to the device.
Conceptually:
The number of original scatterlist entries and the number of DMA-visible segments are not necessarily identical.
DMA controller drivers should consume the mapped DMA representation rather than reconstructing DMA addresses from CPU-side memory information.
vmalloc() and Slave DMA¶
vmalloc() provides virtually contiguous memory but does not guarantee physically contiguous backing pages.
Conceptually:
Virtual Address Space
+------+------+------+------+
| P0 | P1 | P2 | P3 |
+------+------+------+------+
virtually contiguous
The physical pages may be scattered:
Therefore, a vmalloc() range cannot generally be treated as one physically contiguous DMA range.
A suitable driver or subsystem may instead represent the backing pages using scatter-gather DMA:
Whether this is appropriate depends on the subsystem, DMA device, and hardware restrictions.
The important distinction is:
Lack of physical continuity does not by itself imply that DMA is impossible; it changes how the memory must be represented and mapped.
Configuring the Completion Callback¶
After descriptor preparation succeeds, the client can configure completion handling:
The callback is associated with the prepared descriptor, so callback configuration must occur after successful descriptor preparation.
Conceptually:
The callback context must remain valid for as long as the completion path may reference it.
Callback Context Lifetime¶
Asynchronous completion introduces an important lifetime requirement.
The following pattern is unsafe if the function returns before DMA completion:
struct my_request request;
desc->callback_param = &request;
dmaengine_submit(desc);
dma_async_issue_pending(chan);
return 0;
Once the function returns, the stack object no longer has a valid lifetime, while the callback may still execute later.
The required relationship is:
This rule also applies to other resources referenced by asynchronous completion code.
Submitting the Descriptor¶
After callback configuration, the descriptor is submitted:
dma_cookie_t cookie;
int ret;
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret)
/* Handle submission failure. */
Submission transfers the prepared transaction into the DMA Engine/provider lifecycle.
Conceptually:
Submission does not itself mean that hardware execution has started.
Issuing Pending Work¶
The client starts pending DMA work with:
The lifecycle becomes:
The exact transition from issued to active is controller-specific.
After dma_async_issue_pending(), the client must assume that DMA hardware may access the mapped buffer.
This becomes an important resource-lifetime boundary.
DMA Completion vs Peripheral Completion¶
DMA completion describes completion of the DMA transaction.
It does not necessarily mean that the peripheral has completed its own operation.
For example:
The DMA transaction may complete after the final memory data has been delivered to the peripheral FIFO while the peripheral is still transmitting data.
Therefore:
If peripheral completion matters, the peripheral driver must verify the appropriate hardware state.
DMA Engine cannot provide this peripheral-specific guarantee.
One-Shot TX Lifecycle¶
A simple blocking TX transfer can be represented as:
CPU TX Buffer
↓
dma_map_single()
DMA_TO_DEVICE
↓
DMA Address
↓
dmaengine_prep_slave_single()
DMA_MEM_TO_DEV
↓
Set Callback
↓
dmaengine_submit()
↓
dma_async_issue_pending()
↓
DMA Execution
↓
Completion Callback
↓
Wake Waiting Context
↓
dma_unmap_single()
DMA_TO_DEVICE
↓
Buffer Reusable by CPU
The callback can be kept small by using it only to signal a completion object.
One-Shot RX Lifecycle¶
The corresponding RX lifecycle is:
CPU RX Buffer
↓
dma_map_single()
DMA_FROM_DEVICE
↓
DMA Address
↓
dmaengine_prep_slave_single()
DMA_DEV_TO_MEM
↓
Set Callback
↓
dmaengine_submit()
↓
dma_async_issue_pending()
↓
DMA Writes Buffer
↓
Completion Callback
↓
Wake Waiting Context
↓
dma_unmap_single()
DMA_FROM_DEVICE
↓
CPU Reads Received Data
For a one-shot streaming mapping that is unmapped after completion, the CPU can access the RX data after dma_unmap_single() completes.
An additional dma_sync_single_for_cpu() is not required merely to access the buffer after that mapping has been released.
Persistent Mapping and DMA Synchronization¶
Not every DMA mapping is immediately unmapped after one transfer.
A persistent mapping may remain active while CPU and device ownership alternates.
In that case, DMA synchronization APIs may be required:
Device Owns Buffer
↓
dma_sync_*_for_cpu()
↓
CPU Owns / Accesses Buffer
↓
dma_sync_*_for_device()
↓
Device Owns Buffer Again
These synchronization operations address DMA memory visibility and ownership.
They do not stop or pause the DMA controller.
This distinction is especially important for cyclic DMA.
Cyclic DMA Buffer Access¶
Consider a 4096-byte cyclic buffer divided into periods:
+----------+----------+----------+----------+
| Period 0 | Period 1 | Period 2 | Period 3 |
+----------+----------+----------+----------+
DMA may continuously process:
Calling a DMA synchronization API does not prevent DMA from progressing to another period or eventually wrapping around.
The driver must coordinate which period is safe for CPU processing.
Conceptually:
The CPU must consume completed regions before DMA reuses them.
If DMA catches up with the CPU, the problem is a ring-buffer overrun rather than a cache synchronization problem.
Buffer Ownership¶
DMA mapping ownership and buffer allocation ownership are separate.
Consider:
The caller may own the allocation:
while the transfer function temporarily owns:
Therefore:
does not imply that the same function should:
A cleanup path must release only resources that the current owner is responsible for.
Cleanup Before DMA Issue¶
Suppose DMA mapping succeeds but descriptor preparation fails:
The mapping must be released because it was successfully acquired.
However, no active DMA transaction exists that needs to be stopped.
The same principle applies to other pre-issue failures:
Cleanup should correspond to the resources and lifecycle states that were actually reached.
Cleanup After DMA Issue¶
The situation changes after:
At this point, hardware may already be accessing the mapped buffer.
A timeout or driver-removal path must not immediately unmap or free resources.
A typical safe sequence is:
DMA Issued
↓
Timeout / Cancellation / Removal
↓
Stop Accepting New Work
↓
dmaengine_terminate_sync()
↓
DMA and Completion Activity Resolved
↓
dma_unmap_single()
↓
Release Owned Resources
The key safety question is:
Can DMA hardware or asynchronous completion activity still access this resource?
Cookie state alone is not sufficient to answer this lifetime question.
dmaengine_terminate_sync() as a Lifetime Boundary¶
dmaengine_terminate_sync() is useful when cleanup must establish that DMA activity and associated completion processing have been synchronized before resources are released.
Conceptually:
DMA may still access buffer
│
▼
dmaengine_terminate_sync()
│
▼
No remaining DMA/completion access
│
▼
Unmap / Free
This is especially important for:
- Transfer timeout.
- Driver removal.
- Request cancellation.
- Error recovery after DMA has been issued.
The exact controller-specific termination behavior remains the responsibility of the DMA provider.
Slave Configuration and Transaction Information¶
Slave DMA hardware programming combines two different information sources.
Peripheral-Side Configuration¶
Configured with:
Examples include:
Per-Transaction Information¶
Provided during descriptor preparation:
The provider combines them:
Peripheral Configuration
│
│
├───────────────┐
│ │
▼ ▼
Transaction
Information
│
┌───────────────┘
▼
DMA Controller Driver
↓
Hardware Descriptor / LLI
This is the connection between slave configuration and slave transfer preparation.
Provider-Side Translation¶
A DMA controller driver consumes generic DMA Engine information and translates it into hardware-specific fields.
For a memory-to-device transaction:
For a device-to-memory transaction:
The provider also determines controller-specific details such as:
- Transfer-width encoding.
- Burst encoding.
- Maximum block size.
- Transfer count representation.
- Number of hardware descriptors.
- Hardware descriptor linking.
The DMA client should describe the required transfer rather than construct controller-specific descriptors itself.
DMA Client Implementation Sequence¶
A practical DMA client can be developed using three groups of operations.
Initialization¶
Per Transfer¶
Map Buffer
↓
Prepare Descriptor
↓
Configure Callback
↓
Submit
↓
Issue
↓
Wait / Handle Completion
↓
Unmap
Error and Removal¶
This sequence is more important than memorizing individual API prototypes.
Once the lifecycle and ownership boundaries are understood, the exact API details can be verified while implementing a real driver.
Implementation Checklist¶
When implementing a one-shot DMA slave transfer, verify:
- The correct DMA channel has been requested.
- Peripheral-side slave configuration has already been applied.
- The CPU buffer is mapped for the correct DMA mapping direction.
- The DMA mapping succeeded.
- The DMA address rather than the CPU virtual address is passed to DMA Engine.
- The correct DMA Engine transfer direction is used.
- Descriptor preparation succeeded.
- Callback context remains valid until completion can no longer occur.
dmaengine_submit()is checked withdma_submit_error().- Pending DMA work is explicitly issued.
- The mapping remains valid while DMA may access the buffer.
- Timeout and removal paths terminate and synchronize active DMA before unmapping.
- Streaming mappings are eventually unmapped.
- Buffer allocation ownership is handled independently from DMA mapping ownership.
- Peripheral completion is checked separately when DMA completion alone is insufficient.
Mental Model¶
The complete one-shot slave DMA model is:
Channel Setup
dma_request_chan()
↓
dmaengine_slave_config()
↓
Peripheral Configuration
│
│
└─────────────────────────┐
│
Transaction │
│
CPU Buffer │
↓ │
DMA Mapping │
↓ │
DMA Address │
↓ │
Prepare Descriptor │
│ │
└──────────────┬──────────────┘
▼
DMA Controller Driver
↓
Hardware Descriptor
↓
Submit
↓
Issue
↓
DMA Execution
↓
Completion
↓
Unmap
↓
CPU Buffer
The central rule is:
Keep every DMA resource valid until neither the DMA hardware nor asynchronous completion processing can access it.
Related Topics¶
- Linux DMA Engine Framework
- DMA Controller Driver and DMA Channel
- DMA Descriptor
- DMA Completion and Transaction Status
- DMA Termination and Synchronization
- DMA Cyclic
- DMA Slave Configuration