1use 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#[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
117pub type Result<T> = std::result::Result<T, Error>;
119
120#[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
143pub mod chip;
145
146mod edge_event;
147mod event_buffer;
148mod line_request;
149mod request_config;
150
151pub 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
163pub 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
173 pub enum Value {
174 Active,
176 InActive,
178 }
179
180 pub type ValueMap = IntMap<Offset, Value>;
182
183 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 pub type Offset = u32;
211
212 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
214 pub enum Direction {
215 AsIs,
217 Input,
219 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
244 pub enum Bias {
245 Disabled,
247 PullUp,
249 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
279 pub enum Drive {
280 PushPull,
282 OpenDrain,
284 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
309 pub enum Edge {
310 Rising,
312 Falling,
314 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
343 pub enum SettingKind {
344 Direction,
346 Bias,
348 Drive,
350 EdgeDetection,
352 ActiveLow,
354 DebouncePeriod,
356 EventClock,
358 OutputValue,
360 }
361
362 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
364 pub enum SettingVal {
365 Direction(Direction),
367 Bias(Option<Bias>),
369 Drive(Drive),
371 EdgeDetection(Option<Edge>),
373 ActiveLow(bool),
375 DebouncePeriod(Duration),
377 EventClock(EventClock),
379 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
391 pub enum EventClock {
392 Monotonic,
394 Realtime,
396 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
421 pub enum InfoChangeKind {
422 LineRequested,
424 LineReleased,
426 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 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
443 pub enum EdgeKind {
444 Rising,
446 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
461pub fn is_gpiochip_device<P: AsRef<Path>>(path: &P) -> bool {
468 let path = path.as_ref().to_string_lossy() + "\0";
470
471 unsafe { gpiod::gpiod_is_gpiochip_device(path.as_ptr() as *const c_char) }
473}
474
475pub 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
500pub fn libgpiod_version() -> Result<&'static str> {
502 let version = unsafe { gpiod::gpiod_api_version() };
504
505 if version.is_null() {
506 return Err(Error::NullString("GPIO library version"));
507 }
508
509 unsafe { CStr::from_ptr(version) }
511 .to_str()
512 .map_err(Error::StringNotUtf8)
513}
514
515pub fn crate_version() -> &'static str {
517 env!("CARGO_PKG_VERSION")
518}