use core::{marker::PhantomData, ops::Range};
use atsamd_hal_macros::hal_macro_helper;
use crate::dmac::{
self, Beat, Buffer, Transfer, TriggerAction,
channel::{AnyChannel, Busy, Channel, InterruptFlags, Ready},
sram::DmacDescriptor,
transfer::BufferPair,
};
use crate::sercom::{
Sercom,
uart::{self, Uart},
};
#[doc(hidden)]
pub(crate) struct SharedSliceBuffer<'a, T: Beat> {
ptrs: Range<*mut T>,
_lifetime: PhantomData<&'a T>,
}
impl<'a, T: Beat> SharedSliceBuffer<'a, T> {
#[inline]
pub(in super::super) fn from_slice(slice: &'a [T]) -> Self {
unsafe { Self::from_slice_unchecked(slice) }
}
#[inline]
pub(in super::super) unsafe fn from_slice_unchecked(slice: &[T]) -> Self {
let ptrs = slice.as_ptr_range();
let ptrs = Range {
start: ptrs.start.cast_mut(),
end: ptrs.end.cast_mut(),
};
Self {
ptrs,
_lifetime: PhantomData,
}
}
}
unsafe impl<T: Beat> Buffer for SharedSliceBuffer<'_, T> {
type Beat = T;
#[inline]
fn dma_ptr(&mut self) -> *mut Self::Beat {
if self.incrementing() {
self.ptrs.end
} else {
self.ptrs.start
}
}
#[inline]
fn incrementing(&self) -> bool {
self.buffer_len() > 1
}
#[inline]
fn buffer_len(&self) -> usize {
self.ptrs.end as usize - self.ptrs.start as usize
}
}
pub(super) struct SinkSourceBuffer<'a, T: Beat> {
word: &'a mut T,
length: usize,
}
impl<'a, T: Beat> SinkSourceBuffer<'a, T> {
pub(super) fn new(word: &'a mut T, length: usize) -> Self {
Self { word, length }
}
}
unsafe impl<T: Beat> Buffer for SinkSourceBuffer<'_, T> {
type Beat = T;
#[inline]
fn incrementing(&self) -> bool {
false
}
#[inline]
fn buffer_len(&self) -> usize {
self.length
}
#[inline]
fn dma_ptr(&mut self) -> *mut Self::Beat {
self.word as *mut _
}
}
#[doc(hidden)]
#[derive(Clone)]
pub(crate) struct SercomPtr<T: Beat>(pub(in super::super) *mut T);
unsafe impl<T: Beat> Buffer for SercomPtr<T> {
type Beat = T;
#[inline]
fn dma_ptr(&mut self) -> *mut Self::Beat {
self.0
}
#[inline]
fn incrementing(&self) -> bool {
false
}
#[inline]
fn buffer_len(&self) -> usize {
1
}
}
unsafe impl<C, D> Buffer for Uart<C, D>
where
C: uart::ValidConfig,
C::Word: Beat,
D: uart::Capability,
{
type Beat = C::Word;
#[inline]
fn dma_ptr(&mut self) -> *mut Self::Beat {
self.data_ptr()
}
#[inline]
fn incrementing(&self) -> bool {
false
}
#[inline]
fn buffer_len(&self) -> usize {
1
}
}
impl<C, D> Uart<C, D>
where
Self: Buffer<Beat = C::Word>,
C: uart::ValidConfig,
D: uart::Receive,
{
#[inline]
#[hal_macro_helper]
pub fn receive_with_dma<Ch, B>(
self,
buf: B,
mut channel: Ch,
) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<Self, B>>
where
Ch: AnyChannel<Status = Ready>,
B: Buffer<Beat = C::Word> + 'static,
{
channel
.as_mut()
.enable_interrupts(InterruptFlags::new().with_tcmpl(true));
#[hal_cfg("sercom0-d5x")]
let trigger_action = TriggerAction::Burst;
#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
let trigger_action = TriggerAction::Beat;
let xfer = unsafe { dmac::Transfer::new_unchecked(channel, self, buf, false) };
xfer.begin(C::Sercom::DMA_RX_TRIGGER, trigger_action)
}
}
impl<C, D> Uart<C, D>
where
Self: Buffer<Beat = C::Word>,
C: uart::ValidConfig,
D: uart::Transmit,
{
#[inline]
#[hal_macro_helper]
pub fn send_with_dma<Ch, B>(
self,
buf: B,
mut channel: Ch,
) -> Transfer<Channel<Ch::Id, Busy>, BufferPair<B, Self>>
where
Ch: AnyChannel<Status = Ready>,
B: Buffer<Beat = C::Word> + 'static,
{
channel
.as_mut()
.enable_interrupts(InterruptFlags::new().with_tcmpl(true));
#[hal_cfg("sercom0-d5x")]
let trigger_action = TriggerAction::Burst;
#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
let trigger_action = TriggerAction::Beat;
let xfer = unsafe { dmac::Transfer::new_unchecked(channel, buf, self, false) };
xfer.begin(C::Sercom::DMA_TX_TRIGGER, trigger_action)
}
}
#[hal_macro_helper]
pub(super) unsafe fn read_dma<T, B, S>(
channel: &mut impl AnyChannel<Status = Ready>,
sercom_ptr: SercomPtr<T>,
buf: &mut B,
) where
T: Beat,
B: Buffer<Beat = T>,
S: Sercom,
{
unsafe {
read_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None);
}
}
#[hal_macro_helper]
pub(super) unsafe fn read_dma_linked<T, B, S>(
channel: &mut impl AnyChannel<Status = Ready>,
mut sercom_ptr: SercomPtr<T>,
buf: &mut B,
next: Option<&mut DmacDescriptor>,
) where
T: Beat,
B: Buffer<Beat = T>,
S: Sercom,
{
#[hal_cfg("dmac-d5x")]
let trigger_action = TriggerAction::Burst;
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
let trigger_action = TriggerAction::Beat;
unsafe {
channel.as_mut().transfer_unchecked(
&mut sercom_ptr,
buf,
S::DMA_RX_TRIGGER,
trigger_action,
next,
);
}
}
#[hal_macro_helper]
pub(super) unsafe fn write_dma<T, B, S>(
channel: &mut impl AnyChannel<Status = Ready>,
sercom_ptr: SercomPtr<T>,
buf: &mut B,
) where
T: Beat,
B: Buffer<Beat = T>,
S: Sercom,
{
unsafe {
write_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None);
}
}
#[hal_macro_helper]
pub(super) unsafe fn write_dma_linked<T, B, S>(
channel: &mut impl AnyChannel<Status = Ready>,
mut sercom_ptr: SercomPtr<T>,
buf: &mut B,
next: Option<&mut DmacDescriptor>,
) where
T: Beat,
B: Buffer<Beat = T>,
S: Sercom,
{
#[hal_cfg("dmac-d5x")]
let trigger_action = TriggerAction::Burst;
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
let trigger_action = TriggerAction::Beat;
unsafe {
channel.as_mut().transfer_unchecked(
buf,
&mut sercom_ptr,
S::DMA_TX_TRIGGER,
trigger_action,
next,
);
}
}
#[cfg(feature = "async")]
pub(crate) mod async_dma {
use dmac::{Error, ReadyFuture};
use super::*;
#[inline]
pub(in super::super) async fn read_dma<T, B, S>(
channel: &mut impl AnyChannel<Status = ReadyFuture>,
sercom_ptr: SercomPtr<T>,
buf: &mut B,
) -> Result<(), Error>
where
B: Buffer<Beat = T>,
T: Beat,
S: Sercom,
{
unsafe { read_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None).await }
}
#[inline]
#[hal_macro_helper]
pub(in super::super) async unsafe fn read_dma_linked<T, B, S>(
channel: &mut impl AnyChannel<Status = ReadyFuture>,
mut sercom_ptr: SercomPtr<T>,
buf: &mut B,
next: Option<&mut DmacDescriptor>,
) -> Result<(), Error>
where
T: Beat,
B: Buffer<Beat = T>,
S: Sercom,
{
#[hal_cfg("dmac-d5x")]
let trigger_action = TriggerAction::Burst;
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
let trigger_action = TriggerAction::Beat;
unsafe {
channel
.as_mut()
.transfer_future_linked(
&mut sercom_ptr,
buf,
S::DMA_RX_TRIGGER,
trigger_action,
next,
)
.await
}
}
#[inline]
pub(in super::super) async fn write_dma<T, B, S>(
channel: &mut impl AnyChannel<Status = ReadyFuture>,
sercom_ptr: SercomPtr<T>,
buf: &mut B,
) -> Result<(), Error>
where
B: Buffer<Beat = T>,
T: Beat,
S: Sercom,
{
unsafe { write_dma_linked::<_, _, S>(channel, sercom_ptr, buf, None).await }
}
#[inline]
#[hal_macro_helper]
pub(in super::super) async unsafe fn write_dma_linked<T, B, S>(
channel: &mut impl AnyChannel<Status = ReadyFuture>,
mut sercom_ptr: SercomPtr<T>,
buf: &mut B,
next: Option<&mut DmacDescriptor>,
) -> Result<(), Error>
where
B: Buffer<Beat = T>,
T: Beat,
S: Sercom,
{
#[hal_cfg("dmac-d5x")]
let trigger_action = TriggerAction::Burst;
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
let trigger_action = TriggerAction::Beat;
unsafe {
channel
.as_mut()
.transfer_future_linked(
buf,
&mut sercom_ptr,
S::DMA_TX_TRIGGER,
trigger_action,
next,
)
.await
}
}
}