From cb01d0383523af4b9f80c4bdc4410791eba1173e Mon Sep 17 00:00:00 2001
From: Tyler Gilbert <tyler.w.gilbert@gmail.com>
Date: Sun, 31 Mar 2024 16:31:47 -0500
Subject: [PATCH] Add async stop() function to stm32 bdma_dma

---
 embassy-stm32/src/dma/dma_bdma.rs | 63 ++++++++++++++++++++++++++++++-
 1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs
index 7b5b3cf58..a6344cf06 100644
--- a/embassy-stm32/src/dma/dma_bdma.rs
+++ b/embassy-stm32/src/dma/dma_bdma.rs
@@ -1,4 +1,4 @@
-use core::future::Future;
+use core::future::{poll_fn, Future};
 use core::pin::Pin;
 use core::sync::atomic::{fence, AtomicUsize, Ordering};
 use core::task::{Context, Poll, Waker};
@@ -510,6 +510,31 @@ impl AnyChannel {
             DmaInfo::Bdma(r) => r.ch(info.num).ndtr().read().ndt(),
         }
     }
+
+    fn disable_circular_mode(&self) {
+        let info = self.info();
+        match self.info().dma {
+            #[cfg(dma)]
+            DmaInfo::Dma(regs) => regs.st(info.num).cr().modify(|w| {
+                w.set_circ(false);
+            }),
+            #[cfg(bdma)]
+            DmaInfo::Bdma(regs) => regs.ch(info.num).cr().modify(|w| {
+                w.set_circ(false);
+            }),
+        }
+    }
+
+    fn poll_stop(&self) -> Poll<()> {
+        use core::sync::atomic::compiler_fence;
+        compiler_fence(Ordering::SeqCst);
+
+        if !self.is_running() {
+            Poll::Ready(())
+        } else {
+            Poll::Pending
+        }
+    }
 }
 
 /// DMA transfer.
@@ -829,6 +854,25 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
     pub fn is_running(&mut self) -> bool {
         self.channel.is_running()
     }
+
+    /// Stop the DMA transfer and await until the buffer is full.
+    ///
+    /// This disables the DMA transfer's circular mode so that the transfer
+    /// stops when the buffer is full.
+    ///
+    /// This is designed to be used with streaming input data such as the
+    /// I2S/SAI or ADC.
+    ///
+    /// When using the UART, you probably want `request_stop()`.
+    pub async fn stop(&mut self) {
+        self.channel.disable_circular_mode();
+        //wait until cr.susp reads as true
+        poll_fn(|cx| {
+            self.set_waker(cx.waker());
+            self.channel.poll_stop()
+        })
+        .await
+    }
 }
 
 impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
@@ -940,6 +984,23 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
     pub fn is_running(&mut self) -> bool {
         self.channel.is_running()
     }
+
+    /// Stop the DMA transfer and await until the buffer is empty.
+    ///
+    /// This disables the DMA transfer's circular mode so that the transfer
+    /// stops when all available data has been written.
+    ///
+    /// This is designed to be used with streaming output data such as the
+    /// I2S/SAI or DAC.
+    pub async fn stop(&mut self) {
+        self.channel.disable_circular_mode();
+        //wait until cr.susp reads as true
+        poll_fn(|cx| {
+            self.set_waker(cx.waker());
+            self.channel.poll_stop()
+        })
+        .await
+    }
 }
 
 impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> {