gpiocdev_uapi/
v1.rs

1// SPDX-FileCopyrightText: 2021 Kent Gibson <warthog618@gmail.com>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5use bitflags::bitflags;
6use std::fs::File;
7use std::os::unix::prelude::{AsRawFd, FromRawFd};
8
9// common to ABI v1 and v2.
10pub use super::common::*;
11
12#[repr(u8)]
13enum Ioctl {
14    GetLineInfo = 2,
15    GetLineHandle = 3,
16    GetLineEvent = 4,
17    GetLineValues = 8,
18    SetLineValues = 9,
19    SetConfig = 0xA,
20    WatchLineInfo = 0xB,
21}
22
23/// Information about a certain GPIO line.
24#[repr(C)]
25#[derive(Clone, Debug, Default, Eq, PartialEq)]
26pub struct LineInfo {
27    /// The line offset on this GPIO device.
28    /// This is the identifier used when requesting the line from the kernel.
29    pub offset: Offset,
30
31    /// The configuration flags for this line.
32    pub flags: LineInfoFlags,
33
34    /// The name of this GPIO line, such as the output pin of the line on the
35    /// chip, a rail or a pin header name on a board, as specified by the GPIO
36    /// chip.
37    ///
38    /// May be empty.
39    pub name: Name,
40
41    /// A functional name for the consumer of this GPIO line as set by
42    /// whatever is using it.
43    ///
44    /// Will be empty if there is no current user but may
45    /// also be empty if the consumer doesn't set a consumer name.
46    pub consumer: Name,
47}
48
49bitflags! {
50    /// Flags indicating the configuration of a line.
51    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
52    pub struct LineInfoFlags: u32 {
53        /// The line is in use and is not available for request.
54        const USED = 1;
55
56        /// The line is an output.
57        const OUTPUT = 2;
58
59        /// The line active state corresponds to a physical low.
60        const ACTIVE_LOW = 4;
61
62        /// The line is an open drain output.
63        const OPEN_DRAIN = 8;
64
65        /// The line is an open source output.
66        const OPEN_SOURCE = 16;
67
68        /// The line has pull-up bias enabled.
69        const BIAS_PULL_UP = 32;
70
71        /// The line has pull-down bias enabled.
72        const BIAS_PULL_DOWN = 64;
73
74        /// The line has bias disabled.
75        const BIAS_DISABLED = 128;
76    }
77}
78
79/// Get the publicly available information for a line.
80///
81/// This does not include the line value.
82/// The line must be requested to access the value.
83///
84/// * 'cf' - The open gpiochip device file.
85/// * `offset` - The offset of the line.
86#[inline]
87pub fn get_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
88    let mut li = LineInfo {
89        offset,
90        ..Default::default()
91    };
92    // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
93    match unsafe { libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::GetLineInfo, LineInfo), &mut li) } {
94        0 => Ok(li),
95        _ => Err(Error::from_errno()),
96    }
97}
98
99/// Add a watch on changes to the [`LineInfo`] for a line.
100///
101/// Returns the current state of that information.
102/// This does not include the line value.
103/// The line must be requested to access the value.
104///
105/// * 'cf' - The open gpiochip device file.
106/// * `offset` - The offset of the line to watch.
107#[inline]
108pub fn watch_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
109    let mut li = LineInfo {
110        offset,
111        ..Default::default()
112    };
113    // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
114    match unsafe {
115        libc::ioctl(
116            cf.as_raw_fd(),
117            iorw!(Ioctl::WatchLineInfo, LineInfo),
118            &mut li,
119        )
120    } {
121        0 => Ok(li),
122        _ => Err(Error::from_errno()),
123    }
124}
125
126/// Information about a change in status of a GPIO line.
127#[repr(C)]
128#[derive(Clone, Debug, Eq, PartialEq)]
129pub struct LineInfoChangeEvent {
130    /// Updated line information.
131    pub info: LineInfo,
132
133    /// An estimate of time of status change occurrence, in nanoseconds.
134    pub timestamp_ns: u64,
135
136    /// The kind of change event (LineInfoChangeKind).
137    pub kind: u32,
138
139    /// Reserved for future use.
140    #[doc(hidden)]
141    pub padding: Padding<5>,
142}
143
144impl LineInfoChangeEvent {
145    /// Read a LineInfoChangeEvent from a buffer.
146    ///
147    /// The buffer is assumed to have been populated by a read of the chip File,
148    /// so the content is initialised.
149    pub fn from_slice(d: &[u64]) -> Result<&LineInfoChangeEvent> {
150        debug_assert!(std::mem::size_of::<LineInfoChangeEvent>() % 8 == 0);
151        let len = d.len() * 8;
152        if len < std::mem::size_of::<LineInfoChangeEvent>() {
153            return Err(Error::from(UnderReadError::new(
154                "LineInfoChangeEvent",
155                std::mem::size_of::<LineInfoChangeEvent>(),
156                len,
157            )));
158        }
159        // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
160        let le = unsafe { &*(d as *const [u64] as *const LineInfoChangeEvent) };
161        Ok(le)
162    }
163
164    /// The number of u64 words required to store a LineInfoChangeEvent.
165    pub fn u64_size() -> usize {
166        std::mem::size_of::<LineInfoChangeEvent>() / 8
167    }
168}
169
170/// Information about a GPIO line handle request.
171#[repr(C)]
172#[derive(Clone, Debug, Default, Eq, PartialEq)]
173pub struct HandleRequest {
174    /// An array of requested lines, identified by offset on the associated GPIO device.
175    pub offsets: Offsets,
176
177    /// The requested flags for the requested GPIO lines.
178    ///
179    /// Note that even if multiple lines are requested, the same flags must be applicable
180    /// to all of them, if you want lines with individual flags set, request them one by one.
181    /// It is possible to select a batch of input or output lines, but they must all
182    /// have the same characteristics, i.e. all inputs or all outputs, all active low etc.
183    pub flags: HandleRequestFlags,
184
185    /// If the [`HandleRequestFlags::OUTPUT`] is set for a requested line, this specifies the
186    /// output value for each offset.  Should be 0 (*inactive*) or 1 (*active*).
187    /// Anything other than 0 or 1 is interpreted as 1 (*active*).
188    pub values: LineValues,
189
190    /// A requested consumer label for the selected GPIO line(s) such as "*my-bitbanged-relay*".
191    pub consumer: Name,
192
193    /// The number of lines requested in this request, i.e. the number of valid fields in
194    /// the `offsets` and `values` arrays.
195    ///
196    /// Set to 1 to request a single line.
197    pub num_lines: u32,
198
199    /// This field is only present for the underlying ioctl call and is only used internally.
200    //
201    // This is actually specified as an int in gpio.h, but that presents problems
202    // as it is not fixed width.  It is usually i32, so that is what we go with here,
203    // but that may cause issues on platforms.
204    #[doc(hidden)]
205    pub fd: i32,
206}
207
208bitflags! {
209    /// Configuration flags for requested lines.
210    ///
211    /// Note that several of the flags, such as BIAS_PULL_UP and BIAS_PULL_DOWN are mutually
212    /// exclusive.  The kernel will reject requests with flag combinations that do not make
213    /// sense.
214    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
215    pub struct HandleRequestFlags: u32 {
216        /// Requests line as an input.
217        const INPUT = 1;
218
219        /// Requests line as an output.
220        const OUTPUT = 2;
221
222        /// Requests line as active low.
223        const ACTIVE_LOW = 4;
224
225        /// Requests line as open drain.
226        const OPEN_DRAIN = 8;
227
228        /// Requests line as open source.
229        const OPEN_SOURCE = 16;
230
231        /// Requests line with pull-up bias.
232        const BIAS_PULL_UP = 32;
233
234        /// Requests line with pull-down bias.
235        const BIAS_PULL_DOWN = 64;
236
237        /// Requests line with bias disabled.
238        const BIAS_DISABLED = 128;
239    }
240}
241
242/// Request a line or set of lines for exclusive access.
243///
244/// * 'cf' - The open gpiochip device file.
245/// * `hr` - The line handle request.
246#[inline]
247pub fn get_line_handle(cf: &File, mut hr: HandleRequest) -> Result<File> {
248    // SAFETY: hr is consumed and the returned file is drawn from the returned fd.
249    unsafe {
250        match libc::ioctl(
251            cf.as_raw_fd(),
252            iorw!(Ioctl::GetLineHandle, HandleRequest),
253            &mut hr,
254        ) {
255            0 => Ok(File::from_raw_fd(hr.fd)),
256            _ => Err(Error::from_errno()),
257        }
258    }
259}
260
261/// Updated configuration for an existing GPIO handle request.
262#[repr(C)]
263#[derive(Clone, Debug, Default, Eq, PartialEq)]
264pub struct HandleConfig {
265    /// Updated flags for the requested GPIO lines.
266    ///
267    /// The flags will be applied to all lines in the existing request.
268    pub flags: HandleRequestFlags,
269
270    /// If the [`HandleRequestFlags::OUTPUT`] is set in flags, this specifies the
271    /// output value, should be 0 (*inactive*) or 1 (*active*).
272    ///
273    /// All other values are interpreted as active.
274    pub values: LineValues,
275
276    /// Reserved for future use and should be zero filled.
277    #[doc(hidden)]
278    pub padding: Padding<4>,
279}
280
281/// Update the configuration of an existing handle or event request.
282///
283/// * `lf` - The request file returned by [`get_line_handle`].
284/// * `hc` - The configuration to be applied.
285#[inline]
286pub fn set_line_config(lf: &File, hc: HandleConfig) -> Result<()> {
287    // SAFETY: hc is consumed.
288    unsafe {
289        match libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::SetConfig, HandleConfig), &hc) {
290            0 => Ok(()),
291            _ => Err(Error::from_errno()),
292        }
293    }
294}
295
296/// The logical values of the requested lines.
297///
298/// Values are stored as u8, as that is what the uAPI specifies.
299///
300/// 0 is *inactive* with 1 and all other values taken as *active*.
301///
302/// Values are stored in the same order as the offsets in the [`HandleRequest.offsets`].
303///
304/// Values for input lines are ignored.
305///
306/// [`HandleRequest.offsets`]: struct@HandleRequest
307#[repr(C)]
308#[derive(Clone, Copy, Debug, Eq, PartialEq)]
309pub struct LineValues(pub [u8; 64usize]);
310
311impl LineValues {
312    /// Create values from a slice.
313    ///
314    /// The values are in the same order as [`HandleRequest.offsets`].
315    ///
316    /// [`HandleRequest.offsets`]: struct@HandleRequest
317    pub fn from_slice(s: &[u8]) -> Self {
318        let mut n: LineValues = Default::default();
319        for (src, dst) in s.iter().zip(n.0.iter_mut()) {
320            *dst = *src;
321        }
322        n
323    }
324
325    /// Copy values from an iterable list - in order of requested offsets.
326    pub fn copy_from_slice(&mut self, s: &[u8]) {
327        let extent = std::cmp::min(64usize, s.len());
328        self.0[0..extent].copy_from_slice(s);
329    }
330
331    /// Return the value of a line.
332    ///
333    /// Note that the [`LineValues`] need to be populated via a call to [`get_line_values`]
334    /// to get values from the underlying hardware.
335    ///
336    /// * `idx` - The index into the [`HandleRequest.offsets`] for the line of interest.
337    ///
338    /// [`HandleRequest.offsets`]: struct@HandleRequest
339    #[inline]
340    pub fn get(&self, idx: usize) -> u8 {
341        self.0[idx]
342    }
343
344    /// Set the value of a line.
345    ///
346    /// Note that this is not applied to hardware until these values are passed to
347    /// [`set_line_values`].
348    ///
349    /// * `idx` - The index into the [`HandleRequest.offsets`] for the line of interest.
350    /// * `value` - The logical state of the line to be set.
351    ///
352    /// [`HandleRequest.offsets`]: struct@HandleRequest
353    #[inline]
354    pub fn set(&mut self, idx: usize, value: u8) {
355        self.0[idx] = value;
356    }
357}
358impl Default for LineValues {
359    fn default() -> Self {
360        LineValues([0; 64])
361    }
362}
363
364/// Read the values of requested lines.
365///
366/// * `lf` - The request file returned by [`get_line_handle`] or [`get_line_event`].
367/// * `vals` - The line values to be populated.
368#[inline]
369pub fn get_line_values(lf: &File, vals: &mut LineValues) -> Result<()> {
370    // SAFETY: vals are raw integers that are safe to decode.
371    match unsafe {
372        libc::ioctl(
373            lf.as_raw_fd(),
374            iorw!(Ioctl::GetLineValues, LineValues),
375            vals.0.as_mut_ptr(),
376        )
377    } {
378        0 => Ok(()),
379        _ => Err(Error::from_errno()),
380    }
381}
382
383/// Set the values of requested lines.
384///
385/// * `lf` - The request file returned by [`get_line_handle`].
386/// * `vals` - The line values to be set.
387#[inline]
388pub fn set_line_values(lf: &File, vals: &LineValues) -> Result<()> {
389    // SAFETY: vals is not modified.
390    match unsafe {
391        libc::ioctl(
392            lf.as_raw_fd(),
393            iorw!(Ioctl::SetLineValues, LineValues),
394            vals.0.as_ptr(),
395        )
396    } {
397        0 => Ok(()),
398        _ => Err(Error::from_errno()),
399    }
400}
401
402/// Information about a GPIO event request.
403#[repr(C)]
404#[derive(Clone, Debug, Default, Eq, PartialEq)]
405pub struct EventRequest {
406    /// The line to request edge events from, identified by its offset
407    /// on the associated GPIO device.
408    pub offset: Offset,
409
410    /// The requested handle flags for the GPIO line.
411    pub handleflags: HandleRequestFlags,
412
413    /// The requested event flags for the GPIO line.
414    pub eventflags: EventRequestFlags,
415
416    /// A requested consumer label for the selected GPIO line(s) such as "*my-listener*".
417    pub consumer: Name,
418
419    /// This field is only present for the underlying ioctl call and is only used internally.
420    ///
421    // This is actually specified as an int in gpio.h, but that presents problems
422    // as it is not fixed width.  It is usually i32, so that is what we go with here,
423    // though this may cause issues on platforms with a differently sized int.
424    #[doc(hidden)]
425    pub fd: i32,
426}
427
428bitflags! {
429    /// Additional configuration flags for event requests.
430    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
431    pub struct EventRequestFlags: u32 {
432        /// Report rising edge events on the requested line.
433        const RISING_EDGE = 1;
434
435        /// Report falling edge events on the requested line.
436        const FALLING_EDGE = 2;
437
438        /// Report both rising and falling edge events on the requested line.
439        const BOTH_EDGES = Self::RISING_EDGE.bits() | Self::FALLING_EDGE.bits();
440    }
441}
442
443/// Request a line with edge detection enabled.
444///
445/// Detected events can be read from the returned file.
446///
447/// * 'cf' - The open gpiochip device file.
448/// * `er` - The line event request.
449#[inline]
450pub fn get_line_event(cf: &File, mut er: EventRequest) -> Result<File> {
451    // SAFETY: er is consumed and the returned file is drawn from the returned fd.
452    unsafe {
453        match libc::ioctl(
454            cf.as_raw_fd(),
455            iorw!(Ioctl::GetLineEvent, EventRequest),
456            &mut er,
457        ) {
458            0 => Ok(File::from_raw_fd(er.fd)),
459            _ => Err(Error::from_errno()),
460        }
461    }
462}
463
464/// Information about an edge event on a requested line.
465#[repr(C)]
466#[derive(Clone, Debug, Eq, PartialEq)]
467pub struct LineEdgeEvent {
468    /// The best estimate of time of event occurrence, in nanoseconds.
469    pub timestamp_ns: u64,
470    /// The kind of line event. (LineEdgeEventKind)
471    pub kind: u32,
472}
473
474impl LineEdgeEvent {
475    /// Read a LineEdgeEvent from a buffer.
476    ///
477    /// The buffer is assumed to have been populated by a read of the line request File,
478    /// so the content is initialised.
479    pub fn from_slice(d: &[u64]) -> Result<&LineEdgeEvent> {
480        debug_assert!(std::mem::size_of::<LineEdgeEvent>() % 8 == 0);
481        let len = d.len() * 8;
482        if len < std::mem::size_of::<LineEdgeEvent>() {
483            return Err(Error::from(UnderReadError::new(
484                "LineEdgeEvent",
485                std::mem::size_of::<LineEdgeEvent>(),
486                len,
487            )));
488        }
489        // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
490        let le = unsafe { &*(d as *const [u64] as *const LineEdgeEvent) };
491        Ok(le)
492    }
493
494    /// The number of u64 words required to store a LineEdgeEvent.
495    pub fn u64_size() -> usize {
496        std::mem::size_of::<LineEdgeEvent>() / 8
497    }
498}
499
500#[cfg(test)]
501mod tests {
502    use super::*;
503
504    mod line_info {
505        use super::LineInfo;
506
507        #[test]
508        fn size() {
509            assert_eq!(
510                std::mem::size_of::<LineInfo>(),
511                72usize,
512                concat!("Size of: ", stringify!(LineInfo))
513            );
514        }
515    }
516
517    mod line_info_changed {
518        use super::LineInfoChangeEvent;
519
520        #[test]
521        fn size() {
522            assert_eq!(
523                std::mem::size_of::<LineInfoChangeEvent>(),
524                104usize,
525                concat!("Size of: ", stringify!(LineInfoChangeEvent))
526            );
527        }
528    }
529
530    mod handle_request {
531        use super::HandleRequest;
532
533        #[test]
534        fn size() {
535            assert_eq!(
536                std::mem::size_of::<HandleRequest>(),
537                364usize,
538                concat!("Size of: ", stringify!(HandleRequest))
539            );
540        }
541    }
542
543    mod handle_config {
544        use super::HandleConfig;
545
546        #[test]
547        fn size() {
548            assert_eq!(
549                std::mem::size_of::<HandleConfig>(),
550                84usize,
551                concat!("Size of: ", stringify!(HandleConfig))
552            );
553        }
554    }
555
556    mod event_request {
557        use super::EventRequest;
558
559        #[test]
560        fn size() {
561            assert_eq!(
562                std::mem::size_of::<EventRequest>(),
563                48usize,
564                concat!("Size of: ", stringify!(EventRequest))
565            );
566        }
567    }
568
569    mod line_event {
570        use super::LineEdgeEvent;
571
572        #[test]
573        fn size() {
574            assert_eq!(
575                std::mem::size_of::<LineEdgeEvent>(),
576                16usize,
577                concat!("Size of: ", stringify!(LineEdgeEvent))
578            );
579        }
580    }
581
582    mod line_values {
583        use super::LineValues;
584
585        #[test]
586        fn get() {
587            let mut a = LineValues::default();
588            for idx in [0, 2] {
589                assert_eq!(a.0[idx], 0, "idx: {idx}");
590                assert_eq!(a.get(idx), 0, "idx: {idx}");
591                a.0[idx] = 1;
592                assert_eq!(a.get(idx), 1, "idx: {idx}");
593                a.0[idx] = 42;
594                assert_eq!(a.get(idx), 42, "idx: {idx}");
595            }
596        }
597
598        #[test]
599        fn set() {
600            let mut a = LineValues::default();
601            for idx in [0, 2] {
602                a.set(idx, 0);
603                assert_eq!(a.0[idx], 0, "idx: {idx}");
604                a.set(idx, 1);
605                assert_eq!(a.0[idx], 1, "idx: {idx}");
606                a.set(idx, 42);
607                assert_eq!(a.0[idx], 42, "idx: {idx}");
608            }
609        }
610
611        #[test]
612        fn size() {
613            assert_eq!(
614                std::mem::size_of::<LineValues>(),
615                64usize,
616                concat!("Size of: ", stringify!(LineValues))
617            );
618        }
619    }
620}