Design Decisions

Informative

The rationale behind the CAN driver contract. This helps implementers get the behavior right; it is not itself a requirement. Each entry points to the requirement it explains.

Why no automatic bus-off recovery?

Governs CHI-CAN-MUST-07.

Automatic recovery restarts the controller without telling the upper layer, so message loss can go undetected and a persistent physical fault (wiring, termination, a babbling node) gets silently retried. The upper layer is better placed to decide whether and when to rejoin — immediately, after a back-off, or not at all in a safety-related system. So the driver reports bus-off via CanIf_OnError and leaves the channel STOPPED; the integrator restarts it deliberately (see Handle bus-off).

Implication for implementers: do not wire up hardware auto-restart in your ISR; surface the event instead.

Why reject remote frames entirely?

Governs CHI-CAN-MUST-05.

Remote frames add a request/response mode that most modern designs neither use nor want, and that complicates filtering and buffer management. Excluding them keeps the driver small and its behavior predictable. The driver configures the hardware to ignore and reject incoming RTRs and never emits them.

Implication for implementers: set the controller’s filters/config to drop RTRs; do not expose a remote-transmission path.

Why no software priority emulation?

Governs CHI-CAN-MUST-11.

Emulating transmit priority in software adds latency, jitter, and complexity that fight the goal of a thin HAL — and hardware already arbitrates by identifier on the wire. Where priority ordering among pending frames matters, it should come from hardware transmit-object priority or from the upper layer’s queueing.

Implication for implementers: map transmit requests onto hardware objects directly; don’t build a software priority queue inside the driver.

Why split scheduling into poll and cyclic functions?

Governs CHI-CAN-MUST-20.

TX/RX handling needs to run often (sub-millisecond) to keep throughput up on small MCUs with shallow queues, while bus-off and state-transition polling are fine at a slower cadence (≥ 1 ms). Separating Can_PollFunction from Can_CyclicFunction lets the integrator schedule each at an appropriate rate instead of paying the fast rate for everything.

Implication for implementers: keep Can_PollFunction cheap; put slow polling in Can_CyclicFunction. Either may be empty if nothing is configured for polling.

Why is SLEEP only a logical state?

Relates to CHI-CAN-MUST-02.

Wakeup detection depends on transceiver and board features that a portable HAL for small MCUs cannot assume. Rather than mandate it, the driver omits wakeup detection and treats SLEEP as a logical state: the channel stops participating, but no wakeup source is armed.

Implication for implementers: implement SLEEP as “stopped, marked asleep”; do not rely on transceiver wakeup, and map SLEEP→STOPPED on hardware without a sleep state as a no-op.