From df8f508ffa2bec79f6e3fba4ac3cfe0e5545b5b2 Mon Sep 17 00:00:00 2001
From: Corey Schuhen <cschuhen@gmail.com>
Date: Sat, 2 Mar 2024 09:00:54 +1000
Subject: [PATCH] Writing to TX buffer also needs to fire an interrupt to kick
 off transmission if it is idle.

Formatting
---
 embassy-stm32/src/can/fdcan.rs  | 62 ++++++++++++++++++++++++++++++---
 examples/stm32g4/src/bin/can.rs |  6 +++-
 2 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs
index 6a4a25cb7..77db774fc 100644
--- a/embassy-stm32/src/can/fdcan.rs
+++ b/embassy-stm32/src/can/fdcan.rs
@@ -436,7 +436,31 @@ pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_S
 }
 
 /// Sender that can be used for sending CAN frames.
-pub type BufferedCanSender = embassy_sync::channel::DynamicSender<'static, ClassicFrame>;
+#[derive(Copy, Clone)]
+pub struct BufferedCanSender {
+    tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>,
+    waker: fn(),
+}
+
+impl BufferedCanSender {
+    /// Async write frame to TX buffer.
+    pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError<ClassicFrame>> {
+        self.tx_buf.try_send(frame)?;
+        (self.waker)();
+        Ok(())
+    }
+
+    /// Async write frame to TX buffer.
+    pub async fn write(&mut self, frame: ClassicFrame) {
+        self.tx_buf.send(frame).await;
+        (self.waker)();
+    }
+
+    /// Allows a poll_fn to poll until the channel is ready to write
+    pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {
+        self.tx_buf.poll_ready_to_send(cx)
+    }
+}
 
 /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
 pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, (ClassicFrame, Timestamp)>;
@@ -489,7 +513,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
 
     /// Returns a sender that can be used for sending CAN frames.
     pub fn writer(&self) -> BufferedCanSender {
-        self.tx_buf.sender().into()
+        BufferedCanSender {
+            tx_buf: self.tx_buf.sender().into(),
+            waker: T::IT0Interrupt::pend,
+        }
     }
 
     /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
@@ -525,7 +552,31 @@ pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF
 }
 
 /// Sender that can be used for sending CAN frames.
-pub type BufferedFdCanSender = embassy_sync::channel::DynamicSender<'static, FdFrame>;
+#[derive(Copy, Clone)]
+pub struct BufferedFdCanSender {
+    tx_buf: embassy_sync::channel::DynamicSender<'static, FdFrame>,
+    waker: fn(),
+}
+
+impl BufferedFdCanSender {
+    /// Async write frame to TX buffer.
+    pub fn try_write(&mut self, frame: FdFrame) -> Result<(), embassy_sync::channel::TrySendError<FdFrame>> {
+        self.tx_buf.try_send(frame)?;
+        (self.waker)();
+        Ok(())
+    }
+
+    /// Async write frame to TX buffer.
+    pub async fn write(&mut self, frame: FdFrame) {
+        self.tx_buf.send(frame).await;
+        (self.waker)();
+    }
+
+    /// Allows a poll_fn to poll until the channel is ready to write
+    pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {
+        self.tx_buf.poll_ready_to_send(cx)
+    }
+}
 
 /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
 pub type BufferedFdCanReceiver = embassy_sync::channel::DynamicReceiver<'static, (FdFrame, Timestamp)>;
@@ -578,7 +629,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
 
     /// Returns a sender that can be used for sending CAN frames.
     pub fn writer(&self) -> BufferedFdCanSender {
-        self.tx_buf.sender().into()
+        BufferedFdCanSender {
+            tx_buf: self.tx_buf.sender().into(),
+            waker: T::IT0Interrupt::pend,
+        }
     }
 
     /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs
index a41f765c1..11e96361e 100644
--- a/examples/stm32g4/src/bin/can.rs
+++ b/examples/stm32g4/src/bin/can.rs
@@ -184,7 +184,11 @@ async fn main(_spawner: Spawner) {
             let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
             info!("Writing frame");
 
-            _ = can.write(frame).await;
+            // You can use any of these approaches to send. The writer makes it
+            // easy to share sending from multiple tasks.
+            //_ = can.write(frame).await;
+            //can.writer().try_write(frame).unwrap();
+            can.writer().write(frame).await;
 
             match can.read().await {
                 Ok((rx_frame, ts)) => {