Skip to main content

ove/
gpio.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//! GPIO control for oveRTOS.
8//!
9//! Provides pin configuration, digital read/write, and interrupt registration.
10//! Pins are identified by a [`GpioPin`] which bundles the port and pin number,
11//! preventing accidental argument swaps.
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/// A GPIO pin identified by port and pin number.
29///
30/// Bundles `(port, pin)` into a single type to prevent accidental argument
31/// swaps at the call site.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct GpioPin {
34    /// Zero-based port index.
35    pub port: u32,
36    /// Zero-based pin index within the port.
37    pub pin: u32,
38}
39
40impl GpioPin {
41    /// Create a new GPIO pin descriptor.
42    pub const fn new(port: u32, pin: u32) -> Self {
43        Self { port, pin }
44    }
45}
46
47/// GPIO pin direction/pull mode.
48#[repr(u32)]
49#[derive(Debug, Copy, Clone, PartialEq, Eq)]
50pub enum GpioMode {
51    /// Pin configured as a digital input.
52    Input = 0,
53    /// Pin configured as a push-pull output.
54    OutputPP = 1,
55    /// Pin configured as an open-drain output.
56    OutputOD = 2,
57}
58
59/// GPIO interrupt trigger mode.
60#[repr(u32)]
61#[derive(Debug, Copy, Clone, PartialEq, Eq)]
62pub enum GpioIrqMode {
63    /// Trigger interrupt on rising edge.
64    Rising = 0x01,
65    /// Trigger interrupt on falling edge.
66    Falling = 0x02,
67    /// Trigger interrupt on both edges.
68    Both = 0x03,
69}
70
71/// Configure a GPIO pin's direction and pull mode.
72///
73/// # Errors
74/// Returns [`Error::InvalidParam`] if the pin is out of range.
75pub fn configure(pin: GpioPin, mode: GpioMode) -> Result<()> {
76    let rc = unsafe {
77        bindings::ove_gpio_configure(pin.port, pin.pin, mode as bindings::ove_gpio_mode_t)
78    };
79    Error::from_code(rc)
80}
81
82/// Set a GPIO pin's output level (`value`: 0 = low, non-zero = high).
83///
84/// # Errors
85/// Returns [`Error::InvalidParam`] if the pin is out of range.
86pub fn set(pin: GpioPin, value: i32) -> Result<()> {
87    let rc = unsafe { bindings::ove_gpio_set(pin.port, pin.pin, value) };
88    Error::from_code(rc)
89}
90
91/// Read a GPIO pin input value. Returns the pin level (0 or 1) on success.
92pub fn get(pin: GpioPin) -> Result<i32> {
93    let rc = unsafe { bindings::ove_gpio_get(pin.port, pin.pin) };
94    if rc < 0 {
95        Error::from_code(rc)?;
96    }
97    Ok(rc)
98}
99
100/// Register a GPIO interrupt callback.
101///
102/// # Safety
103/// The callback and user_data must remain valid for the lifetime of the
104/// registration.
105pub unsafe fn irq_register(
106    pin: GpioPin,
107    mode: GpioIrqMode,
108    callback: bindings::ove_gpio_irq_cb,
109    user_data: *mut core::ffi::c_void,
110) -> Result<()> {
111    let rc = unsafe {
112        bindings::ove_gpio_irq_register(
113            pin.port,
114            pin.pin,
115            mode as bindings::ove_gpio_irq_mode_t,
116            callback,
117            user_data,
118        )
119    };
120    Error::from_code(rc)
121}
122
123/// Enable a previously registered GPIO interrupt.
124///
125/// # Errors
126/// Returns an error if the interrupt has not been registered or the pin is invalid.
127pub fn irq_enable(pin: GpioPin) -> Result<()> {
128    let rc = unsafe { bindings::ove_gpio_irq_enable(pin.port, pin.pin) };
129    Error::from_code(rc)
130}
131
132/// Disable a registered GPIO interrupt without removing the registration.
133///
134/// # Errors
135/// Returns an error if the interrupt has not been registered or the pin is invalid.
136pub fn irq_disable(pin: GpioPin) -> Result<()> {
137    let rc = unsafe { bindings::ove_gpio_irq_disable(pin.port, pin.pin) };
138    Error::from_code(rc)
139}
140
141// ---------------------------------------------------------------------------
142// Fixed-mode pin newtypes — `embedded_hal::digital::{OutputPin, InputPin}`
143// trait targets.  Created via constructors that pre-configure the
144// underlying pin in the appropriate mode; mode is not changed after
145// construction.  For mode-switching use cases stick with the lower-level
146// `GpioPin` + free-function API above.
147// ---------------------------------------------------------------------------
148
149/// Fixed-mode output GPIO pin.
150///
151/// Pre-configures the underlying pin as a push-pull or open-drain output
152/// at construction time.  Provides simple `set(bool)` access plus the
153/// `embedded_hal::digital::OutputPin` trait impl (behind the
154/// `embedded-hal` feature).
155#[derive(Debug, Clone, Copy)]
156pub struct OutputPin {
157    pin: GpioPin,
158}
159
160impl OutputPin {
161    /// Construct a new output pin and configure it.
162    ///
163    /// # Errors
164    /// Returns an error if `mode` isn't an output mode
165    /// ([`GpioMode::OutputPP`] or [`GpioMode::OutputOD`]) or the pin is
166    /// out of range.
167    pub fn new(port: u32, pin: u32, mode: GpioMode) -> Result<Self> {
168        if matches!(mode, GpioMode::Input) {
169            return Err(Error::InvalidParam);
170        }
171        let p = GpioPin::new(port, pin);
172        configure(p, mode)?;
173        Ok(Self { pin: p })
174    }
175
176    /// Drive the pin high (`true`) or low (`false`).
177    #[inline]
178    pub fn set(&mut self, value: bool) -> Result<()> {
179        set(self.pin, i32::from(value))
180    }
181
182    /// Drive the pin low.
183    #[inline]
184    pub fn set_low(&mut self) -> Result<()> {
185        set(self.pin, 0)
186    }
187
188    /// Drive the pin high.
189    #[inline]
190    pub fn set_high(&mut self) -> Result<()> {
191        set(self.pin, 1)
192    }
193
194    /// Return the underlying `GpioPin` (`(port, pin)`).
195    #[inline]
196    pub fn pin(&self) -> GpioPin {
197        self.pin
198    }
199}
200
201/// Fixed-mode input GPIO pin.
202///
203/// Pre-configures the underlying pin as a digital input at construction
204/// time.  Provides `is_high()` / `is_low()` access plus the
205/// `embedded_hal::digital::InputPin` trait impl (behind the
206/// `embedded-hal` feature).
207#[derive(Debug, Clone, Copy)]
208pub struct InputPin {
209    pin: GpioPin,
210}
211
212impl InputPin {
213    /// Construct a new input pin and configure it as a digital input.
214    pub fn new(port: u32, pin: u32) -> Result<Self> {
215        let p = GpioPin::new(port, pin);
216        configure(p, GpioMode::Input)?;
217        Ok(Self { pin: p })
218    }
219
220    /// Read the pin level — returns `true` if high.
221    #[inline]
222    pub fn is_high(&self) -> Result<bool> {
223        Ok(get(self.pin)? != 0)
224    }
225
226    /// Read the pin level — returns `true` if low.
227    #[inline]
228    pub fn is_low(&self) -> Result<bool> {
229        Ok(!self.is_high()?)
230    }
231
232    /// Return the underlying `GpioPin` (`(port, pin)`).
233    #[inline]
234    pub fn pin(&self) -> GpioPin {
235        self.pin
236    }
237}