1use ::core::cell::UnsafeCell;
19use ::core::ffi::c_void;
20use ::core::future::poll_fn;
21use ::core::sync::atomic::{AtomicI32, Ordering};
22use ::core::task::Poll;
23
24use embassy_sync::waitqueue::AtomicWaker;
25
26use crate::bindings;
27use crate::error::{Error, Result};
28
29pub struct DmaSlot {
33 waker: AtomicWaker,
34 result: AtomicI32,
35}
36
37impl DmaSlot {
38 pub const PENDING: i32 = i32::MIN;
41
42 pub const fn new() -> Self {
43 Self {
44 waker: AtomicWaker::new(),
45 result: AtomicI32::new(Self::PENDING),
46 }
47 }
48
49 pub fn reset(&self) {
50 self.result.store(Self::PENDING, Ordering::Release);
51 }
52
53 pub fn result_store(&self, value: i32) {
54 self.result.store(value, Ordering::Release);
55 }
56
57 pub fn result_load(&self) -> i32 {
58 self.result.load(Ordering::Acquire)
59 }
60
61 pub fn register(&self, w: &::core::task::Waker) {
62 self.waker.register(w);
63 }
64
65 pub fn wake(&self) {
66 self.waker.wake();
67 }
68}
69
70unsafe extern "C" fn dma_complete_cb(result: ::core::ffi::c_int, user_data: *mut c_void) {
71 let slot = unsafe { &*(user_data as *const DmaSlot) };
75 slot.result_store(result as i32);
76 slot.wake();
77}
78
79pub struct AsyncSpi {
81 spi: bindings::ove_spi_t,
82 slot: UnsafeCell<DmaSlot>,
83}
84
85unsafe impl Send for AsyncSpi {}
90unsafe impl Sync for AsyncSpi {}
91
92impl AsyncSpi {
93 #[inline]
100 pub const unsafe fn from_handle(handle: bindings::ove_spi_t) -> Self {
101 Self {
102 spi: handle,
103 slot: UnsafeCell::new(DmaSlot::new()),
104 }
105 }
106
107 pub async fn transfer<'a>(
113 &'a self,
114 cs: Option<&'a bindings::ove_spi_cs>,
115 tx: &'a [u8],
116 rx: &'a mut [u8],
117 ) -> Result<()> {
118 let len = tx.len().max(rx.len());
119 if len == 0 {
120 return Ok(());
121 }
122 let cs_ptr = cs.map_or(::core::ptr::null(), |c| c as *const _);
123 let tx_ptr = if tx.is_empty() {
124 ::core::ptr::null()
125 } else {
126 tx.as_ptr().cast()
127 };
128 let rx_ptr = if rx.is_empty() {
129 ::core::ptr::null_mut()
130 } else {
131 rx.as_mut_ptr().cast()
132 };
133
134 let slot: &DmaSlot = unsafe { &*self.slot.get() };
138 slot.reset();
139
140 let rc = unsafe {
141 bindings::ove_spi_transfer_async(
142 self.spi,
143 cs_ptr,
144 tx_ptr,
145 rx_ptr,
146 len,
147 Some(dma_complete_cb),
148 slot as *const _ as *mut c_void,
149 )
150 };
151 Error::from_code(rc)?;
152
153 poll_fn(|cx| {
154 slot.register(cx.waker());
155 let r = slot.result_load();
156 if r == DmaSlot::PENDING {
157 Poll::Pending
158 } else {
159 Poll::Ready(Error::from_code(r))
160 }
161 })
162 .await
163 }
164
165 pub async fn write<'a>(
167 &'a self,
168 cs: Option<&'a bindings::ove_spi_cs>,
169 data: &'a [u8],
170 ) -> Result<()> {
171 let mut empty = [];
172 self.transfer(cs, data, &mut empty).await
173 }
174
175 pub async fn read<'a>(
177 &'a self,
178 cs: Option<&'a bindings::ove_spi_cs>,
179 buf: &'a mut [u8],
180 ) -> Result<()> {
181 let empty: &[u8] = &[];
182 self.transfer(cs, empty, buf).await
183 }
184}
185
186#[cfg(feature = "embedded-hal-async")]
187mod hal_async_impl {
188 use super::AsyncSpi;
189 use crate::error::Error;
190 use embedded_hal::spi::ErrorType;
191 use embedded_hal_async::spi::SpiBus;
192
193 impl ErrorType for AsyncSpi {
194 type Error = Error;
195 }
196
197 impl SpiBus<u8> for AsyncSpi {
198 async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
199 AsyncSpi::read(self, None, words).await
200 }
201
202 async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
203 AsyncSpi::write(self, None, words).await
204 }
205
206 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
207 AsyncSpi::transfer(self, None, write, read).await
211 }
212
213 async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
214 let mut buf = [0u8; 64];
217 let mut remaining = &mut words[..];
218 while !remaining.is_empty() {
219 let chunk = remaining.len().min(buf.len());
220 buf[..chunk].copy_from_slice(&remaining[..chunk]);
221 AsyncSpi::transfer(self, None, &buf[..chunk], &mut remaining[..chunk]).await?;
222 let split = ::core::mem::take(&mut remaining);
223 remaining = &mut split[chunk..];
224 }
225 Ok(())
226 }
227
228 async fn flush(&mut self) -> Result<(), Self::Error> {
229 Ok(())
230 }
231 }
232}