Skip to main content

osiris/drivers/
can.rs

1use crate::error::PosixError;
2use crate::hal;
3use crate::sync::once::{LazyLock, OnceCell};
4use crate::sync::waiter::ParkedWaiter;
5
6pub use hal::can::{BusState, BusStatus, Diag, Filter, Frame, Mode};
7
8pub type Result<T> = core::result::Result<T, PosixError>;
9
10/// Max CAN controllers the kernel tracks; the device tree may populate fewer.
11const CAN_BUS_MAX: usize = 2;
12
13pub struct Bus {
14    desc: hal::can::Device,
15    /// Single consumer per controller; concurrent `register_waiter`
16    /// calls are rejected with `EBUSY`.
17    waiter: ParkedWaiter,
18}
19
20impl Bus {
21    fn slot(&self) -> u8 {
22        self.desc.index()
23    }
24}
25
26/// `OnceCell::set_or_get` writes in place, so the `&'static Bus` it
27/// returns stays valid for use as IRQ ctx — unlike values held inside a
28/// Vec or returned from a `LazyLock` closure, which move into their
29/// final storage.
30static SLOTS: [OnceCell<Bus>; CAN_BUS_MAX] = [const { OnceCell::new() }; CAN_BUS_MAX];
31
32#[derive(Clone, Copy)]
33struct BusInit {
34    slot: u8,
35    init: Result<()>,
36}
37
38static BUSES: LazyLock<[Option<BusInit>; CAN_BUS_MAX]> = LazyLock::new(|| {
39    let mut inits: [Option<BusInit>; CAN_BUS_MAX] = [None; CAN_BUS_MAX];
40    kprintln!(
41        "Found {} CAN bus entries",
42        hal::device_tree::CAN_REGISTRY.len()
43    );
44    for (i, entry) in hal::device_tree::CAN_REGISTRY.iter().enumerate() {
45        if i >= CAN_BUS_MAX {
46            kprintln!("    CAN registry exceeds CAN_BUS_MAX={CAN_BUS_MAX}");
47            break;
48        }
49        let bus = Bus {
50            desc: hal::can::Device::from_entry(entry),
51            waiter: ParkedWaiter::new(),
52        };
53        let bus_ref: &'static Bus = SLOTS[i].set_or_get(bus);
54        // Wire IRQs before `hal::can::init` — it enables interrupts at the
55        // chip controller, so any frame landing after must already have a
56        // dispatcher in place.
57        let init_result =
58            wire_irqs(bus_ref).and_then(|()| hal::can::init(&bus_ref.desc, Mode::Normal));
59        match init_result {
60            Ok(()) => kprintln!("    Initialized CAN bus at 0x{:x}", entry.instance),
61            Err(e) => kprintln!(
62                "    Failed to initialize CAN bus at 0x{:x}: {:?}",
63                entry.instance,
64                e,
65            ),
66        }
67        inits[i] = Some(BusInit {
68            slot: bus_ref.slot(),
69            init: init_result,
70        });
71    }
72    inits
73});
74
75fn wire_irqs(bus: &'static Bus) -> Result<()> {
76    let ctx = bus as *const Bus as *mut ();
77    hal::can::register_irq_handler(&bus.desc, Some(kernel_dispatch), ctx)?;
78
79    let entry = bus.desc.entry();
80    let rx0_vector = entry.rx0_irq.irqn as usize + 16;
81    let rx1_vector = entry.rx1_irq.irqn as usize + 16;
82    let userdata = Some(bus as *const Bus as usize);
83    unsafe {
84        crate::irq::register_irq(rx0_vector, rx_kernel_handler, userdata)
85            .map_err(|_| PosixError::EIO)?;
86        crate::irq::register_irq(rx1_vector, rx_kernel_handler, userdata)
87            .map_err(|_| PosixError::EIO)?;
88    }
89    Ok(())
90}
91
92extern "C" fn kernel_dispatch(kind: hal::can::Irq, ctx: *mut ()) {
93    if !matches!(kind, hal::can::Irq::Rx0 | hal::can::Irq::Rx1) {
94        return;
95    }
96    if ctx.is_null() {
97        return;
98    }
99    // SAFETY: ctx is the `&'static Bus` set by `wire_irqs`, backed by SLOTS.
100    let bus = unsafe { &*(ctx as *const Bus) };
101    bus.waiter.wake();
102}
103
104fn rx_kernel_handler(_ctx: *mut u8, _vector: usize, userdata: Option<usize>) {
105    let Some(ptr) = userdata else { return };
106    // SAFETY: see `kernel_dispatch`.
107    let bus = unsafe { &*(ptr as *const Bus) };
108    hal::can::dispatch_isr(bus.slot());
109}
110
111pub struct Device {
112    desc: hal::can::Device,
113}
114
115impl Device {
116    pub fn open(compatible: &str, ordinal: usize) -> Result<Self> {
117        let _ = LazyLock::force(&BUSES);
118
119        let desc = hal::can::get(compatible, ordinal)?;
120        let target_slot = desc.index();
121        for entry in BUSES.iter() {
122            if let Some(e) = entry {
123                if e.slot == target_slot {
124                    return match e.init {
125                        Ok(()) => Ok(Self { desc }),
126                        Err(err) => Err(err),
127                    };
128                }
129            }
130        }
131        Err(PosixError::ENODEV)
132    }
133
134    /// Bring the bus online.
135    pub fn start(&self) -> Result<()> {
136        hal::can::start(&self.desc)
137    }
138
139    pub fn transmit(&self, frame: &Frame) -> Result<()> {
140        hal::can::transmit(&self.desc, frame)
141    }
142
143    pub fn receive(&self, out: &mut Frame) -> Result<bool> {
144        hal::can::receive(&self.desc, out)
145    }
146
147    /// Configure a hardware filter. Prefer calling this before `start`.
148    pub fn configure_filter(&self, filter: &Filter) -> Result<()> {
149        hal::can::configure_filter(&self.desc, filter)
150    }
151
152    pub fn bus_status(&self) -> BusStatus {
153        hal::can::bus_status(&self.desc)
154    }
155
156    pub fn recover(&self) -> Result<()> {
157        hal::can::recover(&self.desc)
158    }
159
160    pub fn diag(&self) -> Diag {
161        hal::can::diag(&self.desc)
162    }
163
164    pub fn slot(&self) -> u8 {
165        self.desc.index()
166    }
167
168    /// Park `uid` as the single waiter on this controller. Returns
169    /// `EBUSY` if another thread is already armed — callers must not
170    /// share a single CAN device across concurrent receivers.
171    pub fn register_waiter(&self, uid: usize) -> Result<()> {
172        // `with_bus` yields the inner `arm` Result (kernel `Error`); we
173        // collapse both layers into the CAN driver's `PosixError` alias.
174        self.with_bus(|bus| bus.waiter.arm(uid))?
175            .map_err(|e| e.kind)
176    }
177
178    pub fn unregister_waiter(&self) -> Result<()> {
179        self.with_bus(|bus| bus.waiter.disarm())
180    }
181
182    fn with_bus<R, F: FnOnce(&Bus) -> R>(&self, f: F) -> Result<R> {
183        let target_slot = self.desc.index();
184        for cell in SLOTS.iter() {
185            if let Some(bus) = cell.get() {
186                if bus.slot() == target_slot {
187                    return Ok(f(bus));
188                }
189            }
190        }
191        Err(PosixError::ENODEV)
192    }
193}
194
195pub fn init() {
196    let _ = LazyLock::force(&BUSES);
197}