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}