gpiod_core/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[cfg(not(target_os = "linux"))]
4compile_error!("This crate support Linux only");
5
6mod iop;
7mod raw;
8mod types;
9mod utils;
10mod values;
11
12use std::{fmt, os::unix::io::RawFd};
13
14pub use iop::RawEvent;
15pub use std::{
16    io::{Error, Result},
17    path::{Path, PathBuf},
18    time::Duration as Time,
19};
20pub use types::{
21    Active, Bias, BitId, Direction, Drive, Edge, EdgeDetect, Event, LineId, LineInfo, LineMap,
22};
23pub use utils::*;
24pub use values::{AsValues, AsValuesMut, Bits, Masked, Values, MAX_BITS, MAX_VALUES};
25
26macro_rules! unsafe_call {
27    ($res:expr) => {
28        unsafe { $res }.map_err(Error::from)
29    };
30}
31
32/// Device path trait
33pub trait AsDevicePath {
34    /// Get absolute device path
35    fn as_device_path(&self) -> PathBuf;
36}
37
38impl AsDevicePath for Path {
39    fn as_device_path(&self) -> PathBuf {
40        if self.is_absolute() {
41            self.to_path_buf()
42        } else {
43            Path::new("/dev").join(self)
44        }
45    }
46}
47
48impl AsDevicePath for &Path {
49    fn as_device_path(&self) -> PathBuf {
50        if self.is_absolute() {
51            self.to_path_buf()
52        } else {
53            Path::new("/dev").join(self)
54        }
55    }
56}
57
58impl AsDevicePath for PathBuf {
59    fn as_device_path(&self) -> PathBuf {
60        let path: &Path = self;
61        path.as_device_path()
62    }
63}
64
65impl AsDevicePath for &PathBuf {
66    fn as_device_path(&self) -> PathBuf {
67        let path: &Path = self;
68        path.as_device_path()
69    }
70}
71
72impl AsDevicePath for &str {
73    fn as_device_path(&self) -> PathBuf {
74        Path::new(self).as_device_path()
75    }
76}
77
78impl AsDevicePath for String {
79    fn as_device_path(&self) -> PathBuf {
80        let s: &str = self;
81        s.as_device_path()
82    }
83}
84
85impl AsDevicePath for &String {
86    fn as_device_path(&self) -> PathBuf {
87        let s: &str = self;
88        s.as_device_path()
89    }
90}
91
92impl AsDevicePath for usize {
93    fn as_device_path(&self) -> PathBuf {
94        format!("/dev/gpiochip{self}").as_device_path()
95    }
96}
97
98macro_rules! as_device_path {
99    ($($type:ty),*) => {
100        $(
101            impl AsDevicePath for $type {
102                fn as_device_path(&self) -> PathBuf {
103                    (*self as usize).as_device_path()
104                }
105            }
106        )*
107    };
108}
109
110as_device_path!(u8, u16, u32, u64, i8, i16, i32, i64, isize);
111
112/// Wrapper to hide internals
113#[derive(Clone, Copy, Default)]
114pub struct Internal<T>(T);
115
116impl<T> core::ops::Deref for Internal<T> {
117    type Target = T;
118
119    fn deref(&self) -> &Self::Target {
120        &self.0
121    }
122}
123
124impl<T> core::ops::DerefMut for Internal<T> {
125    fn deref_mut(&mut self) -> &mut Self::Target {
126        &mut self.0
127    }
128}
129
130/// GPIO lines values interface info
131pub struct ValuesInfo {
132    chip_name: String,
133    consumer: String,
134    lines: Vec<LineId>,
135    index: LineMap,
136}
137
138impl fmt::Display for ValuesInfo {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        write!(f, "{} {:?} {:?}", self.chip_name, self.consumer, self.lines)
141    }
142}
143
144impl ValuesInfo {
145    /// Get associated chip name
146    pub fn chip_name(&self) -> &str {
147        &self.chip_name
148    }
149
150    /// Get consumer string
151    pub fn consumer(&self) -> &str {
152        &self.consumer
153    }
154
155    /// Get offsets of requested lines
156    pub fn lines(&self) -> &[LineId] {
157        &self.lines
158    }
159
160    /// Get offset to bit position mapping
161    pub fn index(&self) -> &LineMap {
162        &self.index
163    }
164}
165
166impl Internal<ValuesInfo> {
167    fn new(chip_name: &str, consumer: &str, lines: &[LineId]) -> Self {
168        let chip_name = chip_name.into();
169        let consumer = consumer.into();
170        let index = LineMap::new(lines);
171        let lines = lines.to_owned();
172
173        Self(ValuesInfo {
174            chip_name,
175            consumer,
176            lines,
177            index,
178        })
179    }
180
181    pub fn get_values<T: AsValuesMut>(&self, fd: RawFd, values: &mut T) -> Result<()> {
182        #[cfg(not(feature = "v2"))]
183        {
184            let mut data = raw::v1::GpioHandleData::default();
185
186            unsafe_call!(raw::v1::gpio_get_line_values(fd, &mut data))?;
187
188            data.fill_values(self.lines.len(), values);
189        }
190
191        #[cfg(feature = "v2")]
192        {
193            let mut data = values.convert::<Values>();
194            data.truncate(self.lines.len() as _);
195
196            unsafe_call!(raw::v2::gpio_line_get_values(fd, data.as_mut()))?;
197
198            data.copy_into(values);
199        }
200
201        Ok(())
202    }
203
204    pub fn set_values<T: AsValues>(&self, fd: RawFd, values: T) -> Result<()> {
205        #[cfg(not(feature = "v2"))]
206        {
207            let mut data = raw::v1::GpioHandleData::from_values(self.lines.len(), values);
208
209            unsafe_call!(raw::v1::gpio_set_line_values(fd, &mut data))?;
210        }
211
212        #[cfg(feature = "v2")]
213        {
214            let mut data = values.convert::<Values>();
215            data.truncate(self.lines.len() as _);
216
217            unsafe_call!(raw::v2::gpio_line_set_values(fd, data.as_mut()))?;
218        }
219
220        Ok(())
221    }
222}
223
224/// Direction trait
225pub trait DirectionType: Send + Sync + 'static {
226    const DIR: Direction;
227}
228
229/// Input direction
230pub struct Input;
231
232impl DirectionType for Input {
233    const DIR: Direction = Direction::Input;
234}
235
236/// Output direction
237pub struct Output;
238
239impl DirectionType for Output {
240    const DIR: Direction = Direction::Output;
241}
242
243/// GPIO line values request options
244///
245/// Input config:
246/// ```
247/// # use gpiod_core::{Options, Active, Bias};
248/// let input = Options::input(&[23, 17, 3])
249///     .active(Active::Low)
250///     .bias(Bias::PullUp)
251///     .consumer("my inputs");
252/// ```
253///
254/// Output config:
255/// ```
256/// # use gpiod_core::{Options, Active, Drive};
257/// let output = Options::output(&[11, 20])
258///     .active(Active::Low)
259///     .drive(Drive::PushPull)
260///     .values([false, true])
261///     .consumer("my outputs");
262/// ```
263///
264/// Input with edge detection:
265/// ```
266/// # use gpiod_core::{Options, Active, Bias, EdgeDetect};
267/// let input = Options::input(&[21, 13])
268///     .active(Active::Low)
269///     .bias(Bias::PullUp)
270///     .edge(EdgeDetect::Both)
271///     .consumer("my inputs");
272/// ```
273pub struct Options<Direction = (), Lines = (), Consumer = ()> {
274    lines: Lines,
275    direction: core::marker::PhantomData<Direction>,
276    active: Active,
277    edge: Option<EdgeDetect>,
278    bias: Option<Bias>,
279    drive: Option<Drive>,
280    values: Option<Values>,
281    consumer: Consumer,
282}
283
284impl Options {
285    /// Create input options
286    pub fn input<Lines: AsRef<[LineId]>>(lines: Lines) -> Options<Input, Lines, &'static str> {
287        Options::<Input, Lines, &'static str> {
288            lines,
289            direction: Default::default(),
290            active: Default::default(),
291            edge: Default::default(),
292            bias: Default::default(),
293            drive: Default::default(),
294            values: Default::default(),
295            consumer: "",
296        }
297    }
298
299    /// Create output options
300    pub fn output<Lines: AsRef<[LineId]>>(lines: Lines) -> Options<Output, Lines, &'static str> {
301        Options::<Output, Lines, &'static str> {
302            lines,
303            direction: Default::default(),
304            active: Default::default(),
305            edge: Default::default(),
306            bias: Default::default(),
307            drive: Default::default(),
308            values: Default::default(),
309            consumer: "",
310        }
311    }
312}
313
314impl<Direction, Lines, OldConsumer> Options<Direction, Lines, OldConsumer> {
315    /// Configure consumer string
316    pub fn consumer<Consumer: AsRef<str>>(
317        self,
318        consumer: Consumer,
319    ) -> Options<Direction, Lines, Consumer> {
320        Options::<Direction, Lines, Consumer> {
321            lines: self.lines,
322            direction: self.direction,
323            active: self.active,
324            edge: self.edge,
325            bias: self.bias,
326            drive: self.drive,
327            values: self.values,
328            consumer,
329        }
330    }
331}
332
333impl<Direction, Lines, Consumer> Options<Direction, Lines, Consumer> {
334    /// Configure GPIO lines astive state
335    ///
336    /// Available both for inputs and outputs
337    pub fn active(mut self, active: Active) -> Self {
338        self.active = active;
339        self
340    }
341
342    /// Configure GPIO lines bias
343    ///
344    /// Available both for inputs and outputs
345    pub fn bias(mut self, bias: Bias) -> Self {
346        self.bias = Some(bias);
347        self
348    }
349}
350
351impl<Direction, Lines: AsRef<[LineId]>, Consumer: AsRef<str>> Options<Direction, Lines, Consumer> {
352    /// Make an independent copy of options
353    pub fn to_owned(&self) -> Options<Direction, Vec<LineId>, String> {
354        Options::<Direction, Vec<LineId>, String> {
355            lines: self.lines.as_ref().to_owned(),
356            direction: self.direction,
357            active: self.active,
358            edge: self.edge,
359            bias: self.bias,
360            drive: self.drive,
361            values: self.values,
362            consumer: self.consumer.as_ref().to_owned(),
363        }
364    }
365}
366
367impl<Lines, Consumer> Options<Input, Lines, Consumer> {
368    /// Configure edge detection
369    ///
370    /// Available only for inputs
371    pub fn edge(mut self, edge: EdgeDetect) -> Self {
372        self.edge = Some(edge);
373        self
374    }
375}
376
377impl<Lines, Consumer> Options<Output, Lines, Consumer> {
378    /// Configure edge detection
379    ///
380    /// Available only for outputs
381    pub fn drive(mut self, drive: Drive) -> Self {
382        self.drive = Some(drive);
383        self
384    }
385
386    /// Configure default values
387    ///
388    /// Available only for outputs
389    pub fn values<T: AsValues>(mut self, values: T) -> Self {
390        self.values = Some(values.convert());
391        self
392    }
393}
394
395/// GPIO chip interface info
396pub struct ChipInfo {
397    name: String,
398    label: String,
399    num_lines: LineId,
400}
401
402impl fmt::Display for ChipInfo {
403    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404        write!(
405            f,
406            "{} [{}] ({} lines)",
407            self.name, self.label, self.num_lines
408        )
409    }
410}
411
412impl ChipInfo {
413    /// Get chip name
414    pub fn name(&self) -> &str {
415        &self.name
416    }
417
418    /// Get chip label
419    pub fn label(&self) -> &str {
420        &self.label
421    }
422
423    /// Get number of GPIO lines
424    pub fn num_lines(&self) -> LineId {
425        self.num_lines
426    }
427}
428
429impl Internal<ChipInfo> {
430    pub fn from_fd(fd: RawFd) -> Result<Self> {
431        let mut info = raw::GpioChipInfo::default();
432
433        unsafe_call!(raw::gpio_get_chip_info(fd, &mut info))?;
434
435        Ok(Self(ChipInfo {
436            name: safe_get_str(&info.name)?.into(),
437            label: safe_get_str(&info.label)?.into(),
438            num_lines: info.lines,
439        }))
440    }
441
442    /// Request the info of a specific GPIO line.
443    pub fn line_info(&self, fd: RawFd, line: LineId) -> Result<LineInfo> {
444        #[cfg(not(feature = "v2"))]
445        {
446            let mut info = raw::v1::GpioLineInfo {
447                line_offset: line,
448                ..Default::default()
449            };
450
451            unsafe_call!(raw::v1::gpio_get_line_info(fd, &mut info))?;
452
453            info.as_info()
454        }
455
456        #[cfg(feature = "v2")]
457        {
458            let mut info = raw::v2::GpioLineInfo::default();
459
460            info.offset = line;
461
462            unsafe_call!(raw::v2::gpio_get_line_info(fd, &mut info))?;
463
464            info.as_info()
465        }
466    }
467
468    /// Request the GPIO chip to configure the lines passed as argument as outputs
469    ///
470    /// Calling this operation is a precondition to being able to set the state of the GPIO lines.
471    /// All the lines passed in one request must share the output mode and the active state.
472    pub fn request_lines<Direction: DirectionType>(
473        &self,
474        fd: RawFd,
475        options: Options<Direction, impl AsRef<[LineId]>, impl AsRef<str>>,
476    ) -> Result<(Internal<ValuesInfo>, RawFd)> {
477        let Options {
478            lines,
479            direction: _,
480            active,
481            edge,
482            bias,
483            drive,
484            values,
485            consumer,
486        } = options;
487
488        let direction = Direction::DIR;
489        let lines = lines.as_ref();
490        let consumer = consumer.as_ref();
491
492        #[cfg(not(feature = "v2"))]
493        let fd = {
494            let mut request =
495                raw::v1::GpioHandleRequest::new(lines, direction, active, bias, drive, consumer)?;
496
497            // TODO: edge detection
498
499            unsafe_call!(raw::v1::gpio_get_line_handle(fd, &mut request))?;
500
501            if let Some(values) = values {
502                let mut data = raw::v1::GpioHandleData::from_values(lines.len(), &values);
503
504                unsafe_call!(raw::v1::gpio_set_line_values(fd, &mut data))?;
505            }
506
507            request.fd
508        };
509
510        #[cfg(feature = "v2")]
511        let fd = {
512            let mut request = raw::v2::GpioLineRequest::new(
513                lines, direction, active, edge, bias, drive, values, consumer,
514            )?;
515
516            unsafe_call!(raw::v2::gpio_get_line(fd, &mut request))?;
517
518            request.fd
519        };
520
521        Ok((Internal::<ValuesInfo>::new(&self.name, consumer, lines), fd))
522    }
523}
524
525#[cfg(test)]
526mod test {
527    use super::*;
528
529    #[test]
530    fn input_options() {
531        let _ = Options::input([27, 1, 19])
532            .bias(Bias::PullUp)
533            .active(Active::Low)
534            .edge(EdgeDetect::Both)
535            .consumer("gpin");
536    }
537
538    #[test]
539    fn output_options() {
540        let _ = Options::output([11, 2])
541            .bias(Bias::PullUp)
542            .active(Active::Low)
543            .consumer("gpout")
544            .drive(Drive::OpenDrain)
545            .values([true, false]);
546    }
547}