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}