embassy-rs/embassy · critical

DMA: link transfer error on DMA@

Error message

DMA: link transfer error on DMA@{:08x} channel {}

What it means

The GPDMA channel IRQ handler found the link transfer error flag (ULEF) set: an error occurred while fetching or executing a linked-list item (LLI). The linked-list descriptor address was invalid or the LLI itself was corrupted/unreachable. The driver panics because linked-list state can no longer be trusted.

Solutions

  1. Place linked-list items in DMA-accessible, writable RAM (not flash/DTCM)
  2. Ensure LLI descriptors have required alignment and outlive the transfer
  3. Verify each LLI's next-address field (la/pa) points to a valid item or terminates correctly
  4. Update embassy-stm32 if using the linked-list API, as this area is under active development

Example fix

// before
fn build(x: &[u8]) -> Transfer { let lli = LLi::new(...); Transfer::new(lli) } // lli dropped
// after
static mut LLI: LLiState = LLiState::new(); // keep descriptor alive in RAM
fn build(x: &[u8]) -> Transfer { Transfer::new(unsafe { &mut LLI }) }
Defensive patterns

Strategy: validation

Validate before calling

fn lli_valid(lli_addr: u32, item_size: usize) -> bool {
    lli_addr != 0 && lli_addr % core::mem::align_of::<LLiState>() as u32 == 0
        // must be in DMA-reachable RAM, e.g. SRAM region
        && (0x2000_0000..0x2004_0000).contains(&lli_addr)
        && core::mem::size_of::<LLiState>() == item_size
}

Prevention

When it happens

Trigger: Using linked-list mode (DMA linked-list/ring APIs) where an LLI points to invalid memory, an unaligned descriptor, or a descriptor in DMA-inaccessible memory; LLI struct modified while the channel runs.

Common situations: Building custom linked-list item chains with embassy's gpdma linked-list API and placing items in flash or DTCM; forgetting to keep LLiState items alive (dropped stack descriptors).

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/6932a34a99c985db. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/src/dma/gpdma/mod.rs:576

    let ch = info.dma.ch(info.num);
    let sr = ch.sr().read();

    if sr.dtef() {
        panic!(
            "DMA: data transfer error on DMA@{:08x} channel {}",
            info.dma.cast().as_ptr() as u32,
            info.num
        );
    }
    if sr.usef() {
        panic!(
            "DMA: user settings error on DMA@{:08x} channel {}",
            info.dma.cast().as_ptr() as u32,
            info.num
        );
    }
    if sr.ulef() {
        panic!(
            "DMA: link transfer error on DMA@{:08x} channel {}",
            info.dma.cast().as_ptr() as u32,
            info.num
        );
    }

    if sr.htf() {
        ch.fcr().write(|w| w.set_htf(true));
    }

    if sr.tcf() {
        ch.fcr().write(|w| w.set_tcf(true));

        let lli_count = state.lli_state.count.load(Ordering::Acquire);
        let complete = if lli_count > 0 {
            let next_lli_index = state.lli_state.index.load(Ordering::Acquire) + 1;
            let complete = next_lli_index >= lli_count;

View on GitHub (pinned to 463a07b963)