{"record":{"id":"c22ebf3ad6e3b1c9","repo":"embassy-rs/embassy","slug":"dma-transfers-may-not-be-larger-than-65535-bytes","errorCode":null,"errorMessage":"DMA transfers may not be larger than 65535 bytes.","messagePattern":"DMA transfers may not be larger than 65535 bytes\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-stm32/src/dma/gpdma/linked_list.rs","lineNumber":125,"sourceCode":"\nimpl Item {\n    /// Build a new item from raw transfer parameters.\n    ///\n    /// # Safety\n    /// The caller must ensure `peri_addr` and `mem_addr` are valid for the transfer.\n    #[allow(clippy::too_many_arguments)]\n    pub(super) unsafe fn new(\n        request: Request,\n        dir: Dir,\n        peri_addr: *const u32,\n        mem_addr: *mut u32,\n        mem_len: usize,\n        incr_mem: bool,\n        data_size: WordSize,\n        dst_size: WordSize,\n    ) -> Self {\n        let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {\n            panic!(\"DMA transfers may not be larger than 65535 bytes.\");\n        };\n\n        let mut br1 = regs::ChBr1(0);\n        br1.set_bndt(bndt);\n\n        let mut tr1 = regs::ChTr1(0);\n        tr1.set_sdw(data_size.into());\n        tr1.set_ddw(dst_size.into());\n        tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);\n        tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);\n\n        #[cfg(gpdma)]\n        {\n            use stm32_metapac::gpdma::vals::Ap;\n            tr1.set_sap(match dir {\n                Dir::MemoryToPeripheral => Ap::Port0,\n                Dir::PeripheralToMemory => Ap::Port1,\n                Dir::MemoryToMemory => panic!(\"memory-to-memory transfers are not valid for linked-list items\"),","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-stm32/src/dma/gpdma/linked_list.rs#L107-L143","documentation":"When constructing a GPDMA linked-list Item, the block length BNDT is computed as mem_len * data_size bytes and stored in a u16 register field. If the byte count exceeds 65535, the TryInto<u16> conversion fails and the driver panics, because GPDMA hardware encodes block transfer length in 16 bits.","triggerScenarios":"Calling new_read/new_write (or LinearItem/TwoDItem construction) on a GPDMA channel with a buffer whose total byte size (mem_len * data_size.bytes()) is greater than 65535, e.g. a 70000-byte buffer or a 40000-element u16 buffer.","commonSituations":"Passing large RAM buffers straight to a GPDMA linked-list transfer without chunking; assuming element count limits rather than byte limits (a u32 buffer of 20000 elements already exceeds 65535 bytes); moving code that worked on DMA/BDMA (32-bit NDTR) to GPDMA chips (e.g. H5/U5).","solutions":["Split the transfer into chunks of at most 65535 bytes, chaining multiple linked-list items.","Use a smaller data word size (e.g. u8 words instead of u32) if the data allows, keeping mem_len * word_bytes <= 65535.","Use a 2D linked-list item or repeated blocks if the chip's GPDMA supports larger effective transfers.","Use a classic DMA/BDMA channel (16-bit NDTR in elements, up to 65535 elements) that fits the required length."],"exampleFix":"// before\nlet mut buf = [0u32; 20000]; // 80000 bytes > 65535\nlet item = LinearItem::new_read(request, peri, &mut buf);\n// after\nlet (mut buf1, mut buf2) = ([0u32; 16000], [0u32; 4000]); // 64000 + 16000 bytes\nlet item1 = LinearItem::new_read(request, peri, &mut buf1);\nlet item2 = LinearItem::new_read(request, peri, &mut buf2);","handlingStrategy":"validation","validationCode":"fn gpdma_bndt_ok(mem_len: usize, word_bytes: usize) -> bool {\n    mem_len.checked_mul(word_bytes).map_or(false, |b| b <= 65535)\n}\n// before building the item:\nassert!(gpdma_bndt_ok(buf.len(), core::mem::size_of::<u32>()));","typeGuard":"fn fits_gpdma_block<T>(buf: &[T]) -> bool {\n    core::mem::size_of_val(buf) <= 65535\n}","tryCatchPattern":"// panics are not catchable in embedded Rust; validate before the call:\nif core::mem::size_of_val(&buf) > 65535 {\n    // chunk the transfer instead of constructing the item\n    return Err(DmaError::TooLarge);\n}\nlet item = LinearItem::new_read(request, peri, &mut buf);","preventionTips":["Enforce a 65535-byte cap on GPDMA linked-list buffers at the application boundary","Remember the limit is in BYTES not elements (u32 buffers: max 16383 elements)","Chunk large transfers into multiple linked-list items","When porting from classic DMA, re-check length limits: GPDMA BNDT is 16-bit bytes"],"tags":["dma","gpdma","panic","embedded","buffer-size","linked-list"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}