Skip to main content

vorago_shared_hal/spi/
asynch.rs

1use core::cell::RefCell;
2
3use arbitrary_int::u5;
4use critical_section::Mutex;
5use embassy_sync::waitqueue::AtomicWaker;
6use portable_atomic::AtomicBool;
7use raw_slice::{RawBufSlice, RawBufSliceMut};
8
9use crate::{
10    shared::{FifoClear, TriggerLevel},
11    spi::{
12        FIFO_DEPTH,
13        regs::{Data, InterruptClear, InterruptControl},
14    },
15};
16
17#[cfg(feature = "vor1x")]
18pub const NUM_SPIS: usize = 3;
19#[cfg(feature = "vor4x")]
20pub const NUM_SPIS: usize = 4;
21
22static WAKERS: [AtomicWaker; NUM_SPIS] = [const { AtomicWaker::new() }; NUM_SPIS];
23static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; NUM_SPIS] =
24    [const { Mutex::new(RefCell::new(TransferContext::new())) }; NUM_SPIS];
25// Completion flag. Kept outside of the context structure as an atomic to avoid
26// critical section.
27static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS];
28
29#[derive(Debug, Clone, Copy, thiserror::Error)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31#[error("SPI RX FIFO overrun")]
32pub struct RxOverrunError;
33
34impl embedded_hal_async::spi::Error for RxOverrunError {
35    fn kind(&self) -> embedded_hal::spi::ErrorKind {
36        embedded_hal::spi::ErrorKind::Overrun
37    }
38}
39
40/// This is a generic interrupt handler to handle asynchronous SPI  operations for a given
41/// SPI peripheral.
42///
43/// The user has to call this once in the interrupt handler responsible for the SPI interrupts on
44/// the given SPI bank.
45pub fn on_interrupt(peripheral: super::Bank) {
46    let mut spi = unsafe { peripheral.steal_regs() };
47    let index = peripheral as usize;
48    let enabled_irqs = spi.read_interrupt_control();
49    let interrupt_status = spi.read_interrupt_status();
50    spi.write_interrupt_clear(InterruptClear::ALL);
51    // Prevent spurious interrupts from messing with out logic here.
52    spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
53    // IRQ is not related.
54    if enabled_irqs.raw_value() == 0 {
55        reset_trigger_levels(&mut spi);
56        spi.write_fifo_clear(FifoClear::ALL);
57        return;
58    }
59    if interrupt_status.rx_overrun() {
60        // Not sure how to otherwise handle this cleanly..
61        return handle_rx_overrun(&mut spi, index);
62    }
63    let mut context = critical_section::with(|cs| {
64        let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
65        *context_ref.borrow()
66    });
67    // No transfer active.
68    if context.transfer_type.is_none() {
69        return;
70    }
71    let transfer_type = context.transfer_type.unwrap();
72    match transfer_type {
73        TransferType::Read => on_interrupt_read(index, &mut context, &mut spi, enabled_irqs),
74        TransferType::Write => on_interrupt_write(index, &mut context, &mut spi, enabled_irqs),
75        TransferType::Transfer => {
76            on_interrupt_transfer(index, &mut context, &mut spi, enabled_irqs)
77        }
78        TransferType::TransferInPlace => {
79            on_interrupt_transfer_in_place(index, &mut context, &mut spi, enabled_irqs)
80        }
81    };
82}
83
84fn handle_rx_overrun(spi: &mut super::regs::MmioSpi<'static>, idx: usize) {
85    critical_section::with(|cs| {
86        let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
87        context_ref.borrow_mut().rx_overrun = true;
88    });
89    // Clean up, restore clean state.
90    reset_trigger_levels(spi);
91    spi.write_fifo_clear(FifoClear::ALL);
92    // Interrupts were already disabled and cleared.
93    DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
94    WAKERS[idx].wake();
95}
96
97fn on_interrupt_read(
98    idx: usize,
99    context: &mut TransferContext,
100    spi: &mut super::regs::MmioSpi<'static>,
101    enabled_irqs: InterruptControl,
102) {
103    let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
104    let transfer_len = read_slice.len();
105
106    // Read data from RX FIFO first.
107    while spi.read_status().rx_not_empty() {
108        let data = spi.read_data();
109        if context.rx_progress < transfer_len {
110            read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
111            context.rx_progress += 1;
112        }
113    }
114
115    // The FIFO still needs to be pumped.
116    while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() {
117        spi.write_data(Data::new_with_raw_value(0));
118        context.tx_progress += 1;
119    }
120
121    isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
122}
123
124fn on_interrupt_write(
125    idx: usize,
126    context: &mut TransferContext,
127    spi: &mut super::regs::MmioSpi<'static>,
128    enabled_irqs: InterruptControl,
129) {
130    let write_slice = unsafe { context.tx_slice.get().unwrap() };
131    let transfer_len = write_slice.len();
132
133    // Read data from RX FIFO first.
134    while spi.read_status().rx_not_empty() {
135        spi.read_data();
136        if context.rx_progress < transfer_len {
137            context.rx_progress += 1;
138        }
139    }
140
141    // Data still needs to be sent
142    while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
143        spi.write_data(Data::new_with_raw_value(
144            write_slice[context.tx_progress] as u32,
145        ));
146        context.tx_progress += 1;
147    }
148
149    isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
150}
151
152fn on_interrupt_transfer(
153    idx: usize,
154    context: &mut TransferContext,
155    spi: &mut super::regs::MmioSpi<'static>,
156    enabled_irqs: InterruptControl,
157) {
158    let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
159    let read_len = read_slice.len();
160    let write_slice = unsafe { context.tx_slice.get().unwrap() };
161    let write_len = write_slice.len();
162    let transfer_len = core::cmp::max(read_len, write_len);
163
164    // Send data first to avoid overwriting data that still needs to be sent.
165    while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
166        spi.write_data(Data::new_with_raw_value(
167            write_slice.get(context.tx_progress).copied().unwrap_or(0) as u32,
168        ));
169        // Always increment this.
170        context.tx_progress += 1;
171    }
172
173    // Read data from RX FIFO.
174    while spi.read_status().rx_not_empty() {
175        let data = spi.read_data();
176        if context.rx_progress < read_len {
177            read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
178        }
179        // Always increment this.
180        context.rx_progress += 1;
181    }
182
183    isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
184}
185
186fn on_interrupt_transfer_in_place(
187    idx: usize,
188    context: &mut TransferContext,
189    spi: &mut super::regs::MmioSpi<'static>,
190    enabled_irqs: InterruptControl,
191) {
192    let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
193    let transfer_len = transfer_slice.len();
194    // Send data first to avoid overwriting data that still needs to be sent.
195    while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
196        spi.write_data(Data::new_with_raw_value(
197            transfer_slice[context.tx_progress] as u32,
198        ));
199        context.tx_progress += 1;
200    }
201    // Read data from RX FIFO.
202    while spi.read_status().rx_not_empty() {
203        let data = spi.read_data();
204        if context.rx_progress < transfer_len {
205            transfer_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
206            context.rx_progress += 1;
207        }
208    }
209
210    isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
211}
212
213/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
214/// and unfinished conditions.
215fn isr_finish_handler(
216    idx: usize,
217    spi: &mut super::regs::MmioSpi<'static>,
218    context: &mut TransferContext,
219    transfer_len: usize,
220    enabled: InterruptControl,
221) {
222    // Transfer finish condition.
223    if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
224        finish_transfer(spi, idx, context);
225        return;
226    }
227    // If the transfer is done, the context structure was already written back.
228    // Write back updated context structure.
229    critical_section::with(|cs| {
230        let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
231        *context_ref.borrow_mut() = *context;
232    });
233    unfinished_transfer(spi, transfer_len, context, enabled);
234}
235
236fn finish_transfer(
237    spi: &mut super::regs::MmioSpi<'static>,
238    idx: usize,
239    context: &mut TransferContext,
240) {
241    // Write back updated context structure.
242    critical_section::with(|cs| {
243        let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
244        *context_ref.borrow_mut() = *context;
245    });
246    // Clean up, restore clean state.
247    reset_trigger_levels(spi);
248    spi.write_fifo_clear(FifoClear::ALL);
249    // Interrupts were already disabled and cleared.
250    DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
251    WAKERS[idx].wake();
252}
253
254#[inline]
255fn unfinished_transfer(
256    spi: &mut super::regs::MmioSpi<'static>,
257    transfer_len: usize,
258    context: &TransferContext,
259    enabled_irqs: InterruptControl,
260) {
261    // Take 8 as a conservative value to make sure that the FIFO does not overflow even if there
262    // is a significant delay between the interrupt being triggered and the handler being executed.
263    let new_trig_level = core::cmp::min(8, transfer_len - context.rx_progress);
264    spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
265
266    // If TX was already enabled and the transfer is finished, stop enabling it. Otherwise, we can
267    // become stuck in an interrupt loop. In any other case, enable it. I am not fully sure
268    // why this is necessary and why we can not stop interrupts as soon as we have the full
269    // TX progress, but tests with ADCs have shown that not doing this causes timeouts.
270    let enable_tx = !(enabled_irqs.tx() && context.tx_progress == transfer_len);
271
272    // Re-enable interrupts with the new RX FIFO trigger level.
273    spi.write_interrupt_control(
274        InterruptControl::builder()
275            .with_tx(enable_tx)
276            .with_rx(true)
277            .with_rx_timeout(true)
278            .with_rx_overrun(true)
279            .build(),
280    );
281}
282
283#[inline]
284fn reset_trigger_levels(spi: &mut super::regs::MmioSpi<'static>) {
285    spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
286    spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
287}
288
289#[derive(Debug, Clone, Copy)]
290#[cfg_attr(feature = "defmt", derive(defmt::Format))]
291pub enum TransferType {
292    Read,
293    Write,
294    Transfer,
295    TransferInPlace,
296}
297
298#[derive(Default, Debug, Copy, Clone)]
299pub struct TransferContext {
300    transfer_type: Option<TransferType>,
301    tx_progress: usize,
302    rx_progress: usize,
303    tx_slice: RawBufSlice,
304    rx_slice: RawBufSliceMut,
305    rx_overrun: bool,
306}
307
308#[allow(clippy::new_without_default)]
309impl TransferContext {
310    pub const fn new() -> Self {
311        Self {
312            transfer_type: None,
313            tx_progress: 0,
314            rx_progress: 0,
315            tx_slice: RawBufSlice::new_nulled(),
316            rx_slice: RawBufSliceMut::new_nulled(),
317            rx_overrun: false,
318        }
319    }
320}
321
322pub struct SpiFuture<'spi> {
323    bank: super::Bank,
324    spi: &'spi mut super::Spi<u8>,
325    finished_regularly: core::cell::Cell<bool>,
326}
327
328impl<'spi> SpiFuture<'spi> {
329    fn new_for_read(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &mut [u8]) -> Self {
330        if words.is_empty() {
331            panic!("words length unexpectedly 0");
332        }
333        Self::generic_init_transfer(spi, bank);
334
335        let write_index = core::cmp::min(super::FIFO_DEPTH, words.len());
336        // Send dummy bytes.
337        (0..write_index).for_each(|_| {
338            spi.regs.write_data(Data::new_with_raw_value(0));
339        });
340
341        Self::set_triggers(spi, write_index, words.len());
342
343        critical_section::with(|cs| {
344            let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
345            let mut context = context_ref.borrow_mut();
346            context.transfer_type = Some(TransferType::Read);
347            unsafe {
348                context.rx_slice.set(words);
349            }
350            context.tx_slice.set_null();
351            context.tx_progress = write_index;
352            context.rx_progress = 0;
353            spi.regs.write_interrupt_clear(InterruptClear::ALL);
354            spi.regs.write_interrupt_control(
355                InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
356            );
357            spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
358        });
359        Self {
360            bank,
361            spi,
362            finished_regularly: core::cell::Cell::new(false),
363        }
364    }
365
366    fn new_for_write(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &[u8]) -> Self {
367        if words.is_empty() {
368            panic!("words length unexpectedly 0");
369        }
370        let index = bank as usize;
371        let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
372        critical_section::with(|cs| {
373            let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
374            let mut context = context_ref.borrow_mut();
375            context.transfer_type = Some(TransferType::Write);
376            unsafe {
377                context.tx_slice.set(words);
378            }
379            context.rx_slice.set_null();
380            context.tx_progress = write_index;
381            context.rx_progress = 0;
382            spi.regs.write_interrupt_clear(InterruptClear::ALL);
383            spi.regs.write_interrupt_control(
384                InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
385            );
386            spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
387        });
388        Self {
389            bank,
390            spi,
391            finished_regularly: core::cell::Cell::new(false),
392        }
393    }
394
395    fn new_for_transfer(
396        spi: &'spi mut super::Spi<u8>,
397        bank: super::Bank,
398        read: &mut [u8],
399        write: &[u8],
400    ) -> Self {
401        if read.is_empty() || write.is_empty() {
402            panic!("read or write buffer unexpectedly empty");
403        }
404        let index = bank as usize;
405        let full_write_len = core::cmp::max(read.len(), write.len());
406        let fifo_prefill = core::cmp::min(super::FIFO_DEPTH, full_write_len);
407
408        Self::generic_init_transfer(spi, bank);
409
410        for write_index in 0..fifo_prefill {
411            let value = write.get(write_index).copied().unwrap_or(0);
412            spi.regs.write_data(Data::new_with_raw_value(value as u32));
413        }
414
415        Self::set_triggers(spi, fifo_prefill, full_write_len);
416
417        critical_section::with(|cs| {
418            let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
419            let mut context = context_ref.borrow_mut();
420            context.transfer_type = Some(TransferType::Transfer);
421            unsafe {
422                context.tx_slice.set(write);
423                context.rx_slice.set(read);
424            }
425            context.tx_progress = fifo_prefill;
426            context.rx_progress = 0;
427            spi.regs.write_interrupt_clear(InterruptClear::ALL);
428            spi.regs.write_interrupt_control(
429                InterruptControl::ENABLE_ALL.with_tx(fifo_prefill > FIFO_DEPTH),
430            );
431            spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
432        });
433        Self {
434            bank,
435            spi,
436            finished_regularly: core::cell::Cell::new(false),
437        }
438    }
439
440    fn new_for_transfer_in_place(
441        spi: &'spi mut super::Spi<u8>,
442        bank: super::Bank,
443        words: &mut [u8],
444    ) -> Self {
445        if words.is_empty() {
446            panic!("read and write buffer unexpectedly empty");
447        }
448        let write_idx = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
449        critical_section::with(|cs| {
450            let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
451            let mut context = context_ref.borrow_mut();
452            context.transfer_type = Some(TransferType::TransferInPlace);
453            unsafe {
454                context.rx_slice.set(words);
455            }
456            context.tx_slice.set_null();
457            context.tx_progress = write_idx;
458            context.rx_progress = 0;
459            spi.regs.write_interrupt_clear(InterruptClear::ALL);
460            spi.regs.write_interrupt_control(
461                InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
462            );
463            spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
464        });
465        Self {
466            bank,
467            spi,
468            finished_regularly: core::cell::Cell::new(false),
469        }
470    }
471
472    fn generic_init_transfer(spi: &mut super::Spi<u8>, bank: super::Bank) {
473        let idx = bank as usize;
474        DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
475        spi.regs
476            .write_interrupt_control(InterruptControl::DISABLE_ALL);
477        spi.regs.write_fifo_clear(FifoClear::ALL);
478        spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
479    }
480
481    // Returns amount of bytes written to FIFO.
482    fn generic_init_transfer_write_transfer_in_place(
483        spi: &mut super::Spi<u8>,
484        bank: super::Bank,
485        write: &[u8],
486    ) -> usize {
487        Self::generic_init_transfer(spi, bank);
488
489        let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
490        (0..write_idx).for_each(|idx| {
491            spi.regs
492                .write_data(Data::new_with_raw_value(write[idx] as u32));
493        });
494
495        Self::set_triggers(spi, write_idx, write.len());
496        write_idx
497    }
498
499    fn set_triggers(spi: &mut super::Spi<u8>, fifo_prefill: usize, write_len: usize) {
500        spi.regs
501            .write_rx_fifo_trigger(TriggerLevel::new(u5::new(core::cmp::min(
502                fifo_prefill,
503                FIFO_DEPTH / 2,
504            ) as u8)));
505        // We want to re-fill the TX FIFO before it is completely empty if the full transfer size
506        // is larger than the FIFO depth. Otherwise, set it to 0. Not exactly sure what that does,
507        // but we do not enable interrupts anyway.
508        if write_len > super::FIFO_DEPTH {
509            spi.regs
510                .write_tx_fifo_trigger(TriggerLevel::new(u5::new(8)));
511        } else {
512            spi.regs
513                .write_tx_fifo_trigger(TriggerLevel::new(u5::new(0)));
514        }
515    }
516}
517
518impl<'spi> Future for SpiFuture<'spi> {
519    type Output = Result<(), RxOverrunError>;
520
521    fn poll(
522        self: core::pin::Pin<&mut Self>,
523        cx: &mut core::task::Context<'_>,
524    ) -> core::task::Poll<Self::Output> {
525        WAKERS[self.bank as usize].register(cx.waker());
526        if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
527            let rx_overrun = critical_section::with(|cs| {
528                let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
529                    .borrow(cs)
530                    .borrow_mut();
531                let overrun = ctx.rx_overrun;
532                *ctx = TransferContext::default();
533                overrun
534            });
535            self.finished_regularly.set(true);
536            if rx_overrun {
537                return core::task::Poll::Ready(Err(RxOverrunError));
538            }
539            return core::task::Poll::Ready(Ok(()));
540        }
541        core::task::Poll::Pending
542    }
543}
544
545impl<'spi> Drop for SpiFuture<'spi> {
546    fn drop(&mut self) {
547        if !self.finished_regularly.get() {
548            // It might be sufficient to disable and enable the SPI.. But this definitely
549            // ensures the SPI is fully reset.
550            self.spi.regs.write_interrupt_clear(InterruptClear::ALL);
551            self.spi
552                .regs
553                .write_interrupt_control(InterruptControl::DISABLE_ALL);
554            self.spi.regs.write_fifo_clear(FifoClear::ALL);
555        }
556    }
557}
558
559/// Asynchronous SPI driver.
560///
561/// This is the primary data structure used to perform non-blocking SPI operations.
562/// It implements the [embedded_hal_async::spi::SpiBus] as well.
563pub struct SpiAsync(pub super::Spi<u8>);
564
565impl SpiAsync {
566    pub fn new(
567        mut spi: super::Spi<u8>,
568        #[cfg(feature = "vor1x")] opt_irq_cfg: Option<crate::InterruptConfig>,
569    ) -> Self {
570        #[cfg(feature = "vor1x")]
571        if let Some(irq_cfg) = opt_irq_cfg {
572            spi.regs
573                .write_interrupt_control(InterruptControl::DISABLE_ALL);
574            spi.regs.write_interrupt_clear(InterruptClear::ALL);
575            if irq_cfg.route {
576                crate::enable_peripheral_clock(crate::PeripheralSelect::Irqsel);
577                unsafe { va108xx::Irqsel::steal() }
578                    .spi(spi.id as usize)
579                    .write(|w| unsafe { w.bits(irq_cfg.id as u32) });
580            }
581            if irq_cfg.enable_in_nvic {
582                // Safety: User has specifically configured this.
583                unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
584            }
585        }
586        // Disable blockmode for asynchronous mode.
587        spi.regs
588            .modify_ctrl1(|v| v.with_bm_stall(false).with_blockmode(false));
589        Self(spi)
590    }
591
592    fn read(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
593        if words.is_empty() {
594            return None;
595        }
596        let id = self.0.id;
597        Some(SpiFuture::new_for_read(&mut self.0, id, words))
598    }
599
600    fn write(&mut self, words: &[u8]) -> Option<SpiFuture<'_>> {
601        if words.is_empty() {
602            return None;
603        }
604        let id = self.0.id;
605        Some(SpiFuture::new_for_write(&mut self.0, id, words))
606    }
607
608    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Option<SpiFuture<'_>> {
609        if read.is_empty() || write.is_empty() {
610            return None;
611        }
612        let id = self.0.id;
613        Some(SpiFuture::new_for_transfer(&mut self.0, id, read, write))
614    }
615
616    fn transfer_in_place(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
617        if words.is_empty() {
618            return None;
619        }
620        let id = self.0.id;
621        Some(SpiFuture::new_for_transfer_in_place(&mut self.0, id, words))
622    }
623}
624
625impl embedded_hal_async::spi::ErrorType for SpiAsync {
626    type Error = RxOverrunError;
627}
628
629impl embedded_hal_async::spi::SpiBus for SpiAsync {
630    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
631        if words.is_empty() {
632            return Ok(());
633        }
634        self.read(words).unwrap().await
635    }
636
637    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
638        if words.is_empty() {
639            return Ok(());
640        }
641        self.write(words).unwrap().await
642    }
643
644    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
645        if read.is_empty() && write.is_empty() {
646            return Ok(());
647        }
648        self.transfer(read, write).unwrap().await
649    }
650
651    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
652        if words.is_empty() {
653            return Ok(());
654        }
655        self.transfer_in_place(words).unwrap().await
656    }
657
658    async fn flush(&mut self) -> Result<(), Self::Error> {
659        Ok(())
660    }
661}