embassy-rs/embassy · error

memory-to-memory transfers are not valid for linked-list…

Error message

memory-to-memory transfers are not valid for linked-list items

What it means

GPDMA linked-list items in embassy-stm32 require at least one port to be a peripheral because the item is configured with a request (reqsel) and peripheral port assignment. MemoryToMemory direction has no peripheral request, so Item::new panics when mapping Dir::MemoryToMemory to a source port (AP).

Solutions

  1. Use a memory-to-memory-capable DMA type (classic MDMA or DMA/BDMA channel) instead of a GPDMA linked-list item.
  2. Perform the copy with the CPU (core::ptr::copy_nonoverlapping or slice copy_from_slice) if no DMA is strictly required.
  3. Use MDMA, which explicitly supports MemoryToMemory (with increment configured), for the memory copy.
  4. Restructure the transfer so the data flows through a peripheral (e.g. SPI in loopback) if GPDMA linked lists are mandatory.

Example fix

// before
let item = LinearItem::new_read(request, src as *const u32, dst, Dir::MemoryToMemory);
// after
use core::ptr::copy_nonoverlapping;
unsafe { copy_nonoverlapping(src as *const u32, dst, len); } // CPU copy, no GPDMA item needed
Defensive patterns

Strategy: validation

Validate before calling

use embassy_stm32::dma::Dir;
fn assert_gpdma_item_dir(dir: Dir) {
    assert!(matches!(dir, Dir::MemoryToPeripheral | Dir::PeripheralToMemory),
        "GPDMA linked-list items cannot be memory-to-memory");
}

Type guard

fn is_peripheral_dir(dir: Dir) -> bool {
    matches!(dir, Dir::MemoryToPeripheral | Dir::PeripheralToMemory)
}

Try / catch

// panics are not catchable; gate the call:
if dir == Dir::MemoryToMemory {
    return Err(NotSupported::Mem2MemGpdmaLinkedList);
}
let item = LinearItem::new_read(request, peri, buf);

Prevention

When it happens

Trigger: Constructing a LinearItem/TwoDItem (or calling Item::new) with dir = Dir::MemoryToMemory on a GPDMA channel — e.g. requesting a memory-to-memory copy as a linked-list item via new_read/new_write-style item constructors with MemoryToMemory direction.

Common situations: Porting memory-to-memory DMA code that worked on classic DMA/BDMA (where Mem2Mem is supported) to a GPDMA part (STM32 H5/U5/H7RS); building a linked list to perform an internal buffer copy; passing a generic dir parameter that is MemoryToMemory at runtime.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/dma/gpdma/linked_list.rs:143

            panic!("DMA transfers may not be larger than 65535 bytes.");
        };

        let mut br1 = regs::ChBr1(0);
        br1.set_bndt(bndt);

        let mut tr1 = regs::ChTr1(0);
        tr1.set_sdw(data_size.into());
        tr1.set_ddw(dst_size.into());
        tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);
        tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);

        #[cfg(gpdma)]
        {
            use stm32_metapac::gpdma::vals::Ap;
            tr1.set_sap(match dir {
                Dir::MemoryToPeripheral => Ap::Port0,
                Dir::PeripheralToMemory => Ap::Port1,
                Dir::MemoryToMemory => panic!("memory-to-memory transfers are not valid for linked-list items"),
            });
            tr1.set_dap(match dir {
                Dir::MemoryToPeripheral => Ap::Port1,
                Dir::PeripheralToMemory => Ap::Port0,
                Dir::MemoryToMemory => panic!("memory-to-memory transfers are not valid for linked-list items"),
            });
        }

        let mut tr2 = regs::ChTr2(0);
        tr2.set_dreq(match dir {
            Dir::MemoryToPeripheral => Dreq::DestinationPeripheral,
            Dir::PeripheralToMemory => Dreq::SourcePeripheral,
            Dir::MemoryToMemory => panic!("memory-to-memory transfers are not valid for linked-list items"),
        });
        tr2.set_reqsel(request);

        let (sar, dar) = match dir {
            Dir::MemoryToPeripheral => (mem_addr as _, peri_addr as _),

View on GitHub (pinned to 463a07b963)