libgpiod/
lib.rs

1// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2022 Linaro Ltd.
3// SPDX-FileCopyrightText: 2022 Viresh Kumar <viresh.kumar@linaro.org>
4//
5// Rust wrappers for GPIOD APIs
6
7//! libgpiod public API
8//!
9//! This is the complete documentation of the public Rust API made available to
10//! users of libgpiod.
11//!
12//! The API is logically split into several parts such as: GPIO chip & line
13//! operators, GPIO events handling etc.
14
15use std::ffi::CStr;
16use std::fs;
17use std::os::raw::c_char;
18use std::path::Path;
19use std::time::Duration;
20use std::{fmt, str};
21
22use intmap::IntMap;
23use thiserror::Error as ThisError;
24
25use libgpiod_sys as gpiod;
26
27use gpiod::{
28    gpiod_edge_event_type_GPIOD_EDGE_EVENT_FALLING_EDGE as GPIOD_EDGE_EVENT_FALLING_EDGE,
29    gpiod_edge_event_type_GPIOD_EDGE_EVENT_RISING_EDGE as GPIOD_EDGE_EVENT_RISING_EDGE,
30    gpiod_info_event_type_GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED as GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED,
31    gpiod_info_event_type_GPIOD_INFO_EVENT_LINE_RELEASED as GPIOD_INFO_EVENT_LINE_RELEASED,
32    gpiod_info_event_type_GPIOD_INFO_EVENT_LINE_REQUESTED as GPIOD_INFO_EVENT_LINE_REQUESTED,
33    gpiod_line_bias_GPIOD_LINE_BIAS_AS_IS as GPIOD_LINE_BIAS_AS_IS,
34    gpiod_line_bias_GPIOD_LINE_BIAS_DISABLED as GPIOD_LINE_BIAS_DISABLED,
35    gpiod_line_bias_GPIOD_LINE_BIAS_PULL_DOWN as GPIOD_LINE_BIAS_PULL_DOWN,
36    gpiod_line_bias_GPIOD_LINE_BIAS_PULL_UP as GPIOD_LINE_BIAS_PULL_UP,
37    gpiod_line_bias_GPIOD_LINE_BIAS_UNKNOWN as GPIOD_LINE_BIAS_UNKNOWN,
38    gpiod_line_clock_GPIOD_LINE_CLOCK_HTE as GPIOD_LINE_CLOCK_HTE,
39    gpiod_line_clock_GPIOD_LINE_CLOCK_MONOTONIC as GPIOD_LINE_CLOCK_MONOTONIC,
40    gpiod_line_clock_GPIOD_LINE_CLOCK_REALTIME as GPIOD_LINE_CLOCK_REALTIME,
41    gpiod_line_direction_GPIOD_LINE_DIRECTION_AS_IS as GPIOD_LINE_DIRECTION_AS_IS,
42    gpiod_line_direction_GPIOD_LINE_DIRECTION_INPUT as GPIOD_LINE_DIRECTION_INPUT,
43    gpiod_line_direction_GPIOD_LINE_DIRECTION_OUTPUT as GPIOD_LINE_DIRECTION_OUTPUT,
44    gpiod_line_drive_GPIOD_LINE_DRIVE_OPEN_DRAIN as GPIOD_LINE_DRIVE_OPEN_DRAIN,
45    gpiod_line_drive_GPIOD_LINE_DRIVE_OPEN_SOURCE as GPIOD_LINE_DRIVE_OPEN_SOURCE,
46    gpiod_line_drive_GPIOD_LINE_DRIVE_PUSH_PULL as GPIOD_LINE_DRIVE_PUSH_PULL,
47    gpiod_line_edge_GPIOD_LINE_EDGE_BOTH as GPIOD_LINE_EDGE_BOTH,
48    gpiod_line_edge_GPIOD_LINE_EDGE_FALLING as GPIOD_LINE_EDGE_FALLING,
49    gpiod_line_edge_GPIOD_LINE_EDGE_NONE as GPIOD_LINE_EDGE_NONE,
50    gpiod_line_edge_GPIOD_LINE_EDGE_RISING as GPIOD_LINE_EDGE_RISING,
51    gpiod_line_value_GPIOD_LINE_VALUE_ACTIVE as GPIOD_LINE_VALUE_ACTIVE,
52    gpiod_line_value_GPIOD_LINE_VALUE_ERROR as GPIOD_LINE_VALUE_ERROR,
53    gpiod_line_value_GPIOD_LINE_VALUE_INACTIVE as GPIOD_LINE_VALUE_INACTIVE,
54};
55
56/// Operation types, used with OperationFailed() Error.
57#[derive(Copy, Clone, Debug, Eq, PartialEq)]
58pub enum OperationType {
59    ChipOpen,
60    ChipWaitInfoEvent,
61    ChipGetLine,
62    ChipGetLineInfo,
63    ChipGetLineOffsetFromName,
64    ChipGetInfo,
65    ChipReadInfoEvent,
66    ChipRequestLines,
67    ChipWatchLineInfo,
68    EdgeEventBufferGetEvent,
69    EdgeEventCopy,
70    EdgeEventBufferNew,
71    InfoEventGetLineInfo,
72    LineConfigNew,
73    LineConfigAddSettings,
74    LineConfigSetOutputValues,
75    LineConfigGetOffsets,
76    LineConfigGetSettings,
77    LineInfoCopy,
78    LineRequestReconfigLines,
79    LineRequestGetVal,
80    LineRequestGetValSubset,
81    LineRequestSetVal,
82    LineRequestSetValSubset,
83    LineRequestReadEdgeEvent,
84    LineRequestWaitEdgeEvent,
85    LineSettingsNew,
86    LineSettingsCopy,
87    LineSettingsGetOutVal,
88    LineSettingsSetDirection,
89    LineSettingsSetEdgeDetection,
90    LineSettingsSetBias,
91    LineSettingsSetDrive,
92    LineSettingsSetActiveLow,
93    LineSettingsSetDebouncePeriod,
94    LineSettingsSetEventClock,
95    LineSettingsSetOutputValue,
96    RequestConfigNew,
97    RequestConfigGetConsumer,
98    SimBankGetVal,
99    SimBankNew,
100    SimBankSetLabel,
101    SimBankSetNumLines,
102    SimBankSetLineName,
103    SimBankSetPull,
104    SimBankHogLine,
105    SimCtxNew,
106    SimDevNew,
107    SimDevEnable,
108    SimDevDisable,
109}
110
111impl fmt::Display for OperationType {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        write!(f, "{self:?}")
114    }
115}
116
117/// Result of libgpiod operations.
118pub type Result<T> = std::result::Result<T, Error>;
119
120/// Error codes for libgpiod operations.
121#[derive(Copy, Clone, Debug, Eq, PartialEq, ThisError)]
122pub enum Error {
123    #[error("Failed to get {0}")]
124    NullString(&'static str),
125    #[error("String not utf8: {0:?}")]
126    StringNotUtf8(str::Utf8Error),
127    #[error("Invalid String")]
128    InvalidString,
129    #[error("Invalid enum {0} value: {1}")]
130    InvalidEnumValue(&'static str, i32),
131    #[error("Operation {0} Failed: {1}")]
132    OperationFailed(OperationType, errno::Errno),
133    #[error("Invalid Arguments")]
134    InvalidArguments,
135    #[error("Event count more than buffer capacity: {0} > {1}")]
136    TooManyEvents(usize, usize),
137    #[error("Std Io Error")]
138    IoError,
139}
140
141mod info_event;
142
143/// GPIO chip related definitions.
144pub mod chip;
145
146mod edge_event;
147mod event_buffer;
148mod line_request;
149mod request_config;
150
151/// GPIO chip request related definitions.
152pub mod request {
153    pub use crate::edge_event::*;
154    pub use crate::event_buffer::*;
155    pub use crate::line_request::*;
156    pub use crate::request_config::*;
157}
158
159mod line_config;
160mod line_info;
161mod line_settings;
162
163/// GPIO chip line related definitions.
164pub mod line {
165    pub use crate::line_config::*;
166    pub use crate::line_info::*;
167    pub use crate::line_settings::*;
168
169    use super::*;
170
171    /// Value settings.
172    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
173    pub enum Value {
174        /// Active
175        Active,
176        /// Inactive
177        InActive,
178    }
179
180    /// Maps offset to Value.
181    pub type ValueMap = IntMap<Offset, Value>;
182
183    /// Maps offsets to Settings
184    pub type SettingsMap = IntMap<Offset, Settings>;
185
186    impl Value {
187        pub fn new(val: gpiod::gpiod_line_value) -> Result<Self> {
188            Ok(match val {
189                GPIOD_LINE_VALUE_INACTIVE => Value::InActive,
190                GPIOD_LINE_VALUE_ACTIVE => Value::Active,
191                GPIOD_LINE_VALUE_ERROR => {
192                    return Err(Error::OperationFailed(
193                        OperationType::LineRequestGetVal,
194                        errno::errno(),
195                    ));
196                }
197                _ => return Err(Error::InvalidEnumValue("Value", val)),
198            })
199        }
200
201        pub(crate) fn value(&self) -> gpiod::gpiod_line_value {
202            match self {
203                Value::Active => GPIOD_LINE_VALUE_ACTIVE,
204                Value::InActive => GPIOD_LINE_VALUE_INACTIVE,
205            }
206        }
207    }
208
209    /// Offset type.
210    pub type Offset = u32;
211
212    /// Direction settings.
213    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
214    pub enum Direction {
215        /// Request the line(s), but don't change direction.
216        AsIs,
217        /// Direction is input - for reading the value of an externally driven GPIO line.
218        Input,
219        /// Direction is output - for driving the GPIO line.
220        Output,
221    }
222
223    impl Direction {
224        pub(crate) fn new(dir: gpiod::gpiod_line_direction) -> Result<Self> {
225            Ok(match dir {
226                GPIOD_LINE_DIRECTION_AS_IS => Direction::AsIs,
227                GPIOD_LINE_DIRECTION_INPUT => Direction::Input,
228                GPIOD_LINE_DIRECTION_OUTPUT => Direction::Output,
229                _ => return Err(Error::InvalidEnumValue("Direction", dir as i32)),
230            })
231        }
232
233        pub(crate) fn gpiod_direction(&self) -> gpiod::gpiod_line_direction {
234            match self {
235                Direction::AsIs => GPIOD_LINE_DIRECTION_AS_IS,
236                Direction::Input => GPIOD_LINE_DIRECTION_INPUT,
237                Direction::Output => GPIOD_LINE_DIRECTION_OUTPUT,
238            }
239        }
240    }
241
242    /// Internal bias settings.
243    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
244    pub enum Bias {
245        /// The internal bias is disabled.
246        Disabled,
247        /// The internal pull-up bias is enabled.
248        PullUp,
249        /// The internal pull-down bias is enabled.
250        PullDown,
251    }
252
253    impl Bias {
254        pub(crate) fn new(bias: gpiod::gpiod_line_bias) -> Result<Option<Self>> {
255            Ok(match bias {
256                GPIOD_LINE_BIAS_UNKNOWN => None,
257                GPIOD_LINE_BIAS_AS_IS => None,
258                GPIOD_LINE_BIAS_DISABLED => Some(Bias::Disabled),
259                GPIOD_LINE_BIAS_PULL_UP => Some(Bias::PullUp),
260                GPIOD_LINE_BIAS_PULL_DOWN => Some(Bias::PullDown),
261                _ => return Err(Error::InvalidEnumValue("Bias", bias as i32)),
262            })
263        }
264
265        pub(crate) fn gpiod_bias(bias: Option<Bias>) -> gpiod::gpiod_line_bias {
266            match bias {
267                None => GPIOD_LINE_BIAS_AS_IS,
268                Some(bias) => match bias {
269                    Bias::Disabled => GPIOD_LINE_BIAS_DISABLED,
270                    Bias::PullUp => GPIOD_LINE_BIAS_PULL_UP,
271                    Bias::PullDown => GPIOD_LINE_BIAS_PULL_DOWN,
272                },
273            }
274        }
275    }
276
277    /// Drive settings.
278    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
279    pub enum Drive {
280        /// Drive setting is push-pull.
281        PushPull,
282        /// Line output is open-drain.
283        OpenDrain,
284        /// Line output is open-source.
285        OpenSource,
286    }
287
288    impl Drive {
289        pub(crate) fn new(drive: gpiod::gpiod_line_drive) -> Result<Self> {
290            Ok(match drive {
291                GPIOD_LINE_DRIVE_PUSH_PULL => Drive::PushPull,
292                GPIOD_LINE_DRIVE_OPEN_DRAIN => Drive::OpenDrain,
293                GPIOD_LINE_DRIVE_OPEN_SOURCE => Drive::OpenSource,
294                _ => return Err(Error::InvalidEnumValue("Drive", drive as i32)),
295            })
296        }
297
298        pub(crate) fn gpiod_drive(&self) -> gpiod::gpiod_line_drive {
299            match self {
300                Drive::PushPull => GPIOD_LINE_DRIVE_PUSH_PULL,
301                Drive::OpenDrain => GPIOD_LINE_DRIVE_OPEN_DRAIN,
302                Drive::OpenSource => GPIOD_LINE_DRIVE_OPEN_SOURCE,
303            }
304        }
305    }
306
307    /// Edge detection settings.
308    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
309    pub enum Edge {
310        /// Line detects rising edge events.
311        Rising,
312        /// Line detects falling edge events.
313        Falling,
314        /// Line detects both rising and falling edge events.
315        Both,
316    }
317
318    impl Edge {
319        pub(crate) fn new(edge: gpiod::gpiod_line_edge) -> Result<Option<Self>> {
320            Ok(match edge {
321                GPIOD_LINE_EDGE_NONE => None,
322                GPIOD_LINE_EDGE_RISING => Some(Edge::Rising),
323                GPIOD_LINE_EDGE_FALLING => Some(Edge::Falling),
324                GPIOD_LINE_EDGE_BOTH => Some(Edge::Both),
325                _ => return Err(Error::InvalidEnumValue("Edge", edge as i32)),
326            })
327        }
328
329        pub(crate) fn gpiod_edge(edge: Option<Edge>) -> gpiod::gpiod_line_edge {
330            match edge {
331                None => GPIOD_LINE_EDGE_NONE,
332                Some(edge) => match edge {
333                    Edge::Rising => GPIOD_LINE_EDGE_RISING,
334                    Edge::Falling => GPIOD_LINE_EDGE_FALLING,
335                    Edge::Both => GPIOD_LINE_EDGE_BOTH,
336                },
337            }
338        }
339    }
340
341    /// Line setting kind.
342    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
343    pub enum SettingKind {
344        /// Line direction.
345        Direction,
346        /// Bias.
347        Bias,
348        /// Drive.
349        Drive,
350        /// Edge detection.
351        EdgeDetection,
352        /// Active-low setting.
353        ActiveLow,
354        /// Debounce period.
355        DebouncePeriod,
356        /// Event clock type.
357        EventClock,
358        /// Output value.
359        OutputValue,
360    }
361
362    /// Line settings.
363    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
364    pub enum SettingVal {
365        /// Line direction.
366        Direction(Direction),
367        /// Bias.
368        Bias(Option<Bias>),
369        /// Drive.
370        Drive(Drive),
371        /// Edge detection.
372        EdgeDetection(Option<Edge>),
373        /// Active-low setting.
374        ActiveLow(bool),
375        /// Debounce period.
376        DebouncePeriod(Duration),
377        /// Event clock type.
378        EventClock(EventClock),
379        /// Output value.
380        OutputValue(Value),
381    }
382
383    impl fmt::Display for SettingVal {
384        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385            write!(f, "{self:?}")
386        }
387    }
388
389    /// Event clock settings.
390    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
391    pub enum EventClock {
392        /// Line uses the monotonic clock for edge event timestamps.
393        Monotonic,
394        /// Line uses the realtime clock for edge event timestamps.
395        Realtime,
396        /// Line uses the hardware timestamp engine clock for edge event timestamps.
397        HTE,
398    }
399
400    impl EventClock {
401        pub(crate) fn new(clock: gpiod::gpiod_line_clock) -> Result<Self> {
402            Ok(match clock {
403                GPIOD_LINE_CLOCK_MONOTONIC => EventClock::Monotonic,
404                GPIOD_LINE_CLOCK_REALTIME => EventClock::Realtime,
405                GPIOD_LINE_CLOCK_HTE => EventClock::HTE,
406                _ => return Err(Error::InvalidEnumValue("Eventclock", clock as i32)),
407            })
408        }
409
410        pub(crate) fn gpiod_clock(&self) -> gpiod::gpiod_line_clock {
411            match self {
412                EventClock::Monotonic => GPIOD_LINE_CLOCK_MONOTONIC,
413                EventClock::Realtime => GPIOD_LINE_CLOCK_REALTIME,
414                EventClock::HTE => GPIOD_LINE_CLOCK_HTE,
415            }
416        }
417    }
418
419    /// Line status change event types.
420    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
421    pub enum InfoChangeKind {
422        /// Line has been requested.
423        LineRequested,
424        /// Previously requested line has been released.
425        LineReleased,
426        /// Line configuration has changed.
427        LineConfigChanged,
428    }
429
430    impl InfoChangeKind {
431        pub(crate) fn new(kind: gpiod::gpiod_info_event_type) -> Result<Self> {
432            Ok(match kind {
433                GPIOD_INFO_EVENT_LINE_REQUESTED => InfoChangeKind::LineRequested,
434                GPIOD_INFO_EVENT_LINE_RELEASED => InfoChangeKind::LineReleased,
435                GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED => InfoChangeKind::LineConfigChanged,
436                _ => return Err(Error::InvalidEnumValue("InfoChangeKind", kind as i32)),
437            })
438        }
439    }
440
441    /// Edge event types.
442    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
443    pub enum EdgeKind {
444        /// Rising edge event.
445        Rising,
446        /// Falling edge event.
447        Falling,
448    }
449
450    impl EdgeKind {
451        pub(crate) fn new(kind: gpiod::gpiod_edge_event_type) -> Result<Self> {
452            Ok(match kind {
453                GPIOD_EDGE_EVENT_RISING_EDGE => EdgeKind::Rising,
454                GPIOD_EDGE_EVENT_FALLING_EDGE => EdgeKind::Falling,
455                _ => return Err(Error::InvalidEnumValue("EdgeEvent", kind as i32)),
456            })
457        }
458    }
459}
460
461// Various libgpiod-related functions.
462
463/// Check if the file pointed to by path is a GPIO chip character device.
464///
465/// Returns true if the file exists and is a GPIO chip character device or a
466/// symbolic link to it.
467pub fn is_gpiochip_device<P: AsRef<Path>>(path: &P) -> bool {
468    // Null-terminate the string
469    let path = path.as_ref().to_string_lossy() + "\0";
470
471    // SAFETY: libgpiod won't access the path reference once the call returns.
472    unsafe { gpiod::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) }
473}
474
475/// GPIO devices.
476///
477/// Returns a vector of unique available GPIO Chips.
478///
479/// The chips are sorted in ascending order of the chip names.
480pub fn gpiochip_devices<P: AsRef<Path>>(path: &P) -> Result<Vec<chip::Chip>> {
481    let mut devices = Vec::new();
482
483    for entry in fs::read_dir(path).map_err(|_| Error::IoError)?.flatten() {
484        let path = entry.path();
485
486        if is_gpiochip_device(&path) {
487            let chip = chip::Chip::open(&path)?;
488            let info = chip.info()?;
489
490            devices.push((chip, info));
491        }
492    }
493
494    devices.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
495    devices.dedup_by(|a, b| a.1.eq(&b.1));
496
497    Ok(devices.into_iter().map(|a| a.0).collect())
498}
499
500/// Get the API version of the libgpiod library as a human-readable string.
501pub fn libgpiod_version() -> Result<&'static str> {
502    // SAFETY: The string returned by libgpiod is guaranteed to live forever.
503    let version = unsafe { gpiod::gpiod_api_version() };
504
505    if version.is_null() {
506        return Err(Error::NullString("GPIO library version"));
507    }
508
509    // SAFETY: The string is guaranteed to be valid here by the C API.
510    unsafe { CStr::from_ptr(version) }
511        .to_str()
512        .map_err(Error::StringNotUtf8)
513}
514
515/// Get the API version of the libgpiod crate as a human-readable string.
516pub fn crate_version() -> &'static str {
517    env!("CARGO_PKG_VERSION")
518}