Day 109 — DMA Client Driver Architecture and One-Shot DMA Integration¶
Today's Goal¶
Understand how a peripheral driver integrates DMA Engine APIs into a complete client-side transfer lifecycle, from long-lived DMA channel setup to per-request mapping, descriptor submission, peripheral start, completion, synchronization, and cleanup.
The main focus is no longer an individual DMA Engine API. Instead, the goal is to connect the DMA concepts learned in the previous days into a complete driver architecture.
The main topics are:
- DMA client driver responsibilities
- Driver lifetime vs transfer lifetime
- Request-based DMA vs streaming DMA
- DMA channel setup during driver initialization
- Per-request DMA mapping and descriptor preparation
- Ordering between
dma_async_issue_pending()and peripheral start ARMEDvsACTIVEDMA state- DMA completion vs peripheral completion
- DMA mapping lifetime vs transaction lifetime
- RX ownership and CPU synchronization
- Error and timeout cleanup ordering
dmaengine_terminate_sync()- One-shot DMA client simulation
The implementation goal is to build a small one-shot RX DMA client simulator that combines the previously developed kernel-memory and DMA-mapping simulators with a simplified DMA transaction state machine.
1. DMA Client Driver Perspective¶
Previous DMA topics examined individual parts of the DMA Engine framework:
DMA Channel
DMA Descriptor
DMA Submission
DMA Completion
DMA Termination
DMA Cyclic
DMA Slave Configuration
DMA Slave Transfer
DMA Scatter-Gather
A real peripheral driver must combine these pieces into one coherent lifecycle.
Conceptually:
Peripheral Driver
│
├── acquire DMA channel
├── configure DMA slave parameters
├── manage transfer buffers
├── map memory for DMA
├── prepare descriptors
├── submit transactions
├── start peripheral hardware
├── handle completion
└── terminate and clean up errors
│
▼
DMA Engine
│
▼
DMA Controller Driver
│
▼
DMA Controller Hardware
The DMA Engine framework manages DMA transactions, but the peripheral client driver remains responsible for coordinating the DMA transaction with the peripheral hardware.
2. Driver Lifetime vs Transfer Lifetime¶
DMA resources do not necessarily have the same lifetime.
A DMA channel may remain allocated for the entire lifetime of the driver:
probe()
│
├── dma_request_chan()
│
├── dmaengine_slave_config()
│
▼
Driver Ready
│
│ many transfers
│
▼
remove()
│
└── dma_release_channel()
Individual DMA mappings and descriptors normally have a shorter transfer-specific lifetime:
Request
│
▼
Obtain Buffer
│
▼
DMA Map
│
▼
Prepare Descriptor
│
▼
Submit / Issue
│
▼
Transfer
│
▼
Completion
│
▼
Unmap
│
▼
Request Done
This distinction is important because:
A client driver must manage each lifetime independently.
3. DMA Client Implementation Sequence¶
The complete request-based DMA sequence can be organized into four phases:
- Driver initialization
- Request setup
- DMA transfer
- Completion or error cleanup
The central implementation sequence is:
probe()
│
▼
Request DMA Channel
│
▼
Configure Slave DMA
│
▼
READY
│
│ request arrives
▼
Choose PIO / DMA
│
▼
Obtain Buffer
│
▼
DMA Map
│
▼
Prepare Descriptor
│
▼
Submit
│
▼
Issue Pending
│
▼
ARMED
│
│ start peripheral
▼
ACTIVE
│
├──────────── normal completion
│
└──────────── error / abort
│
▼
DMA SAFE
│
▼
Sync for CPU
if required
│
▼
Unmap
│
▼
Consume / Deliver
│
▼
Release Buffer
The important rule is that cleanup must follow ownership and synchronization boundaries rather than simply following source-code scope.
4. issue_pending() Does Not Mean the Peripheral Is Running¶
A useful client-side mental model is:
After:
the DMA transaction has been made available to the DMA controller provider.
However, for peripheral slave DMA, actual data movement may still depend on DMA requests generated by the peripheral.
Therefore:
A common client ordering is:
prepare DMA
↓
submit DMA
↓
issue pending
↓
DMA ready / armed
↓
enable peripheral
↓
peripheral produces DMA requests
↓
DMA transfers data
This ordering reduces the risk of the peripheral producing data before the DMA path is ready to accept it.
The exact hardware ordering remains peripheral- and controller-specific, but the client driver should deliberately coordinate the two sides.
5. DMA Completion vs Peripheral Completion¶
DMA completion means the DMA controller has completed the programmed memory transfer.
It does not necessarily mean every internal stage of the peripheral has completed.
For a TX peripheral, data may already have left memory and entered a peripheral FIFO or shift register while the peripheral is still transmitting it externally.
Conceptually:
DMA completion may occur after the memory-to-peripheral DMA transaction finishes while the peripheral still has buffered data to transmit.
Therefore:
The client driver must use the appropriate peripheral status when it needs to know that the physical transaction itself has finished.
This distinction is particularly important for TX teardown, chip-select handling, clock shutdown, or peripheral disable operations.
6. Mapping Lifetime After DMA Completion¶
DMA completion and DMA mapping lifetime are also separate concepts.
For a one-shot transaction:
After the DMA transaction has completed, the DMA controller should no longer access the completed transaction's mapping.
However, the mapping remains valid until the client explicitly synchronizes or unmaps it.
Therefore:
This allows the client to perform the required ownership transition before releasing the mapping.
7. RX Ownership and CPU Visibility¶
For a streaming DMA_FROM_DEVICE mapping, device-side completion does not by itself imply that the CPU-side view is immediately valid on a non-coherent system.
The ownership sequence is conceptually:
CPU owns buffer
↓
DMA map
↓
Device owns DMA-visible buffer
↓
DMA writes received data
↓
DMA completes
↓
sync for CPU
↓
CPU owns valid received data
When a mapping remains persistent, the client may use:
before CPU access and:
before returning ownership to the device.
For a one-shot mapping, the exact synchronization behavior of the DMA API and architecture must be respected when the mapping is released.
The Day109 simulator models this ownership boundary explicitly so that CPU-visible and device-visible data do not become implicitly interchangeable.
8. Request-Based DMA vs Streaming DMA¶
Not every DMA client follows the same transfer lifetime.
A request-based client normally associates a DMA transaction with one logical operation.
Examples include:
- SPI message transfer
- One-shot ADC acquisition
- Storage or communication request
- Individual TX or RX command
A streaming client instead keeps DMA resources active across many data-production periods.
Examples include:
- Continuous ADC sampling
- Audio capture
- Audio playback
- Continuous sensor acquisition
The key distinction is:
| Request-Based DMA | Streaming DMA |
|---|---|
| One logical request owns the transfer | Stream remains active across many data periods |
| Completion normally ends the transaction | Period completion normally means data became available |
| Mapping may follow request lifetime | Mapping is commonly persistent |
| One-shot or SG descriptors are common | Cyclic or block-based DMA is common |
| Cleanup occurs after each request | Cleanup occurs when the stream stops |
The same DMA Engine primitives may participate in both architectures, but the buffer lifetime and completion semantics are different.
9. Streaming DMA and IIO Buffers¶
A continuous acquisition driver may combine DMA with a subsystem such as IIO.
The roles are different:
DMA handles data movement between the peripheral and DMA-accessible memory.
The IIO buffer framework provides the subsystem-level buffering and userspace interface.
These are complementary mechanisms rather than alternatives.
A userspace consumer may be slower than the hardware producer. DMA does not eliminate this producer-consumer problem.
The driver and subsystem therefore require an appropriate buffering strategy:
If the consumer cannot keep up indefinitely, buffering only delays eventual overflow. The driver must define an appropriate overflow or data-loss policy.
10. One-Shot DMA Client Simulator¶
A guided simulator was implemented to connect the DMA client concepts into a runnable lifecycle.
The simulator combines:
The simplified transaction states are:
IDLE
↓
PREPARED
↓
SUBMITTED
↓
ARMED
↓
ACTIVE
├──────────────► COMPLETED
│
└──────────────► TERMINATED
The states deliberately distinguish:
SUBMITTED
transaction accepted by the simulated DMA layer
ARMED
transaction issued and ready for peripheral DMA requests
ACTIVE
peripheral enabled and DMA transfer considered active
COMPLETED
normal DMA completion
TERMINATED
explicit synchronized cancellation
11. Mapping Ownership Rule¶
The simulator tracks whether a DMA transaction may still access its mapping.
The central safety rule is:
After:
the transaction itself no longer owns the mapping.
This does not mean the mapping has already been destroyed.
Conceptually:
This distinction allows the simulator to reject unsafe cleanup ordering.
12. Case 1 — Normal Completion¶
The first test verifies the normal one-shot RX lifecycle.
Initialize Request
↓
Allocate RX Buffer
↓
DMA Map
↓
Prepare
↓
Submit
↓
Issue Pending
↓
ARMED
↓
Start Peripheral
↓
ACTIVE
↓
Simulated Device Write
↓
COMPLETED
↓
Completion Callback
↓
Sync for CPU
↓
Unmap
↓
Verify RX Data
Before the peripheral starts, the simulator reports:
After completion, the callback observes:
The callback therefore receives notification after the transaction state has already transitioned to completion.
The received data is synchronized to the CPU side before verification.
13. Case 2 — Abort While DMA Is Active¶
The second test verifies the error path.
The transaction first reaches:
A simulated peripheral error then triggers:
The simplified terminate_sync() model represents a synchronous teardown point.
After it returns successfully:
The simulator intentionally does not model a separate asynchronous termination state because asynchronous termination and later synchronization were already studied separately.
14. Case 3 — Invalid Cleanup Ordering¶
The third test deliberately attempts an unsafe operation:
The simulator rejects the request:
The correct sequence is then executed:
This test demonstrates that cleanup correctness depends on DMA ownership rather than only on whether the client wants to abandon the request.
15. Cleanup Ordering¶
The normal path is:
DMA completes
↓
transaction becomes safe
↓
sync for CPU if required
↓
unmap
↓
consume data
↓
free buffer
The error path is:
error / timeout
↓
stop peripheral
↓
terminate and synchronize DMA
↓
transaction becomes safe
↓
unmap
↓
free buffer
The critical rule is:
This applies whether the memory came from:
kmalloc()- page allocation
- an SG list
- a subsystem-managed buffer
- another DMA-capable allocation path
16. Important Observations¶
DMA Engine and Peripheral State Are Separate¶
DMA Engine state and peripheral state must be coordinated explicitly.
Completion Does Not Destroy the Mapping¶
Normal completion only ends the DMA transaction.
The client still owns the mapping lifecycle:
Buffer Lifetime Extends Beyond DMA Transaction Lifetime¶
For RX:
The buffer cannot be freed merely because a DMA callback has executed.
Streaming Changes the Lifetime Model¶
Request-based DMA normally ends one transaction at completion.
Streaming DMA instead repeatedly exposes new data while the stream remains active.
Therefore cyclic DMA, persistent mappings, subsystem buffers, and producer-consumer behavior become increasingly important for continuous acquisition.
Summary¶
Day109 connected the previously studied DMA Engine mechanisms into a complete DMA client driver architecture.
The main lifecycle is:
Driver Setup
↓
Request
↓
Buffer
↓
DMA Map
↓
Prepare
↓
Submit
↓
Issue
↓
ARMED
↓
Start Peripheral
↓
ACTIVE
↓
Complete or Terminate
↓
DMA Safe
↓
Sync / Unmap
↓
Consume
↓
Release
The most important mental model is:
DMA channel lifetime
≠
DMA transaction lifetime
≠
DMA mapping lifetime
≠
buffer lifetime
≠
peripheral completion lifetime
A correct DMA client driver coordinates all of these lifetimes and does not release a mapping or buffer until the DMA transaction can no longer access it.
The one-shot DMA simulator verified this model through:
- Normal RX completion
- Abort while DMA is active
- Rejection of unsafe unmapping while DMA is active
Next Plan¶
Continue from the one-shot DMA client architecture toward a more realistic continuous acquisition model.
The next topic should build on:
Persistent DMA Mapping
↓
Cyclic / Repeated DMA
↓
Period or Block Completion
↓
Kernel Buffering
↓
Userspace Consumption
This will connect the DMA Engine lifecycle with continuous data acquisition and buffering architectures such as those used by IIO-style drivers.