Skip to main content

xpanse_api/bus/
uart.rs

1//! UART bus abstraction.
2//!
3//! Wraps a concrete backend (hardware, PIO, or bit-banged) behind a single
4//! boxed trait object so that drivers can operate on a `UartBusHandle`
5//! without knowing which backend was selected.
6//!
7//! [`BusAllocator`](crate::bus::allocator::BusAllocator) is the entry point used to
8//! allocate UART buses at startup.
9
10use alloc::boxed::Box;
11use core::future::Future;
12use core::pin::Pin;
13
14/// Error returned by UART operations.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
16pub enum UartError {
17    BufferFull,
18    InvalidBaudRate,
19    Overrun,
20    Break,
21    Parity,
22    Framing,
23    /// Any other error reported by the underlying backend.
24    Other,
25}
26
27impl core::fmt::Display for UartError {
28    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29        write!(f, "{:?}", self)
30    }
31}
32
33impl core::error::Error for UartError {}
34
35impl embedded_io_async::Error for UartError {
36    fn kind(&self) -> embedded_io_async::ErrorKind {
37        embedded_io_async::ErrorKind::Other
38    }
39}
40
41impl From<embassy_rp::uart::Error> for UartError {
42    fn from(error: embassy_rp::uart::Error) -> Self {
43        match error {
44            embassy_rp::uart::Error::Overrun => Self::Overrun,
45            embassy_rp::uart::Error::Break => Self::Break,
46            embassy_rp::uart::Error::Parity => Self::Parity,
47            embassy_rp::uart::Error::Framing => Self::Framing,
48            _ => Self::Other,
49        }
50    }
51}
52
53/// Backend variant of a [`UartBusHandle`].
54#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
55pub enum UartBusVersion {
56    /// Hardware UART peripheral.
57    Hardware,
58    /// PIO-based programmable UART.
59    Pio,
60    /// Pure GPIO bit-bang.
61    BitBang,
62}
63
64/// Trait-object-safe async UART bus.
65pub trait DynUartBus: Send {
66    fn write<'a>(
67        &'a mut self,
68        buf: &'a [u8],
69    ) -> Pin<Box<dyn Future<Output = Result<usize, UartError>> + 'a>>;
70    fn read<'a>(
71        &'a mut self,
72        buf: &'a mut [u8],
73    ) -> Pin<Box<dyn Future<Output = Result<usize, UartError>> + 'a>>;
74    fn flush<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), UartError>> + 'a>>;
75}
76
77/// Owned handle to an async UART bus.
78///
79/// Dropping this handle does not return its startup resources, so keep it alive
80/// for as long as you need UART access.
81#[must_use = "dropping a bus handle does not return its startup resources"]
82pub struct UartBusHandle {
83    inner: Box<dyn DynUartBus>,
84    version: UartBusVersion,
85}
86
87impl UartBusHandle {
88    /// Wraps a boxed backend and records its [`UartBusVersion`].
89    pub fn new(inner: Box<dyn DynUartBus>, version: UartBusVersion) -> Self {
90        Self { inner, version }
91    }
92
93    /// Returns the backend variant.
94    pub fn version(&self) -> UartBusVersion {
95        self.version
96    }
97}
98
99impl embedded_io_async::ErrorType for UartBusHandle {
100    type Error = UartError;
101}
102
103impl embedded_io_async::Read for UartBusHandle {
104    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
105        self.inner.read(buf).await
106    }
107}
108
109impl embedded_io_async::Write for UartBusHandle {
110    async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
111        self.inner.write(buf).await
112    }
113
114    async fn flush(&mut self) -> Result<(), Self::Error> {
115        self.inner.flush().await
116    }
117}
118
119impl<T> DynUartBus for T
120where
121    T: embedded_io_async::Read<Error = UartError> + embedded_io_async::Write<Error = UartError>,
122    T: Send,
123{
124    fn write<'a>(
125        &'a mut self,
126        buf: &'a [u8],
127    ) -> Pin<Box<dyn Future<Output = Result<usize, UartError>> + 'a>> {
128        Box::pin(async move { embedded_io_async::Write::write(self, buf).await })
129    }
130
131    fn read<'a>(
132        &'a mut self,
133        buf: &'a mut [u8],
134    ) -> Pin<Box<dyn Future<Output = Result<usize, UartError>> + 'a>> {
135        Box::pin(async move { embedded_io_async::Read::read(self, buf).await })
136    }
137
138    fn flush<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = Result<(), UartError>> + 'a>> {
139        Box::pin(async move { embedded_io_async::Write::flush(self).await })
140    }
141}