#![cfg_attr(docsrs, procmacros::doc_replace)]
use core::{
mem::ManuallyDrop,
ops::{Deref, DerefMut},
};
use embassy_embedded_hal::SetConfig;
use crate::{
Async,
Blocking,
DriverMode,
dma::{
Channel,
ChannelRx,
ChannelTx,
DmaChannelFor,
DmaEligible,
DmaError,
DmaRxBuffer,
DmaTxBuffer,
PeripheralDmaChannel,
PeripheralRxChannel,
PeripheralTxChannel,
asynch::{DmaRxFuture, DmaTxFuture},
},
pac::uhci0,
peripherals,
system::{GenericPeripheralGuard, Peripheral},
uart::{self, TxError, Uart, UartRx, UartTx},
};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
Dma(DmaError),
Tx(TxError),
}
impl From<DmaError> for Error {
fn from(value: DmaError) -> Self {
Error::Dma(value)
}
}
impl From<TxError> for Error {
fn from(value: TxError) -> Self {
Error::Tx(value)
}
}
crate::any_peripheral! {
pub peripheral AnyUhci<'d> {
Uhci0(crate::peripherals::UHCI0<'d>),
}
}
impl<'d> DmaEligible for AnyUhci<'d> {
#[cfg(dma_kind = "gdma")]
type Dma = crate::dma::AnyGdmaChannel<'d>;
fn dma_peripheral(&self) -> crate::dma::DmaPeripheral {
match &self.0 {
any::Inner::Uhci0(_) => crate::dma::DmaPeripheral::Uhci0,
}
}
}
impl AnyUhci<'_> {
fn register_block(&self) -> &uhci0::RegisterBlock {
match &self.0 {
any::Inner::Uhci0(x) => x.register_block(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ConfigError {
AboveReadLimit,
}
#[derive(Debug, Clone, Copy, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct RxConfig {
chunk_limit: u16,
}
impl RxConfig {
pub(crate) fn apply_config(&self, reg: &uhci0::RegisterBlock) -> Result<(), ConfigError> {
if self.chunk_limit > 4095 {
return Err(ConfigError::AboveReadLimit);
}
reg.pkt_thres()
.write(|w| unsafe { w.bits(self.chunk_limit as u32) });
Ok(())
}
}
#[derive(Debug, Clone, Copy, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct TxConfig {
idle_eof: bool,
len_eof: bool,
}
impl TxConfig {
pub(crate) fn apply_config(&self, reg: &uhci0::RegisterBlock) -> Result<(), ConfigError> {
reg.conf0().modify(|_, w| {
w.uart_idle_eof_en().bit(self.idle_eof);
w.len_eof_en().bit(self.len_eof)
});
Ok(())
}
}
impl Default for RxConfig {
fn default() -> RxConfig {
RxConfig {
chunk_limit: 128,
}
}
}
impl Default for TxConfig {
fn default() -> TxConfig {
TxConfig {
idle_eof: true,
len_eof: true,
}
}
}
impl core::error::Error for ConfigError {}
impl core::fmt::Display for ConfigError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ConfigError::AboveReadLimit => {
write!(
f,
"The requested read limit is not possible. The max is 4095 (12 bits)"
)
}
}
}
}
pub struct Uhci<'d, Dm>
where
Dm: DriverMode,
{
uart: Uart<'d, Dm>,
uhci_per: AnyUhci<'static>,
channel: Channel<Dm, PeripheralDmaChannel<AnyUhci<'d>>>,
_guard: GenericPeripheralGuard<{ Peripheral::Uhci0 as u8 }>,
}
impl<'d, Dm> Uhci<'d, Dm>
where
Dm: DriverMode,
{
fn init(&self) {
self.clean_turn_on();
self.reset();
self.select_uart();
}
fn clean_turn_on(&self) {
let reg = self.uhci_per.register_block();
reg.conf0().modify(|_, w| w.clk_en().set_bit());
reg.conf0().write(|w| {
unsafe { w.bits(0) };
w.clk_en().set_bit()
});
reg.conf1().modify(|_, w| unsafe { w.bits(0) });
reg.escape_conf().modify(|_, w| unsafe { w.bits(0) });
}
fn reset(&self) {
let reg = self.uhci_per.register_block();
reg.conf0().modify(|_, w| {
w.rx_rst().set_bit();
w.tx_rst().set_bit()
});
reg.conf0().modify(|_, w| {
w.rx_rst().clear_bit();
w.tx_rst().clear_bit()
});
}
fn select_uart(&self) {
let reg = self.uhci_per.register_block();
for_each_uart! {
(all $( ($id:literal, $peri:ident, $variant:ident, $($pins:ident),*) ),*) => {
reg.conf0().modify(|_, w| {
cfg_if::cfg_if! {
if #[cfg(uhci_combined_uart_selector_field)] {
unsafe {
w.uart_sel().bits(
match &self.uart.tx.uart.0 {
$(super::any::Inner::$variant(_) => {
debug!("Uhci will use UART{}", stringify!($id));
$id
})*
}
)
}
} else {
paste::paste! {
$(
w.[< $peri:lower _ce >]().clear_bit();
)*
match &self.uart.tx.uart.0 {
$(super::any::Inner::$variant(_) => {
debug!("Uhci will use {}", stringify!($peri));
w.[< $peri:lower _ce >]().set_bit()
})*
}
}
}
}
});
};
}
}
pub fn set_uart_config(&mut self, uart_config: &uart::Config) -> Result<(), uart::ConfigError> {
self.uart.set_config(uart_config)
}
pub fn split(self) -> (UhciRx<'d, Dm>, UhciTx<'d, Dm>) {
let (uart_rx, uart_tx) = self.uart.split();
(
UhciRx {
uhci_per: unsafe { self.uhci_per.clone_unchecked() },
uart_rx,
channel_rx: self.channel.rx,
_guard: self._guard.clone(),
},
UhciTx {
uhci_per: self.uhci_per,
uart_tx,
channel_tx: self.channel.tx,
_guard: self._guard.clone(),
},
)
}
pub fn apply_rx_config(&mut self, config: &RxConfig) -> Result<(), ConfigError> {
let reg = self.uhci_per.register_block();
config.apply_config(reg)
}
pub fn apply_tx_config(&mut self, config: &TxConfig) -> Result<(), ConfigError> {
let reg = self.uhci_per.register_block();
config.apply_config(reg)
}
}
impl<'d> Uhci<'d, Blocking> {
pub fn new(
uart: Uart<'d, Blocking>,
uhci: peripherals::UHCI0<'static>,
channel: impl DmaChannelFor<AnyUhci<'d>>,
) -> Self {
let guard = GenericPeripheralGuard::new();
let channel = Channel::new(channel.degrade());
channel.runtime_ensure_compatible(&uhci);
let uhci = Uhci {
uart,
uhci_per: uhci.into(),
channel,
_guard: guard,
};
uhci.init();
uhci
}
pub fn into_async(self) -> Uhci<'d, Async> {
Uhci {
uart: self.uart.into_async(),
uhci_per: self.uhci_per,
channel: self.channel.into_async(),
_guard: self._guard,
}
}
}
impl<'d> Uhci<'d, Async> {
pub fn into_blocking(self) -> Uhci<'d, Blocking> {
Uhci {
uart: self.uart.into_blocking(),
uhci_per: self.uhci_per,
channel: self.channel.into_blocking(),
_guard: self._guard,
}
}
}
pub struct UhciTx<'d, Dm>
where
Dm: DriverMode,
{
uhci_per: AnyUhci<'static>,
pub uart_tx: UartTx<'d, Dm>,
channel_tx: ChannelTx<Dm, PeripheralTxChannel<AnyUhci<'d>>>,
_guard: GenericPeripheralGuard<{ Peripheral::Uhci0 as u8 }>,
}
impl<'d, Dm> UhciTx<'d, Dm>
where
Dm: DriverMode,
{
pub fn write<Buf: DmaTxBuffer>(
mut self,
mut tx_buffer: Buf,
) -> Result<UhciDmaTxTransfer<'d, Dm, Buf>, (Error, Self, Buf)> {
let res = unsafe {
self.channel_tx
.prepare_transfer(self.uhci_per.dma_peripheral(), &mut tx_buffer)
};
if let Err(err) = res {
return Err((err.into(), self, tx_buffer));
}
let res = self.channel_tx.start_transfer();
if let Err(err) = res {
return Err((err.into(), self, tx_buffer));
}
Ok(UhciDmaTxTransfer::new(self, tx_buffer))
}
pub fn apply_config(&mut self, config: &TxConfig) -> Result<(), ConfigError> {
let reg = self.uhci_per.register_block();
config.apply_config(reg)
}
}
pub struct UhciRx<'d, Dm>
where
Dm: DriverMode,
{
uhci_per: AnyUhci<'static>,
pub uart_rx: UartRx<'d, Dm>,
channel_rx: ChannelRx<Dm, PeripheralRxChannel<AnyUhci<'d>>>,
_guard: GenericPeripheralGuard<{ Peripheral::Uhci0 as u8 }>,
}
impl<'d, Dm> UhciRx<'d, Dm>
where
Dm: DriverMode,
{
pub fn read<Buf: DmaRxBuffer>(
mut self,
mut rx_buffer: Buf,
) -> Result<UhciDmaRxTransfer<'d, Dm, Buf>, (Error, Self, Buf)> {
{
let res = unsafe {
self.channel_rx
.prepare_transfer(self.uhci_per.dma_peripheral(), &mut rx_buffer)
};
if let Err(err) = res {
return Err((err.into(), self, rx_buffer));
}
let res = self.channel_rx.start_transfer();
if let Err(err) = res {
return Err((err.into(), self, rx_buffer));
}
Ok(UhciDmaRxTransfer::new(self, rx_buffer))
}
}
pub fn apply_config(&mut self, config: &RxConfig) -> Result<(), ConfigError> {
let reg = self.uhci_per.register_block();
config.apply_config(reg)
}
}
pub struct UhciDmaTxTransfer<'d, Dm, Buf>
where
Dm: DriverMode,
Buf: DmaTxBuffer,
{
uhci: ManuallyDrop<UhciTx<'d, Dm>>,
dma_buf: ManuallyDrop<Buf::View>,
done: bool,
saved_err: Result<(), Error>,
}
impl<'d, Buf: DmaTxBuffer, Dm: DriverMode> UhciDmaTxTransfer<'d, Dm, Buf> {
fn new(uhci: UhciTx<'d, Dm>, dma_buf: Buf) -> Self {
Self {
uhci: ManuallyDrop::new(uhci),
dma_buf: ManuallyDrop::new(dma_buf.into_view()),
done: false,
saved_err: Ok(()),
}
}
pub fn is_done(&self) -> bool {
self.uhci.channel_tx.is_done()
}
pub fn cancel(mut self) -> (UhciTx<'d, Dm>, Buf::Final) {
self.uhci.channel_tx.stop_transfer();
let retval = unsafe {
(
ManuallyDrop::take(&mut self.uhci),
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
)
};
core::mem::forget(self);
retval
}
pub fn wait(mut self) -> (Result<(), Error>, UhciTx<'d, Dm>, Buf::Final) {
if let Err(err) = self.saved_err {
return (
Err(err),
unsafe { ManuallyDrop::take(&mut self.uhci) },
unsafe { Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)) },
);
}
if !self.done {
let res = self.uhci.uart_tx.flush();
if let Err(err) = res {
return (
Err(err.into()),
unsafe { ManuallyDrop::take(&mut self.uhci) },
unsafe { Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)) },
);
}
while !self.is_done() {}
}
self.uhci.channel_tx.stop_transfer();
let retval = unsafe {
(
Result::<(), Error>::Ok(()),
ManuallyDrop::take(&mut self.uhci),
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
)
};
core::mem::forget(self);
retval
}
}
impl<'d, Buf: DmaTxBuffer> UhciDmaTxTransfer<'d, Async, Buf> {
pub async fn wait_for_done(&mut self) {
let res = self.uhci.uart_tx.flush_async().await;
if let Err(err) = res {
self.saved_err = Err(err.into());
return;
}
let res = DmaTxFuture::new(&mut self.uhci.channel_tx).await;
if let Err(err) = res {
self.saved_err = Err(err.into());
return;
}
self.done = true;
}
}
impl<Dm: DriverMode, Buf: DmaTxBuffer> Deref for UhciDmaTxTransfer<'_, Dm, Buf> {
type Target = Buf::View;
fn deref(&self) -> &Self::Target {
&self.dma_buf
}
}
impl<Dm: DriverMode, Buf: DmaTxBuffer> DerefMut for UhciDmaTxTransfer<'_, Dm, Buf> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.dma_buf
}
}
impl<Dm, Buf> Drop for UhciDmaTxTransfer<'_, Dm, Buf>
where
Dm: DriverMode,
Buf: DmaTxBuffer,
{
fn drop(&mut self) {
self.uhci.channel_tx.stop_transfer();
unsafe {
ManuallyDrop::drop(&mut self.uhci);
drop(Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)));
}
}
}
pub struct UhciDmaRxTransfer<'d, Dm, Buf>
where
Dm: DriverMode,
Buf: DmaRxBuffer,
{
uhci: ManuallyDrop<UhciRx<'d, Dm>>,
dma_buf: ManuallyDrop<Buf::View>,
done: bool,
saved_err: Result<(), Error>,
}
impl<'d, Buf: DmaRxBuffer, Dm: DriverMode> UhciDmaRxTransfer<'d, Dm, Buf> {
fn new(uhci: UhciRx<'d, Dm>, dma_buf: Buf) -> Self {
Self {
uhci: ManuallyDrop::new(uhci),
dma_buf: ManuallyDrop::new(dma_buf.into_view()),
done: false,
saved_err: Ok(()),
}
}
pub fn is_done(&self) -> bool {
self.uhci.channel_rx.is_done()
}
pub fn cancel(mut self) -> (UhciRx<'d, Dm>, Buf::Final) {
self.uhci.channel_rx.stop_transfer();
let retval = unsafe {
(
ManuallyDrop::take(&mut self.uhci),
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
)
};
core::mem::forget(self);
retval
}
pub fn wait(mut self) -> (Result<(), Error>, UhciRx<'d, Dm>, Buf::Final) {
if let Err(err) = self.saved_err {
return (
Err(err),
unsafe { ManuallyDrop::take(&mut self.uhci) },
unsafe { Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)) },
);
}
if !self.done {
while !self.is_done() {}
}
self.uhci.channel_rx.stop_transfer();
let retval = unsafe {
(
Result::<(), Error>::Ok(()),
ManuallyDrop::take(&mut self.uhci),
Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)),
)
};
core::mem::forget(self);
retval
}
}
impl<'d, Buf: DmaRxBuffer> UhciDmaRxTransfer<'d, Async, Buf> {
pub async fn wait_for_done(&mut self) {
let res = DmaRxFuture::new(&mut self.uhci.channel_rx).await;
if let Err(err) = res {
self.saved_err = Err(err.into());
return;
}
self.done = true;
}
}
impl<Dm: DriverMode, Buf: DmaRxBuffer> Deref for UhciDmaRxTransfer<'_, Dm, Buf> {
type Target = Buf::View;
fn deref(&self) -> &Self::Target {
&self.dma_buf
}
}
impl<Dm: DriverMode, Buf: DmaRxBuffer> DerefMut for UhciDmaRxTransfer<'_, Dm, Buf> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.dma_buf
}
}
impl<Dm, Buf> Drop for UhciDmaRxTransfer<'_, Dm, Buf>
where
Dm: DriverMode,
Buf: DmaRxBuffer,
{
fn drop(&mut self) {
self.uhci.channel_rx.stop_transfer();
unsafe {
ManuallyDrop::drop(&mut self.uhci);
drop(Buf::from_view(ManuallyDrop::take(&mut self.dma_buf)));
}
}
}
impl<Dm> embassy_embedded_hal::SetConfig for Uhci<'_, Dm>
where
Dm: DriverMode,
{
type Config = (RxConfig, TxConfig);
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.apply_rx_config(&config.0)?;
self.apply_tx_config(&config.1)
}
}
impl<Dm> embassy_embedded_hal::SetConfig for UhciRx<'_, Dm>
where
Dm: DriverMode,
{
type Config = RxConfig;
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.apply_config(config)
}
}
impl<Dm> embassy_embedded_hal::SetConfig for UhciTx<'_, Dm>
where
Dm: DriverMode,
{
type Config = TxConfig;
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.apply_config(config)
}
}