Skip to content

Commit ec73c4e

Browse files
committed
Tentative TxDma and RxTxDma for SPI
1 parent 12bf98d commit ec73c4e

File tree

2 files changed

+369
-45
lines changed

2 files changed

+369
-45
lines changed

src/dma.rs

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,19 @@ where
351351
}
352352
}
353353

354+
impl<BUFFER, PAYLOAD> Transfer<RW, BUFFER, PAYLOAD>
355+
where
356+
PAYLOAD: TransferPayload,
357+
{
358+
pub(crate) fn rw(buffer: BUFFER, payload: PAYLOAD) -> Self {
359+
Transfer {
360+
_mode: PhantomData,
361+
buffer,
362+
payload,
363+
}
364+
}
365+
}
366+
354367
impl<MODE, BUFFER, PAYLOAD> Drop for Transfer<MODE, BUFFER, PAYLOAD>
355368
where
356369
PAYLOAD: TransferPayload,
@@ -367,6 +380,86 @@ pub struct R;
367380
/// Write transfer
368381
pub struct W;
369382

383+
/// Read/Write transfer
384+
pub struct RW;
385+
386+
macro_rules! for_all_pairs {
387+
($mac:ident: $($x:ident)*) => {
388+
// Duplicate the list
389+
for_all_pairs!(@inner $mac: $($x)*; $($x)*);
390+
};
391+
392+
// The end of iteration: we exhausted the list
393+
(@inner $mac:ident: ; $($x:ident)*) => {};
394+
395+
// The head/tail recursion: pick the first element of the first list
396+
// and recursively do it for the tail.
397+
(@inner $mac:ident: $head:ident $($tail:ident)*; $($x:ident)*) => {
398+
$(
399+
$mac!($head $x);
400+
)*
401+
for_all_pairs!(@inner $mac: $($tail)*; $($x)*);
402+
};
403+
}
404+
405+
macro_rules! rx_tx_channel_mapping {
406+
($CH_A:ident $CH_B:ident) => {
407+
impl<BUFFER, PAYLOAD> Transfer<RW, BUFFER, RxTxDma<PAYLOAD, $CH_A, $CH_B>>
408+
where
409+
RxTxDma<PAYLOAD, $CH_A, $CH_B>: TransferPayload,
410+
{
411+
pub fn is_done(&self) -> bool {
412+
!self.payload.rx_channel.in_progress() && !self.payload.tx_channel.in_progress()
413+
}
414+
415+
pub fn wait(mut self) -> (BUFFER, RxTxDma<PAYLOAD, $CH_A, $CH_B>) {
416+
// XXX should we check for transfer errors here?
417+
// The manual says "A DMA transfer error can be generated by reading
418+
// from or writing to a reserved address space". I think it's impossible
419+
// to get to that state with our type safe API and *safe* Rust.
420+
while !self.is_done() {}
421+
422+
self.payload.stop();
423+
424+
// TODO can we weaken this compiler barrier?
425+
// NOTE(compiler_fence) operations on `buffer` should not be reordered
426+
// before the previous statement, which marks the DMA transfer as done
427+
atomic::compiler_fence(Ordering::SeqCst);
428+
429+
// `Transfer` needs to have a `Drop` implementation, because we accept
430+
// managed buffers that can free their memory on drop. Because of that
431+
// we can't move out of the `Transfer`'s fields, so we use `ptr::read`
432+
// and `mem::forget`.
433+
//
434+
// NOTE(unsafe) There is no panic branch between getting the resources
435+
// and forgetting `self`.
436+
unsafe {
437+
let buffer = ptr::read(&self.buffer);
438+
let payload = ptr::read(&self.payload);
439+
core::mem::forget(self);
440+
(buffer, payload)
441+
}
442+
}
443+
}
444+
445+
impl<BUFFER, PAYLOAD> Transfer<RW, BUFFER, RxTxDma<PAYLOAD, $CH_A, $CH_B>>
446+
where
447+
RxTxDma<PAYLOAD, $CH_A, $CH_B>: TransferPayload,
448+
{
449+
pub fn peek<T>(&self) -> &[T]
450+
where
451+
BUFFER: AsRef<[T]>,
452+
{
453+
let pending = self.payload.rx_channel.get_cndtr() as usize;
454+
455+
let capacity = self.buffer.as_ref().len();
456+
457+
&self.buffer.as_ref()[..(capacity - pending)]
458+
}
459+
}
460+
};
461+
}
462+
370463
macro_rules! dma {
371464
($($DMAX:ident: ($dmaX:ident, $dmaXen:ident, $dmaXrst:ident, {
372465
$($CX:ident: (
@@ -395,12 +488,14 @@ macro_rules! dma {
395488
use core::ptr;
396489
use stable_deref_trait::StableDeref;
397490

398-
use crate::dma::{CircBuffer, FrameReader, FrameSender, DMAFrame, DmaExt, Error, Event, Half, Transfer, W, R, RxDma, TxDma, TransferPayload};
491+
use crate::dma::{CircBuffer, FrameReader, FrameSender, DMAFrame, DmaExt, Error, Event, Half, Transfer, W, R, RW, RxDma, RxTxDma, TxDma, TransferPayload};
399492
use crate::rcc::AHB1;
400493

401494
#[allow(clippy::manual_non_exhaustive)]
402495
pub struct Channels((), $(pub $CX),+);
403496

497+
for_all_pairs!(rx_tx_channel_mapping: $($CX)+);
498+
404499
$(
405500
/// A singleton that represents a single DMAx channel (channel X in this case)
406501
///
@@ -1096,8 +1191,8 @@ pub struct TxDma<PAYLOAD, TXCH> {
10961191
/// DMA Receiver/Transmitter
10971192
pub struct RxTxDma<PAYLOAD, RXCH, TXCH> {
10981193
pub(crate) payload: PAYLOAD,
1099-
pub rxchannel: RXCH,
1100-
pub txchannel: TXCH,
1194+
pub rx_channel: RXCH,
1195+
pub tx_channel: TXCH,
11011196
}
11021197

11031198
pub trait Receive {
@@ -1110,6 +1205,12 @@ pub trait Transmit {
11101205
type ReceivedWord;
11111206
}
11121207

1208+
pub trait ReceiveTransmit {
1209+
type RxChannel;
1210+
type TxChannel;
1211+
type TransferedWord;
1212+
}
1213+
11131214
/// Trait for circular DMA readings from peripheral to memory.
11141215
pub trait CircReadDma<B, RS>: Receive
11151216
where
@@ -1137,3 +1238,12 @@ where
11371238
{
11381239
fn write(self, buffer: B) -> Transfer<R, B, Self>;
11391240
}
1241+
1242+
/// Trait for DMA simultaneously writing and reading between memory and peripheral.
1243+
pub trait TransferDma<B, TS>: ReceiveTransmit
1244+
where
1245+
B: StaticWriteBuffer<Word = TS>,
1246+
Self: core::marker::Sized + TransferPayload,
1247+
{
1248+
fn transfer(self, buffer: B) -> Transfer<RW, B, Self>;
1249+
}

0 commit comments

Comments
 (0)