Skip to main content

embedded_can_interface/
lib.rs

1//! `embedded-can-interface`: HAL-style I/O traits for CAN controllers.
2//!
3//! This crate provides a small, `no_std`-friendly set of traits that describe the *shape* of a CAN
4//! interface, without committing to any particular runtime (blocking vs async), driver model, or
5//! buffer ownership.
6//!
7//! It is intended to sit between:
8//! - a concrete driver implementation (SocketCAN, bxCAN, MCP2515, a simulator, …), and
9//! - protocol layers that need to send/receive CAN frames (e.g. ISO-TP, UDS, J1939, proprietary
10//!   application protocols).
11//!
12//! The core idea is to support both “monolithic” CAN devices and split transmit/receive halves.
13//! Many protocol stacks are easier to integrate with when Tx and Rx are separate objects with
14//! independent borrowing, so this crate explicitly models that pattern.
15//!
16//! # What this crate does (and does not) do
17//! - ✅ Defines traits for sending/receiving frames, configuring acceptance filters, and optional
18//!   driver controls (nonblocking toggle, TX-idle query, buffering wrapper, builder/binding).
19//! - ✅ Provides small helper types for common ID/mask filter patterns.
20//! - ❌ Does not define an error model (e.g. “would block” vs “bus off”); that remains driver-
21//!   specific.
22//! - ❌ Does not define a frame type; you use a type implementing [`embedded_can::Frame`].
23//!
24//! # Quick start
25//! Most code consumes *traits*:
26//!
27//! - If you need only transmit: [`TxFrameIo`]
28//! - If you need only receive: [`RxFrameIo`]
29//! - If you need both (single object): [`FrameIo`]
30//! - If you use a split design: [`SplitTxRx`] to obtain halves
31//!
32//! ## Blocking example (conceptual)
33//! ```rust,ignore
34//! use embedded_can_interface::{RxFrameIo, TxFrameIo};
35//!
36//! fn ping<T>(io: &mut T, frame: &T::Frame) -> Result<T::Frame, T::Error>
37//! where
38//!     T: TxFrameIo + RxFrameIo<Frame = <T as TxFrameIo>::Frame, Error = <T as TxFrameIo>::Error>,
39//! {
40//!     io.send(frame)?;
41//!     io.recv()
42//! }
43//! ```
44//!
45//! ## Async example (conceptual)
46//! ```rust,ignore
47//! use embedded_can_interface::{AsyncRxFrameIo, AsyncTxFrameIo};
48//! use core::time::Duration;
49//!
50//! async fn ping<T>(io: &mut T, frame: &T::Frame) -> Result<T::Frame, T::Error>
51//! where
52//!     T: AsyncTxFrameIo + AsyncRxFrameIo<Frame = <T as AsyncTxFrameIo>::Frame, Error = <T as AsyncTxFrameIo>::Error>,
53//! {
54//!     io.send_timeout(frame, Duration::from_millis(10)).await?;
55//!     io.recv_timeout(Duration::from_millis(10)).await
56//! }
57//! ```
58//!
59//! # Design notes
60//! - `try_*` methods are “non-blocking” in the sense that they should return quickly. This crate
61//!   does not prescribe *how* a driver reports “no data available”; many backends use a dedicated
62//!   error variant (e.g. `nb::Error::WouldBlock`).
63//! - Timeouts are expressed as [`core::time::Duration`]. Implementations may approximate or ignore
64//!   timeouts if the underlying platform cannot support them.
65//! - `async fn` in traits is permitted via `#![allow(async_fn_in_trait)]` to keep the interface
66//!   ergonomic for consumers; concrete drivers can still choose how to implement async operations.
67
68#![allow(async_fn_in_trait)]
69
70use core::time::Duration;
71use embedded_can::{ExtendedId, StandardId};
72
73/// A CAN identifier (standard 11-bit or extended 29-bit).
74///
75/// Many embedded CAN HALs want to stay `no_std` and avoid allocating or storing extra metadata.
76/// This enum is a lightweight wrapper around the ID types provided by [`embedded_can`].
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum Id {
79    /// Standard 11-bit identifier.
80    Standard(StandardId),
81    /// Extended 29-bit identifier.
82    Extended(ExtendedId),
83}
84
85/// Bitmask corresponding to a CAN identifier (standard or extended width).
86///
87/// This is typically used for acceptance filtering: a frame is accepted when the masked bits match.
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum IdMask {
90    /// Mask for a standard 11-bit identifier.
91    Standard(u16),
92    /// Mask for an extended 29-bit identifier.
93    Extended(u32),
94}
95
96/// Acceptance filter: match an [`Id`] against an [`IdMask`].
97///
98/// Common semantics are:
99/// - “mask bit = 1” means “compare this bit”
100/// - “mask bit = 0” means “don’t care”
101///
102/// Exact matching rules and hardware limits are driver-specific.
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub struct IdMaskFilter {
105    /// Identifier to match (standard or extended).
106    pub id: Id,
107    /// Mask to apply; ones are compared, zeros are don't-care.
108    pub mask: IdMask,
109}
110
111/// Transmit-side (blocking) CAN frame I/O.
112///
113/// This is the minimal interface a protocol needs to *send* frames. You can implement it for a
114/// full CAN controller type, or for a dedicated “TX half” returned by [`SplitTxRx`].
115pub trait TxFrameIo {
116    /// The CAN frame type.
117    ///
118    /// Most implementations use a concrete frame type from a driver crate; it must represent a CAN
119    /// frame (including ID, DLC, payload) and is typically also an [`embedded_can::Frame`].
120    type Frame;
121    /// Error returned by the driver implementation.
122    type Error;
123
124    /// Send a frame, blocking until it is accepted by the driver.
125    fn send(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>;
126
127    /// Attempt to send a frame without blocking.
128    ///
129    /// When the driver cannot accept a frame immediately (e.g. no TX mailbox), implementations
130    /// typically return an error such as `nb::Error::WouldBlock`.
131    fn try_send(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>;
132
133    /// Send a frame, waiting up to `timeout` for the driver to accept it.
134    ///
135    /// Implementations that cannot support timeouts may treat this as [`TxFrameIo::send`].
136    fn send_timeout(&mut self, frame: &Self::Frame, timeout: Duration) -> Result<(), Self::Error>;
137}
138
139/// Receive-side (blocking) CAN frame I/O.
140///
141/// This is the minimal interface a protocol needs to *receive* frames. You can implement it for a
142/// full CAN controller type, or for a dedicated “RX half” returned by [`SplitTxRx`].
143pub trait RxFrameIo {
144    /// The CAN frame type.
145    type Frame;
146    /// Error returned by the driver implementation.
147    type Error;
148
149    /// Receive a frame, blocking until one is available.
150    fn recv(&mut self) -> Result<Self::Frame, Self::Error>;
151
152    /// Attempt to receive a frame without blocking.
153    ///
154    /// When no frame is available, implementations typically return an error such as
155    /// `nb::Error::WouldBlock`.
156    fn try_recv(&mut self) -> Result<Self::Frame, Self::Error>;
157
158    /// Receive a frame, waiting up to `timeout`.
159    ///
160    /// Implementations that cannot support timeouts may treat this as [`RxFrameIo::recv`].
161    fn recv_timeout(&mut self, timeout: Duration) -> Result<Self::Frame, Self::Error>;
162
163    /// Wait until the receive queue is non-empty.
164    ///
165    /// This can be used by polling-style protocols to avoid busy loops.
166    fn wait_not_empty(&mut self) -> Result<(), Self::Error>;
167}
168
169/// Transmit-side (async) CAN frame I/O.
170///
171/// This is the async equivalent of [`TxFrameIo`]. It is intentionally small: protocol layers that
172/// need async I/O can build on top of it without depending on a specific async runtime.
173pub trait AsyncTxFrameIo {
174    /// The CAN frame type.
175    type Frame;
176    /// Error returned by the driver implementation.
177    type Error;
178
179    /// Send a frame asynchronously.
180    async fn send(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>;
181
182    /// Send a frame asynchronously, waiting up to `timeout`.
183    ///
184    /// Implementations that cannot support timeouts may treat this as [`AsyncTxFrameIo::send`].
185    async fn send_timeout(
186        &mut self,
187        frame: &Self::Frame,
188        timeout: Duration,
189    ) -> Result<(), Self::Error>;
190}
191
192/// Receive-side (async) CAN frame I/O.
193///
194/// This is the async equivalent of [`RxFrameIo`].
195pub trait AsyncRxFrameIo {
196    /// The CAN frame type.
197    type Frame;
198    /// Error returned by the driver implementation.
199    type Error;
200
201    /// Receive a frame asynchronously.
202    async fn recv(&mut self) -> Result<Self::Frame, Self::Error>;
203
204    /// Receive a frame asynchronously, waiting up to `timeout`.
205    ///
206    /// Implementations that cannot support timeouts may treat this as [`AsyncRxFrameIo::recv`].
207    async fn recv_timeout(&mut self, timeout: Duration) -> Result<Self::Frame, Self::Error>;
208
209    /// Asynchronously wait until the receive queue is non-empty.
210    async fn wait_not_empty(&mut self) -> Result<(), Self::Error>;
211}
212
213/// Convenience marker for types that implement both [`TxFrameIo`] and [`RxFrameIo`] using the same
214/// frame and error types.
215///
216/// This is a *marker trait* only; it has no methods and exists to reduce boilerplate in bounds.
217pub trait FrameIo:
218    TxFrameIo<Frame = <Self as RxFrameIo>::Frame, Error = <Self as RxFrameIo>::Error> + RxFrameIo
219{
220}
221
222impl<T> FrameIo for T where
223    T: TxFrameIo<Frame = <T as RxFrameIo>::Frame, Error = <T as RxFrameIo>::Error> + RxFrameIo
224{
225}
226
227/// Convenience marker for types that implement both [`AsyncTxFrameIo`] and [`AsyncRxFrameIo`] using
228/// the same frame and error types.
229///
230/// This is a *marker trait* only; it has no methods and exists to reduce boilerplate in bounds.
231pub trait AsyncFrameIo:
232    AsyncTxFrameIo<Frame = <Self as AsyncRxFrameIo>::Frame, Error = <Self as AsyncRxFrameIo>::Error>
233    + AsyncRxFrameIo
234{
235}
236
237impl<T> AsyncFrameIo for T where
238    T: AsyncTxFrameIo<Frame = <T as AsyncRxFrameIo>::Frame, Error = <T as AsyncRxFrameIo>::Error>
239        + AsyncRxFrameIo
240{
241}
242
243/// Split a CAN interface into transmit and receive halves.
244///
245/// This trait is usually implemented for a concrete CAN driver type that internally owns shared
246/// state, returning lightweight wrapper types (`Tx`, `Rx`) that can be borrowed independently.
247pub trait SplitTxRx {
248    /// Transmit half type.
249    ///
250    /// This typically implements [`TxFrameIo`] and/or [`AsyncTxFrameIo`].
251    type Tx;
252    /// Receive half type.
253    ///
254    /// This typically implements [`RxFrameIo`] and/or [`AsyncRxFrameIo`].
255    type Rx;
256
257    /// Split into `(Tx, Rx)` halves.
258    fn split(self) -> (Self::Tx, Self::Rx);
259}
260
261/// Configure acceptance filters (aka “hardware filtering”).
262///
263/// CAN controllers often provide a fixed number of acceptance filter “banks”. Protocol layers may
264/// want to install filters to reduce host-side work.
265pub trait FilterConfig {
266    /// Error returned by the driver implementation.
267    type Error;
268
269    /// Handle used for modifying filters in-place (e.g. a bank accessor).
270    ///
271    /// Some drivers expose more complex configuration operations than “set this list”. A borrowed
272    /// handle enables in-place modifications without forcing a particular API shape.
273    type FiltersHandle<'a>: 'a
274    where
275        Self: 'a;
276
277    /// Replace the current filter configuration with a list of ID/mask pairs.
278    ///
279    /// Implementations may error if:
280    /// - the number of filters exceeds hardware limits, or
281    /// - a filter is not representable in hardware (e.g. mask width mismatch).
282    fn set_filters(&mut self, filters: &[IdMaskFilter]) -> Result<(), Self::Error>
283    where
284        Self: Sized;
285
286    /// Access filter banks through a handle (optional ergonomic API).
287    fn modify_filters(&mut self) -> Self::FiltersHandle<'_>;
288}
289
290/// Inspect driver state related to transmit/receive operation.
291pub trait TxRxState {
292    /// Error returned by the driver implementation.
293    type Error;
294
295    /// Returns `true` when the transmitter is idle (no frames pending).
296    ///
297    /// The exact definition of “idle” depends on the hardware/driver (e.g. all mailboxes empty).
298    fn is_transmitter_idle(&self) -> Result<bool, Self::Error>;
299}
300
301/// Control blocking vs nonblocking behavior.
302///
303/// Some drivers can be configured globally to make “blocking” operations return immediately.
304pub trait BlockingControl {
305    /// Error returned by the driver implementation.
306    type Error;
307
308    /// Globally toggle nonblocking mode.
309    ///
310    /// When `on` is true, methods like [`TxFrameIo::send`] and [`RxFrameIo::recv`] may return early
311    /// with a “would block” error.
312    fn set_nonblocking(&mut self, on: bool) -> Result<(), Self::Error>;
313}
314
315/// Buffered I/O wrapper creation.
316///
317/// This trait is for drivers that support adding host-side ring buffers around an underlying CAN
318/// device. This is common when the hardware has small mailboxes, but the host wants to smooth bursty
319/// traffic.
320pub trait BufferedIo {
321    /// The CAN frame type.
322    type Frame;
323    /// Error returned by the driver implementation (typically forwarded from the underlying device).
324    type Error;
325    /// Buffered wrapper type.
326    ///
327    /// The wrapper usually borrows the driver and user-provided storage.
328    type Buffered<'a, const TX: usize, const RX: usize>
329    where
330        Self: 'a;
331
332    /// Wrap the interface with host-side TX/RX ring buffers.
333    ///
334    /// The backing storage is provided by the caller to avoid allocation and to make buffer sizes
335    /// explicit in types.
336    fn buffered<'a, const TX: usize, const RX: usize>(
337        &'a mut self,
338        tx: &'a mut [Self::Frame; TX],
339        rx: &'a mut [Self::Frame; RX],
340    ) -> Self::Buffered<'a, TX, RX>;
341}
342
343/// Constructors/binding helpers.
344///
345/// This is an optional trait for backends that can be opened by name (e.g. `can0`) or configured via
346/// a builder pattern.
347pub trait BuilderBinding: Sized {
348    /// Error returned by the driver implementation.
349    type Error;
350    /// Builder type used to configure before constructing the driver.
351    type Builder;
352
353    /// Open/bind by interface name (SocketCAN-style).
354    fn open(name: &str) -> Result<Self, Self::Error>;
355
356    /// Create a builder that can configure before constructing the driver.
357    fn builder() -> Self::Builder;
358}