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