use super::{
channel::{AnyChannel, Busy, CallbackStatus, Channel, ChannelId, InterruptFlags, Ready},
dma_controller::{ChId, TriggerAction, TriggerSource},
BlockTransferControl, DmacDescriptor, Error, Result, DESCRIPTOR_SECTION,
};
use crate::typelevel::{Is, Sealed};
use core::{ptr::null_mut, sync::atomic};
use modular_bitfield::prelude::*;
#[derive(Clone, Copy, BitfieldSpecifier)]
#[bits = 2]
pub enum BeatSize {
Byte = 0x00,
HalfWord = 0x01,
Word = 0x02,
}
pub unsafe trait Beat: Sealed {
const BEATSIZE: BeatSize;
}
macro_rules! impl_beat {
( $( ($Type:ty, $Size:ident) ),+ ) => {
$(
unsafe impl Beat for $Type {
const BEATSIZE: BeatSize = BeatSize::$Size;
}
)+
};
}
impl_beat!(
(u8, Byte),
(i8, Byte),
(u16, HalfWord),
(i16, HalfWord),
(u32, Word),
(i32, Word),
(f32, Word)
);
pub unsafe trait Buffer {
type Beat: Beat;
fn dma_ptr(&mut self) -> *mut Self::Beat;
fn incrementing(&self) -> bool;
fn buffer_len(&self) -> usize;
}
unsafe impl<T: Beat, const N: usize> Buffer for &mut [T; N] {
type Beat = T;
#[inline]
fn dma_ptr(&mut self) -> *mut Self::Beat {
let ptrs = self.as_mut_ptr_range();
if self.incrementing() {
ptrs.end
} else {
ptrs.start
}
}
#[inline]
fn incrementing(&self) -> bool {
N > 1
}
#[inline]
fn buffer_len(&self) -> usize {
N
}
}
unsafe impl<T: Beat> Buffer for &mut [T] {
type Beat = T;
#[inline]
fn dma_ptr(&mut self) -> *mut Self::Beat {
let ptrs = self.as_mut_ptr_range();
if self.incrementing() {
ptrs.end
} else {
ptrs.start
}
}
#[inline]
fn incrementing(&self) -> bool {
self.len() > 1
}
#[inline]
fn buffer_len(&self) -> usize {
self.len()
}
}
unsafe impl<T: Beat> Buffer for &mut T {
type Beat = T;
#[inline]
fn dma_ptr(&mut self) -> *mut Self::Beat {
*self as *mut T
}
#[inline]
fn incrementing(&self) -> bool {
false
}
#[inline]
fn buffer_len(&self) -> usize {
1
}
}
pub struct BufferPair<S, D = S>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
{
pub source: S,
pub destination: D,
}
pub trait AnyBufferPair: Sealed + Is<Type = SpecificBufferPair<Self>> {
type Src: Buffer;
type Dst: Buffer<Beat = BufferPairBeat<Self>>;
}
pub type SpecificBufferPair<C> = BufferPair<<C as AnyBufferPair>::Src, <C as AnyBufferPair>::Dst>;
pub type BufferPairSrc<B> = <B as AnyBufferPair>::Src;
pub type BufferPairDst<B> = <B as AnyBufferPair>::Dst;
pub type BufferPairBeat<B> = <BufferPairSrc<B> as Buffer>::Beat;
impl<S, D> Sealed for BufferPair<S, D>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
{
}
impl<S, D> AnyBufferPair for BufferPair<S, D>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
{
type Src = S;
type Dst = D;
}
impl<S, D> AsRef<Self> for BufferPair<S, D>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
{
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl<S, D> AsMut<Self> for BufferPair<S, D>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
{
#[inline]
fn as_mut(&mut self) -> &mut Self {
self
}
}
pub struct Transfer<Chan, Buf, W = ()>
where
Buf: AnyBufferPair,
Chan: AnyChannel,
{
chan: Chan,
buffers: Buf,
waker: Option<W>,
complete: bool,
}
impl<C, S, D> Transfer<C, BufferPair<S, D>>
where
S: Buffer + 'static,
D: Buffer<Beat = S::Beat> + 'static,
C: AnyChannel<Status = Ready>,
{
#[allow(clippy::new_ret_no_self)]
#[inline]
pub fn new(
chan: C,
source: S,
destination: D,
circular: bool,
) -> Result<Transfer<C, BufferPair<S, D>>> {
Self::check_buffer_pair(&source, &destination)?;
Ok(unsafe { Self::new_unchecked(chan, source, destination, circular) })
}
}
impl<S, D, C, W> Transfer<C, BufferPair<S, D>, W>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
C: AnyChannel,
{
#[inline]
fn check_buffer_pair(source: &S, destination: &D) -> Result<()> {
let src_len = source.buffer_len();
let dst_len = destination.buffer_len();
if src_len > 1 && dst_len > 1 && src_len != dst_len {
Err(Error::LengthMismatch)
} else {
Ok(())
}
}
#[inline]
unsafe fn fill_descriptor(source: &mut S, destination: &mut D, circular: bool) {
let id = <C as AnyChannel>::Id::USIZE;
let descaddr = if circular {
&mut DESCRIPTOR_SECTION[id] as *mut _
} else {
null_mut()
};
let src_ptr = source.dma_ptr();
let src_inc = source.incrementing();
let src_len = source.buffer_len();
let dst_ptr = destination.dma_ptr();
let dst_inc = destination.incrementing();
let dst_len = destination.buffer_len();
let length = core::cmp::max(src_len, dst_len);
let btctrl = BlockTransferControl::new()
.with_srcinc(src_inc)
.with_dstinc(dst_inc)
.with_beatsize(S::Beat::BEATSIZE)
.with_valid(true);
let xfer_descriptor = DmacDescriptor {
descaddr,
srcaddr: src_ptr as *mut _,
dstaddr: dst_ptr as *mut _,
btcnt: length as u16,
btctrl,
};
DESCRIPTOR_SECTION[id] = xfer_descriptor;
}
}
impl<C, S, D> Transfer<C, BufferPair<S, D>>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
C: AnyChannel<Status = Ready>,
{
#[inline]
pub unsafe fn new_unchecked(
chan: C,
mut source: S,
mut destination: D,
circular: bool,
) -> Transfer<C, BufferPair<S, D>> {
Self::fill_descriptor(&mut source, &mut destination, circular);
let buffers = BufferPair {
source,
destination,
};
Transfer {
buffers,
chan,
waker: None,
complete: false,
}
}
}
impl<C, S, D> Transfer<C, BufferPair<S, D>>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
C: AnyChannel<Status = Ready>,
{
#[inline]
pub fn with_waker<W: FnOnce(CallbackStatus) + 'static>(
self,
waker: W,
) -> Transfer<C, BufferPair<S, D>, W> {
Transfer {
buffers: self.buffers,
chan: self.chan,
complete: self.complete,
waker: Some(waker),
}
}
}
impl<C, S, D, W> Transfer<C, BufferPair<S, D>, W>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
C: AnyChannel<Status = Ready>,
{
#[inline]
pub fn begin(
mut self,
trig_src: TriggerSource,
trig_act: TriggerAction,
) -> Transfer<Channel<ChannelId<C>, Busy>, BufferPair<S, D>, W> {
self.complete = false;
atomic::fence(atomic::Ordering::Release); let chan = self.chan.into().start(trig_src, trig_act);
Transfer {
buffers: self.buffers,
chan,
waker: self.waker,
complete: self.complete,
}
}
}
impl<B, C, const N: usize> Transfer<C, BufferPair<&'static mut [B; N]>>
where
B: 'static + Beat,
C: AnyChannel<Status = Ready>,
{
#[inline]
pub fn new_from_arrays(
chan: C,
source: &'static mut [B; N],
destination: &'static mut [B; N],
circular: bool,
) -> Self {
unsafe { Self::new_unchecked(chan, source, destination, circular) }
}
}
impl<S, D, C, W> Transfer<C, BufferPair<S, D>, W>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
C: AnyChannel<Status = Busy>,
{
#[inline]
pub fn software_trigger(&mut self) {
self.chan.as_mut().software_trigger();
}
#[inline]
pub(crate) unsafe fn borrow_source(&mut self) -> &mut S {
&mut self.buffers.source
}
#[inline]
pub(crate) unsafe fn borrow_destination(&mut self) -> &mut D {
&mut self.buffers.destination
}
#[inline]
pub fn wait(mut self) -> (Channel<ChannelId<C>, Ready>, S, D) {
while !self.complete() {}
self.stop()
}
#[inline]
pub fn complete(&mut self) -> bool {
if !self.complete {
let chan = self.chan.as_mut();
let complete = chan.xfer_complete();
self.complete = complete;
}
self.complete
}
#[inline]
pub fn block_transfer_interrupt(&mut self) -> bool {
self.chan
.as_mut()
.check_and_clear_interrupts(InterruptFlags::new().with_tcmpl(true))
.tcmpl()
}
#[inline]
pub fn recycle(&mut self, mut source: S, mut destination: D) -> Result<(S, D)> {
Self::check_buffer_pair(&source, &destination)?;
if !self.complete() {
return Err(Error::InvalidState);
}
unsafe {
Self::fill_descriptor(&mut source, &mut destination, false);
}
let new_buffers = BufferPair {
source,
destination,
};
let old_buffers = core::mem::replace(&mut self.buffers, new_buffers);
self.chan.as_mut().restart();
Ok((old_buffers.source, old_buffers.destination))
}
#[inline]
pub fn recycle_source(&mut self, mut destination: D) -> Result<D> {
Self::check_buffer_pair(&self.buffers.source, &destination)?;
if !self.complete() {
return Err(Error::InvalidState);
}
unsafe {
Self::fill_descriptor(&mut self.buffers.source, &mut destination, false);
}
let old_destination = core::mem::replace(&mut self.buffers.destination, destination);
self.chan.as_mut().restart();
Ok(old_destination)
}
#[inline]
pub fn recycle_destination(&mut self, mut source: S) -> Result<S> {
Self::check_buffer_pair(&source, &self.buffers.destination)?;
if !self.complete() {
return Err(Error::InvalidState);
}
unsafe {
Self::fill_descriptor(&mut source, &mut self.buffers.destination, false);
}
let old_source = core::mem::replace(&mut self.buffers.source, source);
self.chan.as_mut().restart();
Ok(old_source)
}
#[inline]
pub fn stop(self) -> (Channel<ChannelId<C>, Ready>, S, D) {
let chan = self.chan.into().free();
atomic::fence(atomic::Ordering::Acquire);
(chan, self.buffers.source, self.buffers.destination)
}
}
impl<S, D, C, W> Transfer<C, BufferPair<S, D>, W>
where
S: Buffer,
D: Buffer<Beat = S::Beat>,
C: AnyChannel<Status = Busy>,
W: FnOnce(CallbackStatus) + 'static,
{
#[inline]
pub fn callback(&mut self) {
let status = self.chan.as_mut().callback();
if let CallbackStatus::TransferComplete = status {
self.complete = true;
}
if let Some(w) = self.waker.take() {
w(status)
}
}
}