Expand description
§DMA API
dma-api provides typed DMA ownership primitives for drivers. The public
surface separates three different concepts that should not be mixed:
CoherentArray<T>/CoherentBox<T>: CPU and device see the same memory without explicit cache maintenance. Use these for descriptor rings, command queues, completion queues, and controller contexts. Coherency does not provide ordering; drivers still need their normal barriers before doorbells and after completion ownership changes.ContiguousArray<T>/ContiguousBox<T>: owned device-address-contiguous DMA memory using normal CPU mapping. Use these for data buffers and buffer pools. CPU-only accessors are named with a_cpusuffix; ownership transfer is explicit viaprepare_for_device(range)/complete_for_cpu(range)or the higher-level*_for_device/*_from_devicehelpers.StreamingMap<T>: RAII mapping of an existing caller-owned buffer. Use this for one transfer of a buffer not owned bydma-api. Explicit ownership transfer handles cache maintenance and bounce-buffer copy, andDropunmaps.
The *_for_device and *_from_device helpers are convenience ownership
transfer APIs. They wrap the same synchronization operations as
prepare_for_device(range) and complete_for_cpu(range): CPU writes are made
visible before the device runs, and device writes are made visible before CPU
reads. They do not detect device completion, place MMIO doorbells, or provide
hardware ordering barriers. Drivers still decide when a transfer is submitted
and when it has completed.
DmaAddr is the only portable device-visible address type. Backend-private raw
handles are split into DmaAllocHandle for owned allocations and
DmaMapHandle for streaming mappings.
§Constraints
Every allocation and mapping is checked against DmaConstraints:
pub struct DmaConstraints {
pub addr_mask: u64,
pub align: usize,
pub boundary: Option<usize>,
pub max_segment_size: Option<usize>,
}DmaDeviceInfo keeps a device’s address domain, cache coherency, and constraints
together. Construct DeviceDma from that complete description and the platform
operation implementation:
let info = DmaDeviceInfo::new(
DmaDomainId::Direct,
coherency,
DmaConstraints::new(dma_mask),
);
let dma = DeviceDma::new(info, op);Use DmaDomainId::Direct only when device addresses are physical addresses.
Use DmaDomainId::Translated(id) only with an ID supplied by a real IOMMU
domain. coherency is a required device property obtained from firmware or the
bus; it is separate from address and layout constraints. Use with_constraints
when a specific queue or transfer has stronger requirements.
§Migrating from 0.9
This release intentionally replaces the legacy global-domain API instead of keeping a second compatibility path:
- Replace
DmaDomainId::legacy_global()andDmaDomainId::from_raw(0)withDmaDomainId::Directonly for devices whose DMA addresses are physical addresses. - Replace a nonzero raw domain value with
DmaDomainId::Translated(id)only whenidcomes from a real IOMMU domain already attached to the device. - Build one
DmaDeviceInfofrom the device domain, coherency property, and constraints, then callDeviceDma::new(info, op). - Replace public
sync_for_device/sync_for_cpucalls withprepare_for_device(range)/complete_for_cpu(range)ownership transitions.
There is no automatic translation from an arbitrary legacy domain number: a direct device and an IOMMU-translated device have different address semantics, so callers must select the case supported by their platform.
Backends must never hand a driver a DMA address outside the requested mask. For
example, a device whose constraints use u32::MAX as u64 must only return
32-bit reachable DMA addresses. Streaming mappings may use a fast path when the
original buffer already satisfies the constraints; otherwise they should
allocate an in-mask bounce buffer.
For DmaCoherency::Coherent, coherent allocations retain the normal contiguous
CPU mapping and explicit cache synchronization is skipped. For
DmaCoherency::NonCoherent, the backend creates and later removes the coherent
CPU mapping. Bounce-buffer copies occur for either property; only non-coherent
devices perform cache maintenance around those copies.
§Backend Contract
Implement DmaOp once for the platform:
use core::{alloc::Layout, num::NonZeroUsize, ptr::NonNull};
use dma_api::{
DmaAllocHandle, DmaCoherency, DmaConstraints, DmaDirection, DmaError, DmaMapHandle, DmaOp,
};
struct MyDma;
impl DmaOp for MyDma {
fn page_size(&self) -> usize {
4096
}
unsafe fn alloc_contiguous(
&self,
constraints: DmaConstraints,
layout: Layout,
) -> Option<DmaAllocHandle> {
todo!("allocate normal mapped, device-visible contiguous DMA memory")
}
unsafe fn dealloc_contiguous(&self, handle: DmaAllocHandle) {
todo!("free alloc_contiguous memory")
}
unsafe fn alloc_coherent(
&self,
constraints: DmaConstraints,
layout: Layout,
) -> Option<DmaAllocHandle> {
todo!("create the coherent CPU mapping required by a non-coherent device")
}
unsafe fn dealloc_coherent(&self, handle: DmaAllocHandle) -> Result<(), DmaError> {
todo!("restore mapping policy and free alloc_coherent memory")
}
unsafe fn map_streaming(
&self,
constraints: DmaConstraints,
addr: NonNull<u8>,
size: NonZeroUsize,
direction: DmaDirection,
) -> Result<DmaMapHandle, DmaError> {
todo!("map the existing buffer or create an in-constraint bounce buffer")
}
unsafe fn unmap_streaming(&self, handle: DmaMapHandle) {
todo!("unmap and release any bounce allocation")
}
}The default streaming sync methods accept the device coherency property, perform cache maintenance only for non-coherent devices, and handle bounce-buffer copying for both. Platforms can override them if the architecture needs a different policy.
§Driver Usage
Descriptor/control memory:
let mut ring = dma.coherent_array_zero_with_align::<Descriptor>(256, 64)?;
ring.set_cpu(0, Descriptor::new(buffer_dma));
doorbell_after_release_barrier();Owned data buffers:
let mut tx = dma.contiguous_array_zero_with_align::<u8>(
len,
64,
DmaDirection::ToDevice,
)?;
tx.copy_to_device_from_slice(packet);
submit(tx.dma_addr());Device-written owned buffers:
let rx = dma.contiguous_array_zero_with_align::<u8>(
len,
64,
DmaDirection::FromDevice,
)?;
submit(rx.dma_addr());
wait_complete();
rx.read_from_device(packet_len, consume);Streaming mappings:
let map = dma.map_streaming_slice_for_device(buffer, 64, DmaDirection::Bidirectional)?;
submit(map.dma_addr());
wait_complete();
map.complete_for_cpu(0..map.bytes_len());Buffer pools use ContiguousBufferPool and return ContiguousBuffer values.
They are intended for repeated owned data buffers such as network RX/TX pools
or block read buffers. Reusing a buffer does not imply that the memory is
zeroed again; callers own the content and the explicit sync points.
§Choosing A Primitive
Use Coherent* for hardware metadata whose CPU and device ownership flips
frequently and where per-transfer cache maintenance would be wrong or too
fragile: xHCI contexts and rings, NVMe SQ/CQ, network descriptor rings,
SD/MMC descriptor tables.
Use Contiguous* for owned payload memory that needs a contiguous
device-visible DMA address range but not an uncached CPU mapping: block data,
network pools, NVMe PRP data buffers, and accelerator input/output buffers.
Use StreamingMap for caller-owned buffers whose lifetime is tied to one DMA
operation: USB transfer buffers, SDHCI/DWMMC/Phytium MCI block request slices,
or any buffer allocated outside the DMA API.
Structs§
- Array
CpuIter - Coherent
Array - Coherent
Box - Completed
Dma - DMA backing completed by hardware and visible to CPU again.
- Contiguous
Array - Contiguous
Box - Contiguous
Buffer - Contiguous
Buffer Pool - CpuDma
Buffer - CPU-owned contiguous DMA buffer that can be prepared for one async request.
- Device
Dma - DmaAddr
- DmaAlloc
Handle - DmaConstraints
- Device-visible DMA constraints.
- DmaDevice
Info - Complete device-scoped DMA capability metadata.
- DmaMap
Handle - DmaSegment
- One device-visible DMA segment owned by a prepared request.
- InFlight
Dma - DMA backing currently owned by a hardware request.
- Prepared
Dma - DMA backing prepared for device access but not yet owned by hardware.
- Quarantined
Dma - DMA backing that cannot yet be safely recycled.
- Streaming
Map
Enums§
- DmaCoherency
- Cache-coherency relationship between one DMA device and the CPU.
- DmaDirection
- DMA transfer direction.
- DmaDomain
Id - Identity of the address domain used by one DMA device.
- DmaError
Traits§
- DmaArray
CpuRead - DmaOp
- DmaPod
- Marker for plain data that can be safely stored in typed DMA buffers.