use core::fmt;
use crate::target_device::DMAC;
use crate::{TriggerSource, TriggerAction, Priority};
#[cfg(feature = "samd5x")]
use crate::{BurstLength, FifoThreshold};
use crate::descriptors::{TransferDescriptor};
#[cfg(feature = "samd5x")]
macro_rules! channel_reg {
(@arm $reg:ident, $n:literal) => {
paste::expr! { (&*DMAC::ptr()).[<$reg $n>] }
};
($reg:ident, $n:expr) => {
match $n {
0 => channel_reg!(@arm $reg, 0),
1 => channel_reg!(@arm $reg, 1),
2 => channel_reg!(@arm $reg, 2),
3 => channel_reg!(@arm $reg, 3),
4 => channel_reg!(@arm $reg, 4),
5 => channel_reg!(@arm $reg, 5),
6 => channel_reg!(@arm $reg, 6),
7 => channel_reg!(@arm $reg, 7),
8 => channel_reg!(@arm $reg, 8),
9 => channel_reg!(@arm $reg, 9),
10 => channel_reg!(@arm $reg, 10),
11 => channel_reg!(@arm $reg, 11),
12 => channel_reg!(@arm $reg, 12),
13 => channel_reg!(@arm $reg, 13),
14 => channel_reg!(@arm $reg, 14),
15 => channel_reg!(@arm $reg, 15),
16 => channel_reg!(@arm $reg, 16),
17 => channel_reg!(@arm $reg, 17),
18 => channel_reg!(@arm $reg, 18),
19 => channel_reg!(@arm $reg, 19),
20 => channel_reg!(@arm $reg, 20),
21 => channel_reg!(@arm $reg, 21),
22 => channel_reg!(@arm $reg, 22),
23 => channel_reg!(@arm $reg, 23),
24 => channel_reg!(@arm $reg, 24),
25 => channel_reg!(@arm $reg, 25),
26 => channel_reg!(@arm $reg, 26),
27 => channel_reg!(@arm $reg, 27),
28 => channel_reg!(@arm $reg, 28),
29 => channel_reg!(@arm $reg, 29),
30 => channel_reg!(@arm $reg, 30),
31 => channel_reg!(@arm $reg, 31),
_ => unreachable!()
}
};
}
#[cfg(feature = "samd21")]
macro_rules! channel_reg {
($reg:ident, $n:expr) => {
unsafe {
(&*DMAC::ptr()).chid.write(|w| w.id().bits($n));
&(&*DMAC::ptr()).$reg
}
};
}
#[derive(Debug)]
pub enum TransactionError {
InvalidDescriptor,
TransferError,
CRCError,
}
impl fmt::Display for TransactionError {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
use self::TransactionError::*;
match self {
InvalidDescriptor => write!(w, "An invalid descriptor was fetched from memory."),
TransferError => write!(w, "A bus error was detected during a beat transfer."),
CRCError => write!(w, "The CRC module detected data corruption.")
}
}
}
pub enum WaitResult {
Done,
Suspended,
Ongoing,
}
pub struct Channel {
id: u8,
first_desc: *mut TransferDescriptor,
write_back: *mut TransferDescriptor,
}
impl Channel {
pub(crate) fn new(id: u8, first_desc: *mut TransferDescriptor,
write_back: *mut TransferDescriptor) -> Channel
{
Channel {
id,
first_desc,
write_back,
}
}
pub fn id(&self) -> u8 {
self.id
}
#[cfg(feature = "samd5x")]
pub fn set_run_standby(&mut self, run_standby: bool) {
channel_reg!(chctrla, self.id).modify(|_, w| w.runstdby().bit(run_standby));
}
#[cfg(feature = "samd5x")]
pub fn get_run_standby(&self) -> bool {
channel_reg!(chctrla, self.id).read().runstdby().bit()
}
#[cfg(feature = "samd5x")]
pub fn set_burst_length(&mut self, burst_len: BurstLength) {
channel_reg!(chctrla, self.id).modify(|_, w| w.burstlen().bits(burst_len as u8));
}
#[cfg(feature = "samd5x")]
pub fn get_burst_length(&self) -> BurstLength {
channel_reg!(chctrla, self.id).read().burstlen().variant().into()
}
pub fn set_trigger_action(&mut self, trig_act: TriggerAction) {
#[cfg(feature = "samd5x")]
let reg = channel_reg!(chctrla, self.id);
#[cfg(feature = "samd21")]
let reg = channel_reg!(chctrlb, self.id);
reg.modify(|_, w| unsafe { w.trigact().bits(trig_act as u8) });
}
pub fn get_trigger_action(&self) -> TriggerAction {
#[cfg(feature = "samd5x")]
let reg = channel_reg!(chctrla, self.id);
#[cfg(feature = "samd21")]
let reg = channel_reg!(chctrlb, self.id);
reg.read().trigact().variant().into()
}
#[cfg(feature = "samd5x")]
pub fn set_fifo_threshold(&mut self, fifo_threshold: FifoThreshold) {
channel_reg!(chctrla, self.id).modify(|_, w| w.threshold().bits(fifo_threshold as u8));
}
#[cfg(feature = "samd5x")]
pub fn get_fifi_threshold(&self) -> FifoThreshold {
channel_reg!(chctrla, self.id).read().threshold().variant().into()
}
pub fn set_source(&mut self, source: TriggerSource) {
#[cfg(feature = "samd5x")]
let reg = channel_reg!(chctrla, self.id);
#[cfg(feature = "samd21")]
let reg = channel_reg!(chctrlb, self.id);
reg.modify(|_, w| unsafe { w.trigsrc().bits(source as u8)});
}
pub fn get_source(&self) -> TriggerSource {
#[cfg(feature = "samd5x")]
let reg = channel_reg!(chctrla, self.id);
#[cfg(feature = "samd21")]
let reg = channel_reg!(chctrlb, self.id);
reg.read().trigsrc().variant().into()
}
pub fn set_priority(&mut self, priority: Priority) {
#[cfg(feature = "samd5x")]
channel_reg!(chprilvl, self.id).write(|w| unsafe { w.prilvl().bits(priority as u8) });
#[cfg(feature = "samd21")]
channel_reg!(chctrlb, self.id).modify(|_, w| w.lvl().bits(priority as u8))
}
pub fn get_priority(&self) -> Priority {
#[cfg(feature = "samd5x")]
return channel_reg!(chprilvl, self.id).read().prilvl().variant().into();
#[cfg(feature = "samd21")]
return channel_reg!(chctrlb, self.id).read().lvl().variant().into();
}
pub fn get_first_descriptor(&self) -> &mut TransferDescriptor {
unsafe { &mut *self.first_desc }
}
pub fn get_writeback_descriptor(&mut self) -> *mut TransferDescriptor {
self.write_back
}
pub fn enable(&self) {
channel_reg!(chctrla, self.id).modify(|_, w| w.enable().set_bit());
}
pub fn reset(&mut self) {
channel_reg!(chctrla, self.id).modify(|_, w| w.swrst().set_bit());
}
pub fn trigger(&mut self) {
unsafe {&*DMAC::ptr()}.swtrigctrl.modify(|r, w| unsafe {
w.bits(r.bits() | (1 << self.id))
})
}
pub fn suspend(&self) -> bool {
let mut ongoing = true;
channel_reg!(chctrlb, self.id).modify(|r, w| {
if r.cmd().is_noact() {
w.cmd().suspend();
ongoing = false;
}
w
});
ongoing
}
pub fn resume(&self) -> bool {
let mut ongoing = true;
channel_reg!(chctrlb, self.id).modify(|r, w| {
if r.cmd().is_noact() {
w.cmd().resume();
ongoing = false;
}
w
});
ongoing
}
pub fn disable(&mut self) {
channel_reg!(chctrla, self.id).modify(|_, w| w.enable().clear_bit());
}
pub fn is_pending(&self) -> bool {
channel_reg!(chstatus, self.id).read().pend().bit()
}
pub fn is_busy(&self) -> bool {
channel_reg!(chstatus, self.id).read().busy().bit()
}
pub fn poll_status(&self) -> Result<WaitResult, TransactionError> {
let intflag = channel_reg!(chintflag, self.id).read();
let status = channel_reg!(chstatus, self.id).read();
channel_reg!(chintflag, self.id).reset();
if channel_reg!(chctrla, self.id).read().enable().bit_is_set() {
if intflag.terr().bit_is_set() {
#[cfg(feature = "samd5x")]
if status.crcerr().bit_is_set() {
return Err(TransactionError::CRCError);
}
return Err(TransactionError::TransferError);
} else {
return Ok(WaitResult::Done);
}
}
if intflag.susp().bit_is_set() {
if status.ferr().bit_is_set() {
return Err(TransactionError::InvalidDescriptor);
} else {
return Ok(WaitResult::Suspended)
}
}
Ok(WaitResult::Ongoing)
}
}