Skip to main content

ove/
embedded_hal_impl.rs

1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! `embedded-hal` 1.0 sync trait impls.
8//!
9//! Compiled only when the `embedded-hal` Cargo feature is enabled.
10//! Each section is further gated by the substrate `has_*` cfg for the
11//! corresponding peripheral — a target build with I2C compiled out
12//! omits the I2C impls automatically, same for SPI / GPIO / TIME.
13//!
14//! Provides the ecosystem-compatible trait surface for
15//! `ove::I2c` / `ove::Spi` / `ove::gpio::OutputPin` / `ove::gpio::InputPin`
16//! / `ove::Delay` so that any `embedded-hal`-aware crate.io driver
17//! (BME280, MPU6050, SX1276, …) composes with oveRTOS peripherals at
18//! zero per-call cost (monomorphisation inlines the wrappers).
19//!
20//! Error mapping into the `embedded_hal::*::ErrorKind` enums is best-
21//! effort — the substrate's `OVE_ERR_*` taxonomy is coarser than
22//! `embedded-hal`'s.  Unmappable variants fall back to `ErrorKind::Other`.
23
24use crate::error::Error;
25
26// ---------------------------------------------------------------------------
27// I2C
28// ---------------------------------------------------------------------------
29
30#[cfg(has_i2c)]
31mod i2c_impl {
32    use super::*;
33    use crate::I2c;
34    use embedded_hal::i2c;
35
36    impl i2c::Error for Error {
37        fn kind(&self) -> i2c::ErrorKind {
38            match self {
39                Error::BusNack => i2c::ErrorKind::NoAcknowledge(i2c::NoAcknowledgeSource::Unknown),
40                // `embedded-hal` 1.0 has no "busy" ErrorKind; ArbitrationLoss
41                // is the closest retryable bus-contention signal, so drivers
42                // that back off on arbitration loss also back off on a busy bus.
43                Error::BusBusy => i2c::ErrorKind::ArbitrationLoss,
44                Error::BusError => i2c::ErrorKind::Bus,
45                _ => i2c::ErrorKind::Other,
46            }
47        }
48    }
49
50    impl i2c::ErrorType for I2c {
51        type Error = Error;
52    }
53
54    impl i2c::I2c for I2c {
55        fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
56            I2c::read(
57                self,
58                address as u16,
59                read,
60                core::time::Duration::from_secs(u64::MAX / 2),
61            )
62        }
63
64        fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
65            I2c::write(
66                self,
67                address as u16,
68                write,
69                core::time::Duration::from_secs(u64::MAX / 2),
70            )
71        }
72
73        fn write_read(
74            &mut self,
75            address: u8,
76            write: &[u8],
77            read: &mut [u8],
78        ) -> Result<(), Self::Error> {
79            I2c::write_read(
80                self,
81                address as u16,
82                write,
83                read,
84                core::time::Duration::from_secs(u64::MAX / 2),
85            )
86        }
87
88        fn transaction(
89            &mut self,
90            address: u8,
91            operations: &mut [i2c::Operation<'_>],
92        ) -> Result<(), Self::Error> {
93            // The substrate doesn't expose explicit START/STOP control,
94            // so atomicity across a heterogeneous sequence isn't
95            // guaranteed.  We do fuse Write+Read into `write_read`
96            // (which DOES emit a repeated START) for the common
97            // "register address then read" pattern.
98            let mut i = 0;
99            while i < operations.len() {
100                if i + 1 < operations.len() {
101                    let (left, right) = operations.split_at_mut(i + 1);
102                    if let (i2c::Operation::Write(w), i2c::Operation::Read(r)) =
103                        (&left[i], &mut right[0])
104                    {
105                        self.write_read(address, w, r)?;
106                        i += 2;
107                        continue;
108                    }
109                }
110                match &mut operations[i] {
111                    i2c::Operation::Read(buf) => self.read(address, buf)?,
112                    i2c::Operation::Write(buf) => self.write(address, buf)?,
113                }
114                i += 1;
115            }
116            Ok(())
117        }
118    }
119}
120
121// ---------------------------------------------------------------------------
122// SPI
123// ---------------------------------------------------------------------------
124
125#[cfg(has_spi)]
126mod spi_impl {
127    use super::*;
128    use crate::Spi;
129    use crate::bindings;
130    use embedded_hal::spi;
131
132    impl spi::Error for Error {
133        fn kind(&self) -> spi::ErrorKind {
134            spi::ErrorKind::Other
135        }
136    }
137
138    impl spi::ErrorType for Spi {
139        type Error = Error;
140    }
141
142    impl spi::SpiBus<u8> for Spi {
143        fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
144            Spi::read(
145                self,
146                None,
147                words,
148                core::time::Duration::from_secs(u64::MAX / 2),
149            )
150        }
151
152        fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
153            Spi::write(
154                self,
155                None,
156                words,
157                core::time::Duration::from_secs(u64::MAX / 2),
158            )
159        }
160
161        fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
162            Spi::transfer(
163                self,
164                None,
165                write,
166                read,
167                core::time::Duration::from_secs(u64::MAX / 2),
168            )
169        }
170
171        fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
172            // Substrate accepts `tx_ptr == rx_ptr` — SPI hardware
173            // shifts bits through the same memory location.  No tmp
174            // buffer needed.
175            let len = words.len();
176            let p = words.as_mut_ptr();
177            let rc = unsafe {
178                bindings::ove_spi_transfer(
179                    self.raw(),
180                    core::ptr::null(),
181                    p as *const _,
182                    p as *mut _,
183                    len,
184                    u64::MAX,
185                )
186            };
187            Error::from_code(rc)
188        }
189
190        fn flush(&mut self) -> Result<(), Self::Error> {
191            // Substrate transfers are synchronous — completion implies
192            // bus quiescence.
193            Ok(())
194        }
195    }
196}
197
198// ---------------------------------------------------------------------------
199// Digital GPIO
200// ---------------------------------------------------------------------------
201
202#[cfg(has_gpio)]
203mod gpio_impl {
204    use super::*;
205    use crate::gpio::{InputPin, OutputPin};
206    use embedded_hal::digital;
207
208    impl digital::Error for Error {
209        fn kind(&self) -> digital::ErrorKind {
210            digital::ErrorKind::Other
211        }
212    }
213
214    impl digital::ErrorType for OutputPin {
215        type Error = Error;
216    }
217
218    impl digital::OutputPin for OutputPin {
219        fn set_low(&mut self) -> Result<(), Self::Error> {
220            OutputPin::set_low(self)
221        }
222
223        fn set_high(&mut self) -> Result<(), Self::Error> {
224            OutputPin::set_high(self)
225        }
226    }
227
228    impl digital::ErrorType for InputPin {
229        type Error = Error;
230    }
231
232    impl digital::InputPin for InputPin {
233        fn is_high(&mut self) -> Result<bool, Self::Error> {
234            InputPin::is_high(self)
235        }
236
237        fn is_low(&mut self) -> Result<bool, Self::Error> {
238            InputPin::is_low(self)
239        }
240    }
241}
242
243// ---------------------------------------------------------------------------
244// Delay
245// ---------------------------------------------------------------------------
246
247#[cfg(has_time)]
248mod delay_impl {
249    use crate::time::{Delay, delay_ms, delay_us};
250    use embedded_hal::delay::DelayNs;
251
252    impl DelayNs for Delay {
253        fn delay_ns(&mut self, ns: u32) {
254            if ns == 0 {
255                return;
256            }
257            // Round up to microsecond — substrate has no ns resolution.
258            let us = ns.div_ceil(1_000);
259            delay_us(us);
260        }
261
262        fn delay_us(&mut self, us: u32) {
263            delay_us(us);
264        }
265
266        fn delay_ms(&mut self, ms: u32) {
267            delay_ms(ms);
268        }
269    }
270}