Skip to content

Day 107 — DMA Slave Transfer and dmaengine_prep_slave_single()

Today's Goal

Understand how a DMA client prepares and executes a one-shot slave DMA transaction after the peripheral-side channel configuration has been completed.

The main focus is the relationship between:

  • DMA buffer mapping
  • dmaengine_prep_slave_single()
  • dmaengine_prep_slave_sg()
  • DMA mapping direction
  • DMA Engine transfer direction
  • Descriptor submission and issue
  • Completion callbacks
  • Buffer lifetime
  • DMA cleanup and termination
  • Slave configuration from Day106

The implementation goal is to begin building a practical sequence for writing DMA client drivers instead of studying DMA Engine APIs only as isolated concepts.


1. One-Shot Slave DMA Lifecycle

A typical one-shot slave DMA transfer has two different lifetimes.

Driver initialization prepares long-lived DMA resources:

probe()
dma_request_chan()
dmaengine_slave_config()
DMA channel ready

Each individual transaction then follows a separate lifecycle:

CPU buffer
DMA mapping
Prepare descriptor
Set callback
Submit
Issue pending
DMA execution
Completion
Unmap

This separates:

Channel lifetime
    ≈ driver/device lifetime

Transaction lifetime
    ≈ individual DMA request

2. DMA Mapping Before Descriptor Preparation

For a simple streaming DMA buffer, the CPU buffer must first be mapped for DMA.

For TX:

dma_addr = dma_map_single(dev, buf, len, DMA_TO_DEVICE);

For RX:

dma_addr = dma_map_single(dev, buf, len, DMA_FROM_DEVICE);

The returned:

dma_addr_t dma_addr;

represents the DMA-visible memory address that should be supplied to the DMA Engine transaction preparation API.

The CPU virtual address must not simply be cast to dma_addr_t.


3. DMA Mapping Direction vs DMA Engine Direction

Two different direction enums participate in a slave DMA transaction.

For TX:

Layer Direction
DMA Mapping API DMA_TO_DEVICE
DMA Engine API DMA_MEM_TO_DEV

For RX:

Layer Direction
DMA Mapping API DMA_FROM_DEVICE
DMA Engine API DMA_DEV_TO_MEM

These enums describe different abstractions.

DMA mapping direction describes how the device accesses memory and is relevant to DMA mapping, cache maintenance, and ownership semantics.

DMA Engine direction describes the transaction topology between memory and the peripheral endpoint.


4. dmaengine_prep_slave_single()

A one-shot single-buffer transaction can be prepared with:

dmaengine_prep_slave_single()

Conceptually:

DMA address
    +
Length
    +
Transfer direction
    +
Flags
DMA transaction descriptor

A typical TX preparation uses:

desc = dmaengine_prep_slave_single(chan,
                                   dma_addr,
                                   len,
                                   DMA_MEM_TO_DEV,
                                   DMA_PREP_INTERRUPT |
                                   DMA_CTRL_ACK);

The DMA address supplied to this API must already be DMA mapped.


5. dmaengine_prep_slave_single() Is an SG Helper

The implementation in:

include/linux/dmaengine.h

shows that dmaengine_prep_slave_single() creates a one-entry scatterlist representation and forwards the request through the provider's slave SG preparation callback.

Conceptually:

dma_addr_t + len
one-entry SG
device_prep_slave_sg()

Therefore, single does not mean:

  • One physical page.
  • One hardware descriptor.
  • One hardware transfer block.

It means that the DMA client describes the memory side using one DMA address range.

The DMA controller driver may still split that range into multiple hardware descriptors.


6. dmaengine_prep_slave_sg()

When memory is represented by multiple DMA segments, a client can use:

dmaengine_prep_slave_sg()

Conceptually:

Mapped SG entries
dmaengine_prep_slave_sg()
DMA controller driver
One or more hardware descriptors

A scatterlist entry is a software memory representation and is not the same object as a DMA controller hardware descriptor or LLI.

The provider may further split an SG entry according to hardware limits.


7. vmalloc() and Scatter-Gather DMA

A vmalloc() buffer is virtually contiguous but its backing physical pages are not guaranteed to be physically contiguous.

Conceptually:

vmalloc() buffer
virtually contiguous range
physically scattered backing pages
SG representation
DMA mapping
DMA-visible segments

Therefore, vmalloc() memory is not simply equivalent to "DMA cannot be used."

A suitable subsystem or driver may represent the backing memory using scatter-gather DMA.

The exact mapping mechanism and hardware restrictions still depend on the subsystem and device.


8. BCM2835 SPI DMA Transfer Path

The following source was studied:

drivers/spi/spi-bcm2835.c

The normal DMA transfer path uses:

bcm2835_spi_transfer_one_dma()
bcm2835_spi_prepare_sg()

The BCM2835 SPI driver uses:

dmaengine_prep_slave_sg()

rather than directly using dmaengine_prep_slave_single() for its normal SPI transfer buffers.

The driver receives DMA-mapped scatter-gather information through the SPI subsystem.

Conceptually:

SPI transfer buffer
SPI Core
        │ DMA mapping / SG preparation
tx_sg / rx_sg
BCM2835 SPI Driver
dmaengine_prep_slave_sg()

This demonstrates that a DMA client driver does not always perform DMA mapping directly.

When a subsystem framework already manages mapping, the hardware driver consumes the mapped representation supplied by that framework.


9. Framework Responsibility Boundary

The source trace demonstrated an important responsibility boundary.

The SPI Core handles generic SPI transfer and DMA mapping concerns.

The BCM2835 SPI controller driver handles controller-specific behavior such as:

  • FIFO behavior.
  • DMA interaction.
  • Transfer alignment restrictions.
  • Completion handling.

The DMA Engine provides the generic DMA transaction abstraction.

The DMA controller driver translates the generic transaction into controller-specific hardware descriptors.

Conceptually:

SPI Core
    │ generic mapping
SPI Controller Driver
    │ peripheral-specific behavior
DMA Engine
    │ generic DMA transaction
DMA Controller Driver
    │ controller-specific programming
DMA Hardware

10. DMA Completion vs Peripheral Completion

A DMA completion does not necessarily mean that the peripheral operation has physically completed.

For example:

Memory
    │ DMA
SPI TX FIFO
    │ peripheral transmission
Wire

DMA completion may indicate that the memory data has reached the SPI FIFO while the SPI controller is still shifting the final data onto the wire.

Therefore, a DMA client driver may need to verify peripheral-specific completion after receiving DMA completion.

This responsibility belongs to the peripheral driver rather than the DMA Engine framework.


11. Synopsys AXI DMA Slave SG Preparation

The provider-side source trace continued into the Synopsys AXI DMA driver.

The provider registers a slave SG preparation callback that eventually reaches:

dw_axi_dma_chan_prep_slave_sg()

The function consumes transaction information such as:

sg_dma_address()
sg_dma_len()
direction
flags

while Day106 established that peripheral-side configuration is stored in:

chan->config

The two sources of information meet during hardware descriptor preparation.

Day106                          Day107

chan->config                   mapped memory
Peripheral address             DMA address
Peripheral width               length
Burst                          direction
      │                            │
      └────────────┬───────────────┘
          DMA Controller Driver
          Hardware Descriptor

12. Hardware Descriptor Construction

The AXI DMA provider may split one SG entry into multiple hardware descriptors according to controller block-size limitations.

Therefore:

1 SG entry
1 hardware descriptor

For DMA_MEM_TO_DEV:

SAR = Memory DMA address
DAR = Peripheral address

Memory     = INC
Peripheral = NOINC

For DMA_DEV_TO_MEM:

SAR = Peripheral address
DAR = Memory DMA address

Peripheral = NOINC
Memory     = INC

This connects the transaction information studied today with the slave configuration studied on Day106.


13. Submit and Issue

After preparing a descriptor, the client configures completion handling:

desc->callback = my_dma_complete;
desc->callback_param = req;

The descriptor is then submitted:

cookie = dmaengine_submit(desc);

ret = dma_submit_error(cookie);
if (ret)
    /* Handle submission failure. */

and issued:

dma_async_issue_pending(chan);

The lifecycle is:

Prepared
Submitted
Issued
Active
Completion

Preparing or constructing hardware descriptors does not itself mean that DMA execution has started.


14. Callback Context Lifetime

DMA completion is asynchronous.

Therefore, callback context must remain valid until the completion path can no longer reference it.

The following pattern is unsafe:

struct my_dma_request req;

desc->callback_param = &req;

dmaengine_submit(desc);
dma_async_issue_pending(chan);

return 0;

if req is a stack object and the function returns before the callback executes.

The general rule is:

Callback context lifetime must cover the entire period in which the callback may execute.

The same lifetime reasoning applies to the DMA buffer and DMA mapping.


15. Buffer Ownership vs DMA Mapping Ownership

Buffer allocation ownership and DMA mapping ownership are separate concepts.

For example:

Caller
    └── owns buffer allocation


DMA transfer function
    └── owns temporary DMA mapping

The code that calls:

dma_map_single()

normally owns the corresponding:

dma_unmap_single()

but that does not automatically mean it owns the allocation and may call:

kfree(buf);

Ownership must be defined independently.


16. Error Path Before DMA Issue

If DMA mapping succeeds but descriptor preparation fails:

Map
Prepare
Unmap

There is no active DMA transaction to terminate.

Similarly, a failed descriptor submission must release resources that remain owned by the client, including the streaming DMA mapping.

The cleanup path should reflect which resources have actually been acquired.


17. Timeout After DMA Issue

Once:

dma_async_issue_pending()

has been called, DMA hardware may already be accessing the mapped buffer.

A timeout path must not immediately unmap or free the transaction resources.

The safe sequence studied today is:

DMA issued
wait_for_completion_timeout()
Timeout
dmaengine_terminate_sync()
DMA / completion activity synchronized
dma_unmap_single()

The important safety question is not the cookie number.

The important question is:

Can DMA hardware or completion activity still access this transaction's resources?


18. Blocking One-Shot TX Implementation

A guided TX implementation used:

dma_map_single(..., DMA_TO_DEVICE)
dmaengine_prep_slave_single(..., DMA_MEM_TO_DEV)
set callback
dmaengine_submit()
dma_async_issue_pending()
wait_for_completion_timeout()
completion
dma_unmap_single(..., DMA_TO_DEVICE)

On timeout:

Timeout
dmaengine_terminate_sync()
dma_unmap_single()

This exercise established the basic implementation sequence without requiring the complete API prototypes to be memorized.


19. Blocking One-Shot RX Implementation

The RX exercise used the corresponding directions:

DMA Mapping API
    =
DMA_FROM_DEVICE

DMA Engine API
    =
DMA_DEV_TO_MEM

The lifecycle is:

RX buffer
dma_map_single(..., DMA_FROM_DEVICE)
dmaengine_prep_slave_single(..., DMA_DEV_TO_MEM)
submit
issue
DMA writes buffer
completion
dma_unmap_single(..., DMA_FROM_DEVICE)
CPU accesses received data

For this one-shot streaming mapping, an additional dma_sync_single_for_cpu() is not required after the mapping has been unmapped.


20. Cyclic DMA and DMA Synchronization

A related question was examined for a persistent cyclic DMA buffer.

For example:

4096-byte cyclic buffer

+----------+----------+----------+----------+
| Period 0 | Period 1 | Period 2 | Period 3 |
+----------+----------+----------+----------+

DMA may continuously move through:

P0 → P1 → P2 → P3 → P0 → ...

In this model, dma_sync_*_for_cpu() does not pause cyclic DMA.

Synchronization addresses memory/cache visibility and ownership. It does not stop the DMA controller.

The CPU must process a completed period before DMA wraps around and reuses that region.

This introduces a producer/consumer problem:

DMA
    =
Producer

CPU
    =
Consumer

If the CPU cannot keep up, the result is a ring-buffer overrun rather than a problem that dma_sync_*() alone can solve.

The exact synchronization strategy depends on whether the driver uses coherent memory, persistent streaming mappings, or subsystem-specific DMA handling.


21. Reusable Diagram

A reusable diagram was created to summarize the one-shot slave DMA lifecycle.

DMA Slave Transfer Lifecycle

The diagram separates the normal transaction flow from cleanup after DMA has already been issued.

It also emphasizes the relationship between DMA mapping, descriptor preparation, completion, termination, and mapping lifetime.


22. Implementation Mental Model

The practical sequence established today can be reduced to a small set of operations.

Driver initialization:

Request
Configure

Per-transfer path:

Map
Prepare
Callback
Submit
Issue
Wait / Completion
Unmap

Error or removal path:

Stop new work
Terminate / Synchronize
Unmap
Release

The goal is not to memorize every DMA Engine prototype.

The important implementation skill is understanding operation order, ownership, and resource lifetime.


Key Takeaways

  1. dmaengine_slave_config() prepares persistent peripheral-side channel configuration, while descriptor preparation describes an individual transaction.
  2. Streaming DMA buffers must be mapped before their DMA addresses are passed to DMA Engine preparation APIs.
  3. DMA_TO_DEVICE / DMA_FROM_DEVICE belong to the DMA mapping API.
  4. DMA_MEM_TO_DEV / DMA_DEV_TO_MEM belong to the DMA Engine transaction API.
  5. dmaengine_prep_slave_single() represents one DMA address range and internally uses the slave SG provider path.
  6. A single client-side DMA range may become multiple controller-specific hardware descriptors.
  7. dmaengine_prep_slave_sg() consumes DMA-mapped SG information.
  8. SG entries and hardware LLIs are different abstraction layers.
  9. A DMA client may delegate buffer mapping to its subsystem framework.
  10. vmalloc() memory may participate in DMA through a suitable SG-based mapping path; physical continuity must not be assumed.
  11. DMA completion and peripheral operation completion are not necessarily the same event.
  12. Callback context and DMA buffer lifetime must extend until asynchronous access has ended.
  13. Buffer allocation ownership and DMA mapping ownership must be tracked independently.
  14. A failure before DMA is issued normally requires resource cleanup but not active-DMA termination.
  15. After DMA is issued, timeout or removal paths must synchronize termination before unmapping or freeing transaction resources.
  16. dma_unmap_single() completes a one-shot streaming mapping before the CPU reuses an RX buffer.
  17. dma_sync_*() is relevant when a mapping remains active while CPU and device access alternate; it does not pause cyclic DMA.
  18. The practical DMA client sequence is Request → Configure → Map → Prepare → Submit → Issue → Complete → Unmap.



Next Plan

Continue from one-shot slave DMA transfers toward the next stage of practical DMA client implementation, with emphasis on reusable transfer handling, synchronization, and preparation for a future hardware-backed DMA lab.