use enumset::{EnumSet, EnumSetType};
use crate::{
asynch::AtomicWaker,
dma::{BurstConfig, DmaRxInterrupt, DmaTxInterrupt},
interrupt::InterruptHandler,
peripherals::Interrupt,
private::{Internal, Sealed},
system::PeripheralGuard,
};
for_each_dma_engine! {
("AHB_GDMA") => {
mod gdma;
pub use gdma::*;
};
("AXI_GDMA") => {
mod axi_gdma;
pub use axi_gdma::*;
};
("COPY_DMA") => {
mod copy;
pub use copy::*;
};
("CRYPTO_DMA") => {
mod crypto;
pub use crypto::*;
};
("I2S_DMA") => {
mod i2s;
pub use i2s::*;
};
("SPI_DMA") => {
mod spi;
pub use spi::*;
};
}
pub trait DmaEligiblePeripheral<D: DmaChannel> {
fn dma_peripheral(&self) -> DmaPeripheral;
}
for_each_peripheral! {
(dma_eligible $(( $peri:ident, $name:ident, $id:literal, $any_ch:ident )),*) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[doc(hidden)]
pub struct DmaPeripheral(pub(crate) u8);
impl DmaPeripheral {
$(
#[doc = concat!("DMA accesses ", stringify!($name))]
pub const $peri: Self = Self($id);
)*
}
$(
impl<'d> DmaEligiblePeripheral<$any_ch<'d>> for crate::peripherals::$peri<'d> {
fn dma_peripheral(&self) -> DmaPeripheral {
DmaPeripheral::$peri
}
}
)*
};
}
#[doc(hidden)]
pub trait RegisterAccess: Sealed {
#[allow(private_interfaces)]
fn enable(&self) -> Option<PeripheralGuard>;
fn reset(&self);
fn set_burst_mode(&self, burst_mode: BurstConfig);
fn set_descr_burst_mode(&self, burst_mode: bool);
#[cfg(dma_max_priority_is_set)]
fn set_priority(&self, priority: crate::dma::DmaPriority);
fn set_peripheral(&self, _peripheral: u8) {}
fn set_link_addr(&self, address: u32);
fn start(&self);
fn stop(&self);
fn restart(&self);
fn set_check_owner(&self, check_owner: Option<bool>);
#[cfg(dma_ext_mem_configurable_block_size)]
fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize);
#[cfg(dma_can_access_psram)]
fn can_access_psram(&self) -> bool;
fn compatible_peripherals(&self) -> &[u8];
fn runtime_ensure_compatible(&self, peripheral: DmaPeripheral) {
let peripherals = self.compatible_peripherals();
assert!(
peripherals.contains(&peripheral.0),
"This DMA channel is not compatible with peripheral id {}",
peripheral.0
);
}
}
#[doc(hidden)]
pub trait RxRegisterAccess: RegisterAccess {
#[cfg(dma_supports_mem2mem)]
fn set_mem2mem_mode(&self, value: bool);
fn peripheral_interrupt(&self) -> Option<Interrupt>;
fn async_handler(&self) -> Option<InterruptHandler>;
}
#[doc(hidden)]
pub trait TxRegisterAccess: RegisterAccess {
fn is_fifo_empty(&self) -> bool;
fn set_auto_write_back(&self, enable: bool);
fn last_dscr_address(&self) -> usize;
fn peripheral_interrupt(&self) -> Option<Interrupt>;
fn async_handler(&self) -> Option<InterruptHandler>;
}
#[doc(hidden)]
pub trait InterruptAccess<T: EnumSetType>: Sealed {
fn listen(&self, interrupts: impl Into<EnumSet<T>>) {
self.enable_listen(interrupts.into(), true)
}
fn unlisten(&self, interrupts: impl Into<EnumSet<T>>) {
self.enable_listen(interrupts.into(), false)
}
fn clear_all(&self) {
self.clear(EnumSet::all());
}
fn enable_listen(&self, interrupts: EnumSet<T>, enable: bool);
fn is_listening(&self) -> EnumSet<T>;
fn clear(&self, interrupts: impl Into<EnumSet<T>>);
fn pending_interrupts(&self) -> EnumSet<T>;
fn waker(&self) -> &'static AtomicWaker;
fn is_async(&self) -> bool;
fn set_async(&self, is_async: bool);
}
#[instability::unstable]
pub trait DmaRxChannel: RxRegisterAccess + InterruptAccess<DmaRxInterrupt> {}
#[instability::unstable]
pub trait DmaTxChannel: TxRegisterAccess + InterruptAccess<DmaTxInterrupt> {}
pub trait DmaChannel: Sized + crate::private::Sealed {
type Rx: DmaRxChannel + From<Self>;
type Tx: DmaTxChannel + From<Self>;
#[cfg(any(esp32c5, esp32c6, esp32h2, esp32s3))] fn split(self) -> (Self::Rx, Self::Tx) {
unsafe { self.split_internal(Internal) }
}
unsafe fn split_internal(self, _: Internal) -> (Self::Rx, Self::Tx);
}
#[doc(hidden)]
pub trait DmaChannelExt: DmaChannel {
fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt>;
fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt>;
}
macro_rules! impl_channel_common {
($peri:ident, $instance:ident) => {
paste::paste! {
impl<'d> DmaChannel for $instance<'d> {
type Rx = [<$peri RxChannel>]<'d>;
type Tx = [<$peri TxChannel>]<'d>;
unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) {
unsafe {
(
[<$peri RxChannel>](Self::steal().into()),
[<$peri TxChannel>](Self::steal().into()),
)
}
}
}
impl<'d> From<$instance<'d>> for [<$peri RxChannel>]<'d> {
fn from(this: $instance<'d>) -> [<$peri RxChannel>]<'d> {
[<$peri RxChannel>](this.into())
}
}
impl<'d> From<$instance<'d>> for [<$peri TxChannel>]<'d> {
fn from(this: $instance<'d>) -> [<$peri TxChannel>]<'d> {
[<$peri TxChannel>](this.into())
}
}
impl crate::dma::DmaChannelExt for $instance<'_> {
fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
[<$peri RxChannel>]::from(unsafe { Self::steal() })
}
fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
[<$peri TxChannel>]::from(unsafe { Self::steal() })
}
}
}
};
}
pub(crate) use impl_channel_common;