Skip to content

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
  • ARMED vs ACTIVE DMA 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:

DMA channel lifetime
DMA mapping lifetime
DMA transaction lifetime
buffer lifetime

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:

  1. Driver initialization
  2. Request setup
  3. DMA transfer
  4. Completion or error cleanup

DMA client implementation sequence

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:

PREPARED
SUBMITTED
ARMED
    │ peripheral starts producing DMA requests
ACTIVE

After:

dma_async_issue_pending(chan);

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:

issue_pending()
peripheral transfer already active

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:

Memory
   │ DMA
Peripheral FIFO
Shift Register
Physical Interface

DMA completion may occur after the memory-to-peripheral DMA transaction finishes while the peripheral still has buffered data to transmit.

Therefore:

DMA completion
peripheral protocol completion

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:

DMA ACTIVE
DMA COMPLETED
mapping still exists
sync / unmap
mapping released

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:

DMA transaction completed
DMA mapping automatically destroyed

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:

dma_sync_single_for_cpu();

before CPU access and:

dma_sync_single_for_device();

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

Request-based DMA vs streaming DMA

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:

Peripheral
DMA
Kernel acquisition buffer
IIO buffering interface
Userspace

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:

Producer rate
      vs
Consumer rate

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:

Kernel Memory Simulator
DMA Mapping Simulator
DMA Client Simulator

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:

PREPARED
SUBMITTED
ARMED
ACTIVE
    └── DMA mapping must not be released

After:

COMPLETED
or
TERMINATED

the transaction itself no longer owns the mapping.

This does not mean the mapping has already been destroyed.

Conceptually:

COMPLETED
    ├── DMA transaction no longer accesses mapping
    └── mapping may still exist

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:

DMA state         : ARMED
Mapping state     : true
Peripheral enabled: false

After completion, the callback observes:

state=COMPLETED

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:

DMA state         : ACTIVE
Mapping state     : true
Peripheral enabled: true

A simulated peripheral error then triggers:

ACTIVE
terminate_sync()
TERMINATED
unmap
free buffer

The simplified terminate_sync() model represents a synchronous teardown point.

After it returns successfully:

DMA transaction must no longer access the mapping

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:

ACTIVE
unmap

The simulator rejects the request:

[DMA] dma may still access the mapping: state=ACTIVE

The correct sequence is then executed:

ACTIVE
terminate_sync()
TERMINATED
unmap
success

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:

Never release memory while DMA may still access it.

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.

DMA ARMED
Peripheral enabled

DMA COMPLETED
Peripheral protocol necessarily finished

Completion Does Not Destroy the Mapping

Normal completion only ends the DMA transaction.

The client still owns the mapping lifecycle:

complete
sync / unmap

Buffer Lifetime Extends Beyond DMA Transaction Lifetime

For RX:

allocate
map
DMA transaction
sync / unmap
CPU consumes data
free

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:

  1. Normal RX completion
  2. Abort while DMA is active
  3. 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.