Lines
74.36 %
Functions
100 %
use super::*;
/// DMA transmit buffer
///
/// This is a contiguous buffer linked together by DMA descriptors of length
/// 4095 at most. It can only be used for transmitting data to a peripheral's
/// FIFO. See [ScopedDmaRxBuf] for receiving data.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct ScopedDmaTxBuf<'a> {
descriptors: DescriptorSet<'a>,
buffer: DmaAlignedMut<'a, [u8]>,
burst: BurstConfig,
}
impl<'a> ScopedDmaTxBuf<'a> {
/// Creates a new [ScopedDmaTxBuf] from some descriptors and a buffer.
pub fn new(
descriptors: DmaAlignedMut<'a, [DmaDescriptor]>,
) -> Result<Self, DmaBufError> {
Self::new_with_config(descriptors, buffer, BurstConfig::default())
/// Creates a new [ScopedDmaTxBuf] with a caller-chosen minimum alignment.
/// There must be enough descriptors for the provided buffer.
/// Depending on alignment requirements, each descriptor can handle at most
/// 4095 bytes worth of buffer.
/// Both the descriptors and buffer must be in DMA-capable memory.
/// Only DRAM is supported for descriptors.
pub fn new_with_config(
config: impl Into<BurstConfig>,
let mut buf = Self {
descriptors: DescriptorSet::new(descriptors)?,
buffer,
burst: BurstConfig::default(),
};
let capacity = buf.capacity();
buf.configure(config, capacity)?;
Ok(buf)
fn configure(
&mut self,
burst: impl Into<BurstConfig>,
length: usize,
) -> Result<(), DmaBufError> {
let burst = burst.into();
self.set_length_fallible(length, burst)?;
let max_chunk_size = burst.max_chunk_size_for(&self.buffer, TransferDirection::Out);
self.descriptors
.link_with_buffer(&mut self.buffer, max_chunk_size)?;
self.burst = burst;
Ok(())
/// Configures the DMA to use burst transfers to access this buffer.
pub fn set_burst_config(&mut self, burst: BurstConfig) -> Result<(), DmaBufError> {
let len = self.len();
self.configure(burst, len)
/// Consumes the buf, returning the descriptors and buffer.
pub fn split(self) -> (DmaAlignedMut<'a, [DmaDescriptor]>, DmaAlignedMut<'a, [u8]>) {
(self.descriptors.descriptors, self.buffer)
/// Returns the size of the underlying buffer.
pub fn capacity(&self) -> usize {
self.buffer.len()
/// Returns the number of bytes that would be transmitted by this buf.
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
.linked_iter()
.map(|d| d.len())
.sum::<usize>()
fn set_length_fallible(&mut self, len: usize, burst: BurstConfig) -> Result<(), DmaBufError> {
if len > self.capacity() {
return Err(DmaBufError::BufferTooSmall);
burst.ensure_buffer_compatible(&self.buffer[..len], TransferDirection::Out)?;
self.descriptors.set_tx_length(len, max_chunk_size)?;
// This only needs to be done once (after every significant length change) as
// Self::prepare sets Preparation::auto_write_back to false.
for desc in self.descriptors.linked_iter_mut() {
// In non-circular mode, we only set `suc_eof` for the last descriptor to signal
// the end of the transfer.
desc.reset_for_tx(desc.next.is_null());
/// Resets the descriptors to only transmit `len` amount of bytes from this
/// buf.
/// The number of bytes in data must be less than or equal to the buffer
/// size.
pub fn set_length(&mut self, len: usize) {
unwrap!(self.set_length_fallible(len, self.burst))
/// Fills the TX buffer with the bytes provided in `data` and reset the
/// descriptors to only cover the filled section.
pub fn fill(&mut self, data: &[u8]) {
self.set_length(data.len());
self.as_mut_slice()[..data.len()].copy_from_slice(data);
/// Returns the buf as a mutable slice that can be written.
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.buffer
/// Returns the buf as a slice that can be read.
pub fn as_slice(&self) -> &[u8] {
&self.buffer
unsafe impl<'a> DmaTxBuffer for ScopedDmaTxBuf<'a> {
type View = BufView<ScopedDmaTxBuf<'a>>;
type Final = ScopedDmaTxBuf<'a>;
fn prepare(&mut self) -> Preparation {
#[cfg(soc_internal_memory_cached)]
self.descriptors.descriptors.writeback();
#[cfg(dma_can_access_psram)]
let is_data_in_psram = !is_valid_ram_address(self.buffer.as_ptr() as usize);
#[cfg(any(soc_internal_memory_cached, dma_can_access_psram))]
self.buffer.writeback();
Preparation {
start: self.descriptors.head(),
accesses_psram: is_data_in_psram,
burst_transfer: self.burst,
check_owner: None,
auto_write_back: false,
fn into_view(self) -> BufView<ScopedDmaTxBuf<'a>> {
BufView(self)
fn from_view(view: Self::View) -> Self {
view.0
/// DMA receive buffer
/// 4092. It can only be used for receiving data from a peripheral's FIFO.
/// See [ScopedDmaTxBuf] for transmitting data.
pub(crate) struct ScopedDmaRxBuf<'a> {
impl<'a> ScopedDmaRxBuf<'a> {
/// Creates a new [ScopedDmaRxBuf] from some descriptors and a buffer.
/// 4092 bytes worth of buffer.
buf.configure(config, buf.capacity())?;
let max_chunk_size = burst.max_chunk_size_for(&self.buffer, TransferDirection::In);
/// Returns the maximum number of bytes that this buf has been configured to
/// receive.
.map(|d| d.size())
burst.ensure_buffer_compatible(&self.buffer[..len], TransferDirection::In)?;
self.descriptors.set_rx_length(
len,
burst.max_chunk_size_for(&self.buffer[..len], TransferDirection::In),
)
/// Resets the descriptors to only receive `len` amount of bytes into this
unwrap!(self.set_length_fallible(len, self.burst));
/// Returns the entire underlying buffer as a slice that can be read.
/// Returns the entire underlying buffer as a slice that can be written.
/// Returns the number of bytes that was received by this buf.
pub fn number_of_received_bytes(&self) -> usize {
self.sync_received_from_dma();
/// Reads the received data into the provided `buf`.
/// If `buf.len()` is less than the amount of received data then only the
/// first `buf.len()` bytes of received data is written into `buf`.
/// Returns the number of bytes written to `buf`.
pub fn read_received_data(&self, mut buf: &mut [u8]) -> usize {
// Note that due to an ESP32 quirk, the last received descriptor may not get
// updated.
let capacity = buf.len();
for chunk in self.received_data() {
if buf.is_empty() {
break;
let to_fill;
(to_fill, buf) = buf.split_at_mut(chunk.len());
to_fill.copy_from_slice(chunk);
capacity - buf.len()
/// Returns the received data as an iterator of slices.
pub fn received_data(&self) -> impl Iterator<Item = &[u8]> {
self.descriptors.linked_iter().map(|desc| {
// SAFETY: We set up the descriptor to point to a subslice of the buffer, and
// here we are only recreating that slice with a perhaps shorter length.
// We are also not accessing `self.buffer` while this slice is alive, so we
// are not violating any aliasing rules.
unsafe { core::slice::from_raw_parts(desc.buffer.cast_const(), desc.len()) }
})
/// Reads DMA-written RX data from memory on cached SoCs.
fn sync_received_from_dma(&self) {
{
let descriptors = &*self.descriptors.descriptors;
unsafe {
crate::soc::cache_invalidate_addr(
descriptors.as_ptr() as u32,
core::mem::size_of_val(descriptors) as u32,
);
for desc in self.descriptors.linked_iter() {
let len = desc.len();
if len > 0 {
crate::soc::cache_invalidate_addr(desc.buffer as u32, len as u32);
unsafe impl<'a> DmaRxBuffer for ScopedDmaRxBuf<'a> {
type View = BufView<ScopedDmaRxBuf<'a>>;
type Final = ScopedDmaRxBuf<'a>;
desc.reset_for_rx();
self.buffer.invalidate();
auto_write_back: true,
fn into_view(self) -> BufView<Self> {