soc_esp32::dma
The shared DMA channels, and who owns them.
The ESP32 has no general-purpose DMA controller — that arrived with the S3’s GDMA. What it has is three channels behind a crossbar, shared by SPI1, SPI2 and SPI3, selected by a 2-bit field per host in one DPORT register.
Two drivers each assuming they own a channel do not fail; they write each other’s descriptors and corrupt each other’s transfers. Nothing in the hardware objects. So the point of this module is that the second claim returns an error.
DMA is a per-chip thing, not a portable one
Section titled “DMA is a per-chip thing, not a portable one”This module is soc-esp32 and nothing about it generalises. The ESP32 has
a three-channel crossbar for SPI; the S3 has a GDMA with a completely
different allocation model; an STM32 has numbered streams with fixed
peripheral mappings; an RP2040 has twelve uniform channels. There is no
shared vocabulary worth inventing until a second chip is actually here to
say what it needs, so there is no hal trait for this and drivers name the
SoC’s allocator directly.
What is portable is the discipline: buffers and descriptors must live
where the engine can reach them, and that is kernel::dma_broker.
What is not in this crossbar
Section titled “What is not in this crossbar”I2S0 and I2S1 each have their own dedicated DMA engine on this chip, and so do SDMMC and UHCI. They are not allocated from here and do not contend with SPI. (Issue #18 lists I2S as sharing these channels; that is true of some later parts and not of the ESP32 v1.)
Register facts
Section titled “Register facts”DPORT_SPI_DMA_CHAN_SEL_REG at DPORT_BASE + 0x5A8, from dport_reg.h:
| Host | Field |
|---|---|
| SPI1 | bits [1:0] |
| SPI2 | bits [3:2] |
| SPI3 | bits [5:4] |
A field of 0 means “no channel”; 1, 2 and 3 select the channel of that
number. There is no channel 0, which is why [Channel] starts at 1.
Structs
Section titled “Structs”Channel
Section titled “Channel”struct Channel { /* private fields */ }A claimed DMA channel.
Not Clone or Copy: two owners is exactly the situation this module
exists to prevent, and the type should not hand one out.
Implements: Debug, Eq, PartialEq, StructuralPartialEq
DmaReach
Section titled “DmaReach”struct DmaReach;This SoC’s [hal::dma::DmaReach] — the ESP32’s DMA-reachable window is all
internal DRAM. A driver holds one of these to validate a buffer without
naming the address range itself.
Implements: DmaReach
DmaError
Section titled “DmaError”enum DmaError { NoChannelFree, HostAlreadyHasChannel, ChunkTooLong, UnreachableAddress, NotEnoughDescriptors,}Why a claim failed.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
enum Host { Spi1, Spi2, Spi3,}A SPI host that can be given a DMA channel.
Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
Functions
Section titled “Functions”unsafe fn claim(host: Host) -> Result<Channel, DmaError>Claim a free channel for host and point the crossbar at it.
Safety
Section titled “Safety”Writes DPORT_SPI_DMA_CHAN_SEL_REG. The returned channel is exclusively
the caller’s until it is [release]d.
References: Channel, DmaError, Host
free_channels
Section titled “free_channels”fn free_channels() -> u8How many channels are unclaimed.
owner_of
Section titled “owner_of”fn owner_of(channel: u8) -> Option<Host>Which host owns channel (1..=3), if any.
References: Host
release
Section titled “release”unsafe fn release(channel: Channel)Give a channel back and disconnect it from its host.
Safety
Section titled “Safety”Writes DPORT_SPI_DMA_CHAN_SEL_REG. Any transfer still in flight on this
channel is abandoned, not stopped — stop the peripheral first.
References: Channel
sync_for_device
Section titled “sync_for_device”fn sync_for_device()Publish descriptor and buffer writes before a DMA link is started.
Xtensa’s write buffer can otherwise leave the peripheral observing the previous owner bit when a descriptor is rebuilt and immediately re-armed.
Constants
Section titled “Constants”CHANNELS
Section titled “CHANNELS”const CHANNELS: u8;How many channels the crossbar has. Numbered 1..=3.