Linux DMA Mapping APIs¶
Purpose¶
The Linux DMA Mapping API provides a portable interface for preparing CPU memory so that it can be accessed by DMA-capable devices.
Unlike ioremap(), which maps device registers into the CPU address space, the DMA Mapping API exposes CPU memory to hardware devices.
Header¶
Common APIs¶
| API | Purpose |
|---|---|
dma_alloc_coherent() |
Allocate a coherent DMA buffer. |
dma_free_coherent() |
Release a coherent DMA buffer. |
dma_map_single() |
Map an existing CPU buffer for streaming DMA. |
dma_unmap_single() |
Remove a streaming DMA mapping. |
dma_sync_single_for_device() |
Synchronize a mapped buffer before device access. |
dma_sync_single_for_cpu() |
Synchronize a mapped buffer before CPU access. |
dma_mapping_error() |
Check whether a DMA mapping failed. |
dma_alloc_coherent()¶
Purpose¶
Allocates a coherent DMA buffer.
The returned CPU virtual address and DMA address refer to the same underlying memory and remain valid until released.
Prototype¶
Parameters¶
| Parameter | Description |
|---|---|
dev |
Device owning the DMA buffer. |
size |
Allocation size in bytes. |
dma_handle |
Returned DMA address. |
gfp |
Allocation flags. |
Return Value¶
Returns the CPU virtual address on success.
Returns NULL on failure.
Example¶
dma_addr_t dma_addr;
void *cpu_addr;
cpu_addr = dma_alloc_coherent(dev,
PAGE_SIZE,
&dma_addr,
GFP_KERNEL);
if (!cpu_addr)
return -ENOMEM;
/* Use the DMA buffer. */
dma_free_coherent(dev,
PAGE_SIZE,
cpu_addr,
dma_addr);
dma_free_coherent()¶
Purpose¶
Releases a coherent DMA buffer.
Prototype¶
dma_map_single()¶
Purpose¶
Maps an existing CPU buffer for streaming DMA.
A streaming mapping may require explicit ownership synchronization when the CPU and DMA device access the mapped memory at different times.
Prototype¶
dma_addr_t dma_map_single(struct device *dev,
void *cpu_addr,
size_t size,
enum dma_data_direction direction);
Parameters¶
| Parameter | Description |
|---|---|
dev |
DMA-capable device. |
cpu_addr |
CPU virtual address. |
size |
Buffer size. |
direction |
DMA transfer direction. |
Return Value¶
Returns a DMA address on success.
The return value should always be checked with dma_mapping_error().
Example¶
dma_addr_t dma_addr;
dma_addr = dma_map_single(dev,
buffer,
length,
DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr))
return -EIO;
dma_unmap_single()¶
Purpose¶
Ends a streaming DMA mapping.
The mapping must not be removed while the DMA device may still access the mapped memory.
Prototype¶
void dma_unmap_single(struct device *dev,
dma_addr_t dma_addr,
size_t size,
enum dma_data_direction direction);
Important Notes¶
Before unmapping:
- stop or complete DMA activity
- synchronize outstanding DMA operations when required
- ensure the device no longer accesses the mapping
- ensure higher-level buffer ownership rules are satisfied
For a persistent streaming mapping, mapping lifetime may span multiple DMA completion events.
dma_sync_single_for_device()¶
Purpose¶
Synchronizes a streaming DMA mapping before device access.
This API is used when ownership of a mapped region is being returned to the DMA device after CPU access.
Prototype¶
void dma_sync_single_for_device(struct device *dev,
dma_addr_t dma_addr,
size_t size,
enum dma_data_direction direction);
Important Notes¶
The API does not create a new DMA mapping.
It operates on memory that is already mapped for streaming DMA.
A useful ownership model is:
The exact cache-maintenance behavior is architecture-dependent.
dma_sync_single_for_cpu()¶
Purpose¶
Synchronizes a streaming DMA mapping before CPU access.
This API is commonly used after a DMA device has written into a streaming-mapped buffer and the CPU needs to consume the result.
Prototype¶
void dma_sync_single_for_cpu(struct device *dev,
dma_addr_t dma_addr,
size_t size,
enum dma_data_direction direction);
Important Notes¶
A useful ownership model is:
Device owns mapped region
↓
DMA completion
↓
dma_sync_single_for_cpu()
↓
CPU may access mapped region
The mapping itself remains valid after synchronization.
Therefore:
This distinction becomes especially important for persistent streaming DMA buffers.
dma_mapping_error()¶
Purpose¶
Checks whether a DMA mapping operation failed.
Prototype¶
Return Value¶
Returns non-zero if the DMA mapping failed.
Returns zero if the mapping succeeded.
Persistent Streaming Mappings¶
A streaming mapping does not need to correspond to only one DMA transaction.
A client may keep a larger buffer mapped across an entire streaming session.
For example:
Persistent Mapping
┌────────┬────────┬────────┬────────┐
│ Slot 0 │ Slot 1 │ Slot 2 │ Slot 3 │
└────────┴────────┴────────┴────────┘
DMA may repeatedly operate on different regions of the same mapping.
Conceptually:
Map Complete Buffer
↓
DMA Slot 0
↓
CPU Consumes Slot 0
↓
DMA Slot 1
↓
CPU Consumes Slot 1
↓
...
↓
Terminate Stream
↓
Unmap Complete Buffer
This separates:
A client must still obey the DMA Mapping API synchronization requirements for each region it allows the CPU or device to access.
Mapping Granularity and Buffer Ownership¶
A persistent mapping may contain multiple logical buffer regions.
For example:
┌─────────────┬─────────────┬─────────────┬─────────────┐
│ Region 0 │ Region 1 │ Region 2 │ Region 3 │
└─────────────┴─────────────┴─────────────┴─────────────┘
Region 0 → CPU consumption
Region 1 → DMA active
Region 2 → available for later DMA
Region 3 → available for later DMA
The complete mapping can remain valid even though the logical ownership of individual regions changes over time.
This is common in continuous acquisition architectures such as cyclic DMA.
The exact synchronization APIs and allowed ranges must follow the Linux DMA API requirements for the architecture and mapping type being used.
Lab Simulator Extensions¶
The Embedded Linux Learning DMA simulator includes additional helper APIs used by the Day110 continuous DMA lab.
These helpers are not Linux kernel DMA API functions.
They exist only to model partial ownership transitions inside one persistent simulated DMA mapping.
The simulator extensions are:
| Simulator API | Purpose |
|---|---|
dma_sync_single_range_for_cpu() |
Synchronize one subrange of a persistent simulated mapping for CPU access. |
dma_sync_single_range_for_device() |
Synchronize one subrange of a persistent simulated mapping for device access. |
They complement the simulator's existing whole-mapping synchronization APIs.
dma_sync_single_range_for_cpu()¶
Purpose¶
Synchronizes a subrange of an existing simulated streaming DMA mapping for CPU access.
The base DMA address remains the DMA address originally returned when the complete buffer was mapped.
The requested synchronization region is specified using an offset and size.
Simulator Prototype¶
bool dma_sync_single_range_for_cpu(
dma_addr_t dma_addr,
size_t offset,
size_t size,
enum dma_data_direction direction);
Parameters¶
| Parameter | Description |
|---|---|
dma_addr |
Base DMA address of the existing simulated streaming mapping. |
offset |
Byte offset from the beginning of the mapping. |
size |
Number of bytes to synchronize. |
direction |
DMA direction used when the mapping was created. |
Return Value¶
Returns true when the requested range is valid and the simulated synchronization succeeds.
Returns false when:
- the mapping does not exist
- the mapping type is invalid
- the DMA direction does not match
- the offset is outside the mapping
- the requested range exceeds the mapping
Example¶
Assume one 64-byte persistent mapping is divided into four 16-byte slots:
DMA Mapping
offset 0
│
▼
┌────────┬────────┬────────┬────────┐
│ Slot 0 │ Slot 1 │ Slot 2 │ Slot 3 │
│ 16 B │ 16 B │ 16 B │ 16 B │
└────────┴────────┴────────┴────────┘
To synchronize Slot 2:
The simulator copies only that requested device-visible subrange into the simulated CPU-visible view.
dma_sync_single_range_for_device()¶
Purpose¶
Synchronizes a subrange of an existing simulated streaming DMA mapping before that region is returned to device access.
Simulator Prototype¶
bool dma_sync_single_range_for_device(
dma_addr_t dma_addr,
size_t offset,
size_t size,
enum dma_data_direction direction);
Parameters¶
| Parameter | Description |
|---|---|
dma_addr |
Base DMA address of the existing simulated streaming mapping. |
offset |
Byte offset from the beginning of the mapping. |
size |
Number of bytes to synchronize. |
direction |
DMA direction used when the mapping was created. |
Return Value¶
Returns true when the requested range is valid and synchronization succeeds.
Returns false when the mapping or requested range is invalid.
Example¶
The Day110 simulator uses this operation when a CPU-owned slot has been consumed and is being returned to the reusable DMA buffer pool.
Simulator Range Validation¶
The simulator validates a subrange using the equivalent of:
This avoids allowing:
to extend beyond the persistent mapping.
It also avoids integer-overflow-prone range validation.
Simulator Ownership Model¶
The original simulator's whole-buffer synchronization functions model one ownership state for the complete mapping.
Day110 introduces a different requirement.
One persistent mapping may simultaneously contain logical slots in different states:
Therefore the Day110 stream simulator tracks slot ownership separately using its DMA ring metadata.
The range synchronization helpers update simulated CPU/device visibility for the requested range but do not redefine the Linux DMA API itself.
Linux API vs Simulator API¶
Keep the distinction explicit:
| Linux DMA Mapping API | Lab Simulator Extension |
|---|---|
dma_sync_single_for_cpu() |
dma_sync_single_range_for_cpu() |
dma_sync_single_for_device() |
dma_sync_single_range_for_device() |
| Real kernel API | Project-specific simulator helper |
| Uses Linux DMA API semantics | Models per-slot synchronization for the learning simulator |
The range helper names should not be copied into a real Linux driver as if they were kernel APIs.
When implementing a real driver, use the synchronization interfaces and range semantics provided by the Linux DMA Mapping API for the target kernel and architecture.