Skip to main content

ove/
i2c.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//! I2C bus master driver.
8//!
9//! [`I2c`] wraps an opaque `ove_i2c_t` handle and exposes a method-shaped
10//! API.  An `embedded_hal::i2c::I2c` impl is provided when the
11//! `embedded-hal` Cargo feature is enabled.
12
13use crate::bindings;
14use crate::error::{Error, Result};
15
16// SAFETY (module-wide contract for the `unsafe { bindings::ove_*(...) }` FFI
17// calls below): any handle passed to the C API is non-null and refers to a
18// live RTOS object — wrapper constructors establish validity via
19// `Error::from_code`, and `Drop` (or an explicit `deinit`) is the only place
20// a handle is released. Pointer and slice arguments reference caller-owned
21// memory valid for the duration of the call; the C side copies whatever it
22// retains and does not alias them past return (verified against the
23// signatures in `include/ove/*.h`). Blocks that deviate — `transmute`, raw
24// pointer casts from user data, slice reconstruction via `from_raw_parts`,
25// or storing a callback across the FFI boundary — carry their own
26// `// SAFETY:` comment.
27
28/// I2C bus speed grade.
29#[repr(u32)]
30#[derive(Debug, Copy, Clone, PartialEq, Eq)]
31pub enum Speed {
32    /// Standard mode — 100 kHz.
33    Standard = 0,
34    /// Fast mode — 400 kHz.
35    Fast = 1,
36    /// Fast-mode Plus — 1 MHz.
37    FastPlus = 2,
38}
39
40/// I2C bus master.
41///
42/// Wraps an opaque `ove_i2c_t` handle provided by the board configuration
43/// (the substrate manages the handle's lifetime; the binding does not own
44/// it).  Construct via [`I2c::from_handle`] with a handle obtained from
45/// the BSP layer or board descriptor.
46#[derive(Debug, Copy, Clone)]
47pub struct I2c {
48    handle: bindings::ove_i2c_t,
49}
50
51impl I2c {
52    /// Wrap an existing `ove_i2c_t` handle.
53    ///
54    /// # Safety
55    /// - `handle` must be a valid I2C handle returned by the substrate
56    ///   (typically via the board config / BSP layer).
57    /// - The caller is responsible for ensuring no other `I2c` wrapper
58    ///   exists for the same handle concurrently — the substrate
59    ///   serialises bus access internally, but constructing two
60    ///   wrappers with conflicting trait-impl state is a logic error.
61    #[inline]
62    pub const unsafe fn from_handle(handle: bindings::ove_i2c_t) -> Self {
63        Self { handle }
64    }
65
66    /// Return the underlying handle.  Useful when bridging into the
67    /// raw FFI for an operation the binding hasn't surfaced.
68    #[inline]
69    pub fn raw(&self) -> bindings::ove_i2c_t {
70        self.handle
71    }
72
73    /// Write data to an I2C device.
74    pub fn write(&self, addr: u16, data: &[u8], timeout: core::time::Duration) -> Result<()> {
75        let rc = unsafe {
76            bindings::ove_i2c_write(
77                self.handle,
78                addr,
79                data.as_ptr().cast(),
80                data.len(),
81                crate::time::dur_to_ns(timeout),
82            )
83        };
84        Error::from_code(rc)
85    }
86
87    /// Read data from an I2C device.
88    pub fn read(&self, addr: u16, buf: &mut [u8], timeout: core::time::Duration) -> Result<()> {
89        let rc = unsafe {
90            bindings::ove_i2c_read(
91                self.handle,
92                addr,
93                buf.as_mut_ptr().cast(),
94                buf.len(),
95                crate::time::dur_to_ns(timeout),
96            )
97        };
98        Error::from_code(rc)
99    }
100
101    /// Combined write-then-read with I2C repeated start.
102    pub fn write_read(
103        &self,
104        addr: u16,
105        tx: &[u8],
106        rx: &mut [u8],
107        timeout: core::time::Duration,
108    ) -> Result<()> {
109        let rc = unsafe {
110            bindings::ove_i2c_write_read(
111                self.handle,
112                addr,
113                tx.as_ptr().cast(),
114                tx.len(),
115                rx.as_mut_ptr().cast(),
116                rx.len(),
117                crate::time::dur_to_ns(timeout),
118            )
119        };
120        Error::from_code(rc)
121    }
122
123    /// Write to a single-byte-addressed register.
124    pub fn reg_write(
125        &self,
126        addr: u16,
127        reg: u8,
128        data: &[u8],
129        timeout: core::time::Duration,
130    ) -> Result<()> {
131        let rc = unsafe {
132            bindings::ove_i2c_reg_write(
133                self.handle,
134                addr,
135                reg,
136                data.as_ptr().cast(),
137                data.len(),
138                crate::time::dur_to_ns(timeout),
139            )
140        };
141        Error::from_code(rc)
142    }
143
144    /// Read from a single-byte-addressed register.
145    pub fn reg_read(
146        &self,
147        addr: u16,
148        reg: u8,
149        buf: &mut [u8],
150        timeout: core::time::Duration,
151    ) -> Result<()> {
152        let rc = unsafe {
153            bindings::ove_i2c_reg_read(
154                self.handle,
155                addr,
156                reg,
157                buf.as_mut_ptr().cast(),
158                buf.len(),
159                crate::time::dur_to_ns(timeout),
160            )
161        };
162        Error::from_code(rc)
163    }
164
165    /// Probe for a device at the given address (zero-length write, check ACK).
166    pub fn probe(&self, addr: u16, timeout: core::time::Duration) -> Result<()> {
167        let rc =
168            unsafe { bindings::ove_i2c_probe(self.handle, addr, crate::time::dur_to_ns(timeout)) };
169        Error::from_code(rc)
170    }
171}