use crate::config::Mode;
use crate::messageram::Capacities;
use crate::reg;
use core::convert::Infallible;
use core::marker::PhantomData;
use generic_array::{typenum::Unsigned, GenericArray};
use reg::AccessRegisterBlock as _;
use vcell::VolatileCell;
#[derive(Debug)]
pub enum Error {
OutOfBounds,
FdDisabled,
BitRateSwitchingDisabled,
}
pub struct Tx<'a, P, C: Capacities> {
memory: &'a mut GenericArray<VolatileCell<C::TxMessage>, C::TxBuffers>,
pub(crate) mode: Mode,
_markers: PhantomData<P>,
}
pub trait DynTx {
type Id;
type Message;
fn transmit_dedicated(&mut self, index: usize, message: Self::Message)
-> nb::Result<(), Error>;
fn transmit_queued(&mut self, message: Self::Message) -> nb::Result<(), Error>;
fn enable_cancellation_interrupt(&mut self, to_be_enabled: TxBufferSet);
fn disable_cancellation_interrupt(&mut self, to_be_disabled: TxBufferSet);
fn enable_transmission_completed_interrupt(&mut self, to_be_enabled: TxBufferSet);
fn disable_transmission_completed_interrupt(&mut self, to_be_disabled: TxBufferSet);
fn get_cancellation_flags(&self) -> TxBufferSet;
fn get_transmission_completed_flags(&self) -> TxBufferSet;
fn iter_cancellation_flags(&self) -> Iter;
fn iter_transmission_completed_flags(&self) -> Iter;
fn cancel_multi(&mut self, to_be_canceled: TxBufferSet) -> nb::Result<(), Infallible>;
fn cancel(&mut self, index: usize) -> nb::Result<(), Infallible>;
}
impl<'a, P: mcan_core::CanId, C: Capacities> Tx<'a, P, C> {
pub(crate) unsafe fn new(
memory: &'a mut GenericArray<VolatileCell<C::TxMessage>, C::TxBuffers>,
mode: Mode,
) -> Self {
Self {
memory,
mode,
_markers: PhantomData,
}
}
unsafe fn regs(&self) -> ®::RegisterBlock {
&(*P::register_block())
}
fn txfqs(&self) -> ®::TXFQS {
unsafe { &self.regs().txfqs }
}
fn txbrp(&self) -> ®::TXBRP {
unsafe { &self.regs().txbrp }
}
fn txbar(&self) -> ®::TXBAR {
unsafe { &self.regs().txbar }
}
fn txbcr(&self) -> ®::TXBCR {
unsafe { &self.regs().txbcr }
}
fn txbto(&self) -> ®::TXBTO {
unsafe { &self.regs().txbto }
}
fn txbcf(&self) -> ®::TXBCF {
unsafe { &self.regs().txbcf }
}
fn txbtie(&self) -> ®::TXBTIE {
unsafe { &self.regs().txbtie }
}
fn txbcie(&self) -> ®::TXBCIE {
unsafe { &self.regs().txbcie }
}
fn add_request(&self, index: usize) {
unsafe { self.txbar().write(|w| w.bits(1 << index)) }
}
fn is_buffer_in_use(&self, index: usize) -> bool {
let add_requests = self.txbar().read().bits();
let pending = self.txbrp().read().bits();
(add_requests | pending) & (1 << index) != 0
}
fn transmit(&mut self, index: usize, message: C::TxMessage) -> nb::Result<(), Error> {
if self.is_buffer_in_use(index) {
return Err(nb::Error::WouldBlock);
}
self.validate_message(&message)?;
self.memory
.get_mut(index)
.ok_or(Error::OutOfBounds)?
.set(message);
self.add_request(index);
Ok(())
}
fn find_put_index(&self) -> Option<usize> {
let status = self.txfqs().read();
if status.tfqf().bit() {
None
} else {
Some(status.tfqpi().bits() as usize)
}
}
fn poll_canceled(&self, to_be_canceled: TxBufferSet) -> nb::Result<(), Infallible> {
let already_canceled = self.get_cancellation_flags();
if already_canceled.0 & to_be_canceled.0 == to_be_canceled.0 {
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
fn validate_message(&self, message: &C::TxMessage) -> Result<(), Error> {
use crate::message::Raw;
if message.fd_format() && !matches!(self.mode, Mode::Fd { .. }) {
return Err(Error::FdDisabled);
}
if message.bit_rate_switching()
&& !matches!(
self.mode,
Mode::Fd {
allow_bit_rate_switching: true,
..
}
)
{
return Err(Error::BitRateSwitchingDisabled);
}
Ok(())
}
}
impl<P: mcan_core::CanId, C: Capacities> DynTx for Tx<'_, P, C> {
type Id = P;
type Message = C::TxMessage;
fn transmit_dedicated(
&mut self,
index: usize,
message: Self::Message,
) -> nb::Result<(), Error> {
if index > C::DedicatedTxBuffers::USIZE {
Err(Error::OutOfBounds)?;
}
self.transmit(index, message)
}
fn transmit_queued(&mut self, message: Self::Message) -> nb::Result<(), Error> {
let index = self.find_put_index().ok_or(nb::Error::WouldBlock)?;
self.transmit(index, message)
}
fn enable_cancellation_interrupt(&mut self, to_be_enabled: TxBufferSet) {
unsafe {
self.txbcie()
.modify(|r, w| w.bits(r.bits() | to_be_enabled.0));
}
}
fn disable_cancellation_interrupt(&mut self, to_be_disabled: TxBufferSet) {
unsafe {
self.txbcie()
.modify(|r, w| w.bits(r.bits() & !to_be_disabled.0));
}
}
fn enable_transmission_completed_interrupt(&mut self, to_be_enabled: TxBufferSet) {
unsafe {
self.txbtie()
.modify(|r, w| w.bits(r.bits() | to_be_enabled.0));
}
}
fn disable_transmission_completed_interrupt(&mut self, to_be_disabled: TxBufferSet) {
unsafe {
self.txbtie()
.modify(|r, w| w.bits(r.bits() & !to_be_disabled.0));
}
}
fn get_cancellation_flags(&self) -> TxBufferSet {
TxBufferSet(self.txbcf().read().bits())
}
fn get_transmission_completed_flags(&self) -> TxBufferSet {
TxBufferSet(self.txbto().read().bits())
}
fn iter_cancellation_flags(&self) -> Iter {
self.get_cancellation_flags().iter()
}
fn iter_transmission_completed_flags(&self) -> Iter {
self.get_transmission_completed_flags().iter()
}
fn cancel_multi(&mut self, to_be_canceled: TxBufferSet) -> nb::Result<(), Infallible> {
self.poll_canceled(to_be_canceled).or_else(|_| {
unsafe {
self.txbcr().write(|w| w.bits(to_be_canceled.0));
}
self.poll_canceled(to_be_canceled)
})
}
fn cancel(&mut self, index: usize) -> nb::Result<(), Infallible> {
self.cancel_multi([index].into_iter().collect())
}
}
#[derive(Copy, Clone)]
pub struct TxBufferSet(pub u32);
impl FromIterator<usize> for TxBufferSet {
fn from_iter<T: IntoIterator<Item = usize>>(iter: T) -> Self {
let mut set = 0_u32;
for i in iter.into_iter() {
set |= 1u32 << i;
}
TxBufferSet(set)
}
}
impl TxBufferSet {
pub fn all() -> Self {
Self(u32::MAX)
}
pub fn iter(&self) -> Iter {
Iter {
flags: *self,
index: 0,
}
}
}
pub struct Iter {
flags: TxBufferSet,
index: u8,
}
impl Iterator for Iter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
let i = self.index;
self.index = self.index.saturating_add(1);
if i > 31 {
None
} else if self.flags.0 & (1 << i) != 0 {
Some(i as usize)
} else {
self.next()
}
}
}