use core::marker::PhantomData;
use core::sync::atomic::{self, compiler_fence, Ordering};
use core::{mem, ptr};
use embedded_dma::{ReadBuffer, WriteBuffer};
pub struct R;
pub struct W;
pub struct RxDma<PAYLOAD> {
pub(crate) payload: PAYLOAD,
}
pub struct TxDma<PAYLOAD> {
pub(crate) payload: PAYLOAD,
}
pub struct RxTxDma<PAYLOAD> {
pub(crate) payload: PAYLOAD,
}
pub trait Receive {
type TransmittedWord;
}
pub trait Transmit {
type ReceivedWord;
}
pub trait ReadDma<B, RS>: Receive
where
B: WriteBuffer<Word = RS>,
Self: core::marker::Sized + TransferPayload,
{
fn read(self, buffer: B) -> Transfer<W, B, Self>;
}
pub trait ReadDmaPaused<B, RS>: Receive
where
B: WriteBuffer<Word = RS>,
Self: core::marker::Sized + TransferPayload,
{
fn read_paused(self, buffer: B) -> Transfer<W, B, Self>;
}
pub trait WriteDma<B, TS>: Transmit
where
B: ReadBuffer<Word = TS>,
Self: core::marker::Sized + TransferPayload,
{
fn write(self, buffer: B) -> Transfer<R, B, Self>;
}
pub trait ReadWriteDma<RXB, TXB, TS>: Transmit + Receive
where
RXB: WriteBuffer<Word = TS>,
TXB: ReadBuffer<Word = TS>,
Self: core::marker::Sized + TransferPayload,
{
fn read_write(self, rx_buffer: RXB, tx_buffer: TXB) -> Transfer<W, (RXB, TXB), Self>;
}
pub trait ReadWriteDmaLen<RXB, TXB, TS>: Transmit + Receive
where
RXB: WriteBuffer<Word = TS>,
TXB: ReadBuffer<Word = TS>,
Self: core::marker::Sized + TransferPayload,
{
fn read_write_len(
self,
rx_buffer: RXB,
rx_buf_len: usize,
tx_buffer: TXB,
tx_buf_len: usize,
) -> Transfer<W, (RXB, TXB), Self>;
}
pub trait TransferPayload {
fn start(&mut self);
fn stop(&mut self);
fn in_progress(&self) -> bool;
}
pub struct Transfer<MODE, BUFFER, PAYLOAD>
where
PAYLOAD: TransferPayload,
{
_mode: PhantomData<MODE>,
buffer: BUFFER,
payload: PAYLOAD,
}
impl<BUFFER, PAYLOAD> Transfer<R, BUFFER, PAYLOAD>
where
PAYLOAD: TransferPayload,
{
pub(crate) fn r(buffer: BUFFER, payload: PAYLOAD) -> Self {
Transfer {
_mode: PhantomData,
buffer,
payload,
}
}
}
impl<BUFFER, PAYLOAD> Transfer<W, BUFFER, PAYLOAD>
where
PAYLOAD: TransferPayload,
{
pub(crate) fn w(buffer: BUFFER, payload: PAYLOAD) -> Self {
Transfer {
_mode: PhantomData,
buffer,
payload,
}
}
}
impl<MODE, BUFFER, PAYLOAD> Drop for Transfer<MODE, BUFFER, PAYLOAD>
where
PAYLOAD: TransferPayload,
{
fn drop(&mut self) {
self.payload.stop();
compiler_fence(Ordering::SeqCst);
}
}
macro_rules! pdc_transfer {
(
$DmaType:ident
) => {
impl<BUFFER, PAYLOAD, MODE> Transfer<MODE, BUFFER, $DmaType<PAYLOAD>>
where
$DmaType<PAYLOAD>: TransferPayload,
{
pub fn is_done(&self) -> bool {
!self.payload.in_progress()
}
pub fn wait(mut self) -> (BUFFER, $DmaType<PAYLOAD>) {
while !self.is_done() {}
atomic::compiler_fence(Ordering::Acquire);
self.payload.stop();
unsafe {
ptr::read_volatile(&0);
}
atomic::compiler_fence(Ordering::Acquire);
unsafe {
let buffer = ptr::read(&self.buffer);
let payload = ptr::read(&self.payload);
mem::forget(self);
(buffer, payload)
}
}
pub fn pause(&mut self) {
self.payload.stop();
}
pub fn resume(&mut self) {
self.payload.start();
}
}
};
}
pdc_transfer!(RxDma);
pdc_transfer!(TxDma);
pdc_transfer!(RxTxDma);
macro_rules! pdc_rx {
(
$Periph:ident: $periph:ident, $isr:ident
) => {
impl $Periph {
pub fn set_receive_address(&mut self, address: u32) {
self.$periph
.rpr
.write(|w| unsafe { w.rxptr().bits(address) });
}
pub fn set_receive_counter(&mut self, count: u16) {
self.$periph.rcr.write(|w| unsafe { w.rxctr().bits(count) });
}
pub fn set_receive_next_address(&mut self, address: u32) {
self.$periph
.rnpr
.write(|w| unsafe { w.rxnptr().bits(address) });
}
pub fn set_receive_next_counter(&mut self, count: u16) {
self.$periph
.rncr
.write(|w| unsafe { w.rxnctr().bits(count) });
}
pub fn start_rx_pdc(&mut self) {
unsafe { self.$periph.ptcr.write_with_zero(|w| w.rxten().set_bit()) };
}
pub fn stop_rx_pdc(&mut self) {
unsafe { self.$periph.ptcr.write_with_zero(|w| w.rxtdis().set_bit()) };
}
pub fn active_rx_pdc(&self) -> bool {
self.$periph.ptsr.read().rxten().bit()
}
pub fn rx_in_progress(&self) -> bool {
!self.$periph.$isr.read().rxbuff().bit()
}
pub fn enable_endrx_interrupt(&mut self) {
unsafe { self.$periph.ier.write_with_zero(|w| w.endrx().set_bit()) };
}
pub fn disable_endrx_interrupt(&mut self) {
unsafe { self.$periph.idr.write_with_zero(|w| w.endrx().set_bit()) };
}
pub fn enable_rxbuff_interrupt(&mut self) {
unsafe { self.$periph.ier.write_with_zero(|w| w.rxbuff().set_bit()) };
}
pub fn disable_rxbuff_interrupt(&mut self) {
unsafe { self.$periph.idr.write_with_zero(|w| w.rxbuff().set_bit()) };
}
}
};
}
pub(crate) use pdc_rx;
macro_rules! pdc_tx {
(
$Periph:ident: $periph:ident, $isr:ident
) => {
impl $Periph {
pub fn set_transmit_address(&mut self, address: u32) {
self.$periph
.tpr
.write(|w| unsafe { w.txptr().bits(address) });
}
pub fn set_transmit_counter(&mut self, count: u16) {
self.$periph.tcr.write(|w| unsafe { w.txctr().bits(count) });
}
pub fn set_transmit_next_address(&mut self, address: u32) {
self.$periph
.tnpr
.write(|w| unsafe { w.txnptr().bits(address) });
}
pub fn set_transmit_next_counter(&mut self, count: u16) {
self.$periph
.tncr
.write(|w| unsafe { w.txnctr().bits(count) });
}
pub fn start_tx_pdc(&mut self) {
unsafe {
self.$periph.ptcr.write_with_zero(|w| w.txten().set_bit());
}
}
pub fn stop_tx_pdc(&mut self) {
unsafe {
self.$periph.ptcr.write_with_zero(|w| w.txtdis().set_bit());
}
}
pub fn active_tx_pdc(&self) -> bool {
self.$periph.ptsr.read().txten().bit()
}
pub fn tx_in_progress(&self) -> bool {
!self.$periph.$isr.read().txbufe().bit()
}
pub fn enable_endtx_interrupt(&mut self) {
unsafe {
self.$periph.ier.write_with_zero(|w| w.endtx().set_bit());
}
}
pub fn disable_endtx_interrupt(&mut self) {
unsafe {
self.$periph.idr.write_with_zero(|w| w.endtx().set_bit());
}
}
pub fn enable_txbufe_interrupt(&mut self) {
unsafe {
self.$periph.ier.write_with_zero(|w| w.txbufe().set_bit());
}
}
pub fn disable_txbufe_interrupt(&mut self) {
unsafe {
self.$periph.idr.write_with_zero(|w| w.txbufe().set_bit());
}
}
}
};
}
pub(crate) use pdc_tx;
macro_rules! pdc_rxtx {
(
$Periph:ident: $periph:ident
) => {
impl $Periph {
pub fn start_rxtx_pdc(&mut self) {
unsafe {
self.$periph
.ptcr
.write_with_zero(|w| w.txten().set_bit().rxten().set_bit());
}
}
pub fn stop_rxtx_pdc(&mut self) {
unsafe {
self.$periph
.ptcr
.write_with_zero(|w| w.txtdis().set_bit().rxtdis().set_bit());
}
}
}
};
}
pub(crate) use pdc_rxtx;