Merge pull request #1681 from alexferro/feature/stm32-dma-read-exact

Add a STM32/DMARingBuffer::read_exact helper
This commit is contained in:
xoviat 2023-07-28 01:16:48 +00:00 committed by GitHub
commit 44c8db2911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 15 deletions

View File

@ -466,15 +466,53 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow()));
} }
/// Read bytes from the ring buffer /// Read elements from the ring buffer
/// Return a tuple of the length read and the length remaining in the buffer /// Return a tuple of the length read and the length remaining in the buffer
/// If not all of the bytes were read, then there will be some bytes in the buffer remaining /// If not all of the elements were read, then there will be some elements in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Read an exact number of elements from the ringbuffer.
///
/// Returns the remaining number of elements available for immediate reading.
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
///
/// Async/Wake Behavior:
/// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,
/// and when it wraps around. This means that when called with a buffer of length 'M', when this
/// ring buffer was created with a buffer of size 'N':
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> {
use core::future::poll_fn;
use core::sync::atomic::compiler_fence;
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(&mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
}
/// The capacity of the ringbuffer /// The capacity of the ringbuffer
pub fn cap(&self) -> usize { pub fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()

View File

@ -711,15 +711,53 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow()));
} }
/// Read bytes from the ring buffer /// Read elements from the ring buffer
/// Return a tuple of the length read and the length remaining in the buffer /// Return a tuple of the length read and the length remaining in the buffer
/// If not all of the bytes were read, then there will be some bytes in the buffer remaining /// If not all of the elements were read, then there will be some elements in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf)
} }
/// Read an exact number of elements from the ringbuffer.
///
/// Returns the remaining number of elements available for immediate reading.
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
///
/// Async/Wake Behavior:
/// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point,
/// and when it wraps around. This means that when called with a buffer of length 'M', when this
/// ring buffer was created with a buffer of size 'N':
/// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source.
/// - Otherwise, this function may need up to N/2 extra elements to arrive before returning.
pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result<usize, OverrunError> {
use core::future::poll_fn;
use core::sync::atomic::compiler_fence;
let mut read_data = 0;
let buffer_len = buffer.len();
poll_fn(|cx| {
self.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst);
match self.read(&mut buffer[read_data..buffer_len]) {
Ok((len, remaining)) => {
read_data += len;
if read_data == buffer_len {
Poll::Ready(Ok(remaining))
} else {
Poll::Pending
}
}
Err(e) => Poll::Ready(Err(e)),
}
})
.await
}
// The capacity of the ringbuffer // The capacity of the ringbuffer
pub fn cap(&self) -> usize { pub fn cap(&self) -> usize {
self.ringbuf.cap() self.ringbuf.cap()

View File

@ -72,10 +72,10 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> {
self.cap() - remaining_transfers self.cap() - remaining_transfers
} }
/// Read bytes from the ring buffer /// Read elements from the ring buffer
/// Return a tuple of the length read and the length remaining in the buffer /// Return a tuple of the length read and the length remaining in the buffer
/// If not all of the bytes were read, then there will be some bytes in the buffer remaining /// If not all of the elements were read, then there will be some elements in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
/* /*
@ -95,11 +95,11 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> {
*/ */
let end = self.pos(dma.get_remaining_transfers()); let end = self.pos(dma.get_remaining_transfers());
if self.start == end && dma.get_complete_count() == 0 { if self.start == end && dma.get_complete_count() == 0 {
// No bytes are available in the buffer // No elements are available in the buffer
Ok((0, self.cap())) Ok((0, self.cap()))
} else if self.start < end { } else if self.start < end {
// The available, unread portion in the ring buffer DOES NOT wrap // The available, unread portion in the ring buffer DOES NOT wrap
// Copy out the bytes from the dma buffer // Copy out the elements from the dma buffer
let len = self.copy_to(buf, self.start..end); let len = self.copy_to(buf, self.start..end);
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
@ -128,7 +128,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> {
// The DMA writer has wrapped since we last read and is currently // The DMA writer has wrapped since we last read and is currently
// writing (or the next byte added will be) in the beginning of the ring buffer. // writing (or the next byte added will be) in the beginning of the ring buffer.
// The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. // The provided read buffer is not large enough to include all elements from the tail of the dma buffer.
// Copy out from the dma buffer // Copy out from the dma buffer
let len = self.copy_to(buf, self.start..self.cap()); let len = self.copy_to(buf, self.start..self.cap());
@ -154,8 +154,8 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> {
// The DMA writer has wrapped since we last read and is currently // The DMA writer has wrapped since we last read and is currently
// writing (or the next byte added will be) in the beginning of the ring buffer. // writing (or the next byte added will be) in the beginning of the ring buffer.
// The provided read buffer is large enough to include all bytes from the tail of the dma buffer, // The provided read buffer is large enough to include all elements from the tail of the dma buffer,
// so the next read will not have any unread tail bytes in the ring buffer. // so the next read will not have any unread tail elements in the ring buffer.
// Copy out from the dma buffer // Copy out from the dma buffer
let tail = self.copy_to(buf, self.start..self.cap()); let tail = self.copy_to(buf, self.start..self.cap());
@ -180,7 +180,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> {
} }
/// Copy from the dma buffer at `data_range` into `buf` /// Copy from the dma buffer at `data_range` into `buf`
fn copy_to(&mut self, buf: &mut [W], data_range: Range<usize>) -> usize { fn copy_to(&mut self, buf: &mut [W], data_range: Range<usize>) -> usize {
// Limit the number of bytes that can be copied // Limit the number of elements that can be copied
let length = usize::min(data_range.len(), buf.len()); let length = usize::min(data_range.len(), buf.len());
// Copy from dma buffer into read buffer // Copy from dma buffer into read buffer