gpiocdev_uapi/
common.rs

1// SPDX-FileCopyrightText: 2021 Kent Gibson <warthog618@gmail.com>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5use std::ffi::OsStr;
6use std::fs::File;
7use std::os::unix::prelude::{AsRawFd, OsStrExt};
8use std::ptr;
9use std::slice;
10use std::time::Duration;
11
12/// Check if the file has an event available to read.
13///
14/// For gpiochip files the events are LineInfoChangeEvent.
15/// For line request files the events are LineEdgeEvent.
16#[inline]
17pub fn has_event(f: &File) -> Result<bool> {
18    wait_event(f, Duration::ZERO)
19}
20
21// workaround musl and android libc::ioctl() having a different signature
22#[cfg(any(target_env = "musl", target_os = "android"))]
23pub(crate) type IoctlRequestType = libc::c_int;
24#[cfg(not(any(target_env = "musl", target_os = "android")))]
25pub(crate) type IoctlRequestType = libc::c_ulong;
26
27macro_rules! ior {
28    ($nr:expr, $dty:ty) => {
29        ioctl_sys::ior!(IOCTL_MAGIC, $nr, std::mem::size_of::<$dty>()) as IoctlRequestType
30    };
31}
32
33macro_rules! iorw {
34    ($nr:expr, $dty:ty) => {
35        ioctl_sys::iorw!(IOCTL_MAGIC, $nr, std::mem::size_of::<$dty>()) as IoctlRequestType
36    };
37}
38pub(crate) use iorw;
39
40/// Read an event from a chip or request file descriptor.
41///
42/// Returns the number of u64 words read.
43#[inline]
44pub fn read_event(f: &File, buf: &mut [u64]) -> Result<usize> {
45    unsafe {
46        let bufptr: *mut libc::c_void = std::ptr::addr_of_mut!(*buf) as *mut libc::c_void;
47        match libc::read(f.as_raw_fd(), bufptr, buf.len() * 8) {
48            -1 => Err(Error::from_errno()),
49            x => {
50                let size: usize = x.try_into().unwrap();
51                if size % 8 == 0 {
52                    Ok(size / 8)
53                } else {
54                    Err(Error::from(UnderReadError::new(
55                        "read_event",
56                        buf.len() * 8,
57                        size,
58                    )))
59                }
60            }
61        }
62    }
63}
64
65/// Wait for the file to have an event available to read.
66pub fn wait_event(f: &File, d: Duration) -> Result<bool> {
67    let mut pfd = libc::pollfd {
68        fd: f.as_raw_fd(),
69        events: libc::POLLIN,
70        revents: 0,
71    };
72    // prevent musl builds complaining about use of deprecated time_t
73    #[cfg(not(target_env = "musl"))]
74    use libc::time_t as TimeT;
75    #[cfg(all(target_env = "musl", target_pointer_width = "32"))]
76    use std::primitive::i32 as TimeT;
77    #[cfg(all(target_env = "musl", target_pointer_width = "64"))]
78    use std::primitive::i64 as TimeT;
79    let timeout = libc::timespec {
80        tv_sec: d.as_secs() as TimeT,
81        tv_nsec: d.subsec_nanos() as libc::c_long,
82    };
83    unsafe {
84        match libc::ppoll(
85            std::ptr::addr_of_mut!(pfd),
86            1,
87            std::ptr::addr_of!(timeout),
88            ptr::null(),
89        ) {
90            -1 => Err(Error::from_errno()),
91            0 => Ok(false),
92            _ => Ok(true),
93        }
94    }
95}
96
97pub(crate) const IOCTL_MAGIC: u8 = 0xb4;
98
99#[repr(u8)]
100enum Ioctl {
101    GetChipInfo = 1,
102    UnwatchLineInfo = 0xC,
103}
104
105/// Information about a particular GPIO chip.
106#[repr(C)]
107#[derive(Clone, Debug, Default, Eq, PartialEq)]
108pub struct ChipInfo {
109    /// The Linux kernel name of this GPIO chip.
110    pub name: Name,
111
112    /// A functional name for this GPIO chip, such as a product number.
113    ///
114    /// May be empty.
115    pub label: Name,
116
117    /// The number of GPIO lines on this chip.
118    pub num_lines: u32,
119}
120
121/// Get the publicly available information for a chip.
122///
123/// * `cf` - The open gpiochip device file.
124pub fn get_chip_info(cf: &File) -> Result<ChipInfo> {
125    let mut chip = ChipInfo::default();
126    // SAFETY: returned struct contains raw byte arrays and ints that are safe to decode.
127    unsafe {
128        match libc::ioctl(
129            cf.as_raw_fd(),
130            ior!(Ioctl::GetChipInfo, ChipInfo),
131            &mut chip,
132        ) {
133            0 => Ok(chip),
134            _ => Err(Error::from_errno()),
135        }
136    }
137}
138
139/// Remove any watch on changes to the [`LineInfo`] for a line.
140///
141/// * `cf` - The open gpiochip device file.
142/// * `offset` - The offset of the line to unwatch.
143///
144/// [`LineInfo`]: struct.LineInfo.html
145pub fn unwatch_line_info(cf: &File, offset: Offset) -> Result<()> {
146    match unsafe { libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::UnwatchLineInfo, u32), &offset) } {
147        0 => Ok(()),
148        _ => Err(Error::from_errno()),
149    }
150}
151
152/// An error number returned by a kernel ioctl call.
153#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
154pub struct Errno(pub i32);
155
156impl From<&std::io::Error> for Errno {
157    fn from(e: &std::io::Error) -> Self {
158        Errno(e.raw_os_error().unwrap_or(0))
159    }
160}
161impl std::fmt::Display for Errno {
162    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
163        write!(f, "{}", std::io::Error::from_raw_os_error(self.0))
164    }
165}
166
167/// The result returned by [`gpiocdev_uapi`] functions.
168///
169/// [`gpiocdev_uapi`]: crate
170pub type Result<T> = std::result::Result<T, Error>;
171
172/// Errors returned by [`gpiocdev_uapi`] functions.
173///
174/// [`gpiocdev_uapi`]: crate
175#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
176pub enum Error {
177    /// An error returned from an underlying system call.
178    #[error(transparent)]
179    Os(Errno),
180
181    /// An error indicating insufficient data read for the expected object.
182    #[error(transparent)]
183    UnderRead(#[from] UnderReadError),
184}
185
186impl Error {
187    /// Create an error from the current errno value.
188    #[inline]
189    #[cfg(target_os = "linux")]
190    pub fn from_errno() -> Error {
191        Error::Os(Errno(unsafe { *libc::__errno_location() }))
192    }
193    #[inline]
194    #[cfg(target_os = "android")]
195    pub fn from_errno() -> Error {
196        Error::Os(Errno(
197            std::io::Error::last_os_error().raw_os_error().unwrap(),
198        ))
199    }
200}
201
202/// A failure to read sufficient bytes to construct an object.
203//
204// This should never happen - but is checked to be safe.
205#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
206#[error("Reading {obj} returned {found} bytes, expected {expected}.")]
207pub struct UnderReadError {
208    /// The struct that under read.
209    pub obj: &'static str,
210    /// The number of bytes expected.
211    pub expected: usize,
212    /// The number of bytes read.
213    pub found: usize,
214}
215
216impl UnderReadError {
217    /// Create an UnderReadError.
218    pub(crate) fn new(obj: &'static str, expected: usize, found: usize) -> UnderReadError {
219        UnderReadError {
220            obj,
221            expected,
222            found,
223        }
224    }
225}
226
227/// The maximum number of bytes stored in a Name.
228pub const NAME_LEN_MAX: usize = 32;
229
230/// A uAPI name string, common to ABI v1 and v2.
231#[repr(C)]
232#[derive(Clone, Debug, Default, Eq, PartialEq)]
233pub struct Name(pub [u8; NAME_LEN_MAX]);
234
235impl Name {
236    /// Checks whether the Name is empty.
237    #[inline]
238    pub fn is_empty(&self) -> bool {
239        self.0[0] == 0
240    }
241
242    /// The length of the contained name.
243    #[inline]
244    pub fn strlen(&self) -> usize {
245        self.0.iter().position(|&x| x == 0).unwrap_or(self.0.len())
246    }
247
248    /// Convert the contained name to a OsString slice.
249    #[inline]
250    pub fn as_os_str(&self) -> &OsStr {
251        unsafe { OsStr::from_bytes(slice::from_raw_parts(&self.0[0], self.strlen())) }
252    }
253
254    /// Construct a Name from byte slice.
255    ///
256    /// Slice will be truncated if longer than the Name size.
257    /// Truncation occurs on UTF-8 codepoint boundaries so the resulting
258    /// name is still valid UTF-8.
259    pub fn from_bytes(s: &[u8]) -> Name {
260        let mut d: Name = Default::default();
261        // drop any truncated UTF-8 codepoint
262        let len = if s.len() < NAME_LEN_MAX {
263            s.len()
264        } else if s[NAME_LEN_MAX - 3] >= 0xf0 {
265            NAME_LEN_MAX - 3
266        } else if s[NAME_LEN_MAX - 2] >= 0xe0 {
267            NAME_LEN_MAX - 2
268        } else if s[NAME_LEN_MAX - 1] >= 0xc0 {
269            NAME_LEN_MAX - 1
270        } else {
271            NAME_LEN_MAX
272        };
273        for (src, dst) in s.iter().take(len).zip(d.0.iter_mut()) {
274            *dst = *src;
275        }
276        d
277    }
278}
279impl From<&Name> for String {
280    fn from(s: &Name) -> Self {
281        String::from(s.as_os_str().to_string_lossy())
282    }
283}
284impl From<&str> for Name {
285    fn from(s: &str) -> Self {
286        Name::from_bytes(s.as_bytes())
287    }
288}
289
290/// An identifier for a line on a particular chip.
291///
292/// Valid offsets are in the range 0..`num_lines` as reported in the [`ChipInfo`].
293pub type Offset = u32;
294
295/// The maximum number of lines that may be requested in a single request.
296pub const NUM_LINES_MAX: usize = 64;
297
298/// A collection of line offsets.
299///
300/// Typically used to identify the lines belonging to a particular request.
301#[repr(C)]
302#[derive(Clone, Debug, Eq, PartialEq)]
303pub struct Offsets(pub [Offset; NUM_LINES_MAX]);
304
305impl Offsets {
306    /// Create offsets from an iterable list.
307    pub fn from_slice(s: &[u32]) -> Self {
308        let mut n: Offsets = Default::default();
309        for (src, dst) in s.iter().zip(n.0.iter_mut()) {
310            *dst = *src;
311        }
312        n
313    }
314
315    /// Get the indexed offset from the set.
316    #[inline]
317    pub fn get(&self, idx: usize) -> Offset {
318        self.0[idx]
319    }
320
321    /// Set the indexed offset in the set.
322    #[inline]
323    pub fn set(&mut self, idx: usize, offset: Offset) {
324        self.0[idx] = offset;
325    }
326
327    /// Copy offsets from an iterable list.
328    pub fn copy_from_slice(&mut self, s: &[u32]) {
329        let extent = std::cmp::min(NUM_LINES_MAX, s.len());
330        self.0[0..extent].copy_from_slice(s);
331    }
332}
333
334impl Default for Offsets {
335    fn default() -> Self {
336        Offsets([0; NUM_LINES_MAX])
337    }
338}
339
340/// Space reserved for future use.
341///
342/// Sized in multiples of u32 words.
343#[repr(C)]
344#[derive(Clone, Copy, Debug, Eq, PartialEq)]
345#[doc(hidden)]
346pub struct Padding<const SIZE: usize>([u32; SIZE]);
347
348impl<const SIZE: usize> Default for Padding<SIZE> {
349    fn default() -> Self {
350        Padding([0; SIZE])
351    }
352}
353
354impl<const SIZE: usize> Padding<SIZE> {
355    pub fn is_zeroed(&self) -> bool {
356        for x in self.0.iter() {
357            if *x != 0 {
358                return false;
359            }
360        }
361        true
362    }
363}
364
365/// The trigger identifier for a [`LineInfoChangeEvent`].
366///
367/// [`LineInfoChangeEvent`]: struct.LineInfoChangeEvent.html
368#[repr(u32)]
369#[derive(Clone, Copy, Debug, Eq, PartialEq)]
370pub enum LineInfoChangeKind {
371    /// The line has been requested.
372    Requested = 1,
373
374    /// The line has been released.
375    Released = 2,
376
377    /// The line has been reconfigured.
378    Reconfigured = 3,
379}
380
381impl TryFrom<u32> for LineInfoChangeKind {
382    type Error = ();
383
384    fn try_from(v: u32) -> std::result::Result<Self, Self::Error> {
385        Ok(match v {
386            1 => LineInfoChangeKind::Requested,
387            2 => LineInfoChangeKind::Released,
388            3 => LineInfoChangeKind::Reconfigured,
389            _ => return Err(()),
390        })
391    }
392}
393
394/// The trigger identifier for a [`LineEdgeEvent`].
395///
396/// [`LineEdgeEvent`]: struct.LineEdgeEvent.html
397#[repr(u32)]
398#[derive(Clone, Copy, Debug, Eq, PartialEq)]
399pub enum LineEdgeEventKind {
400    /// Indicates the line transitioned from *inactive* to *active*.
401    RisingEdge = 1,
402
403    /// Indicates the line transitioned from *active* to *inactive*.
404    FallingEdge = 2,
405}
406
407impl TryFrom<u32> for LineEdgeEventKind {
408    type Error = ();
409
410    fn try_from(v: u32) -> std::result::Result<Self, Self::Error> {
411        Ok(match v {
412            1 => LineEdgeEventKind::RisingEdge,
413            2 => LineEdgeEventKind::FallingEdge,
414            _ => return Err(()),
415        })
416    }
417}
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422
423    #[test]
424    fn size_of_chip_info() {
425        assert_eq!(
426            std::mem::size_of::<ChipInfo>(),
427            68usize,
428            concat!("Size of: ", stringify!(ChipInfo))
429        );
430    }
431
432    #[test]
433    fn line_info_changed_try_from() {
434        assert_eq!(
435            LineInfoChangeKind::try_from(LineInfoChangeKind::Requested as u32),
436            Ok(LineInfoChangeKind::Requested)
437        );
438        assert_eq!(
439            LineInfoChangeKind::try_from(LineInfoChangeKind::Released as u32),
440            Ok(LineInfoChangeKind::Released)
441        );
442        assert_eq!(
443            LineInfoChangeKind::try_from(LineInfoChangeKind::Reconfigured as u32),
444            Ok(LineInfoChangeKind::Reconfigured)
445        );
446        assert!(LineInfoChangeKind::try_from(0).is_err());
447        assert!(LineInfoChangeKind::try_from(4).is_err());
448        assert_eq!(
449            LineInfoChangeKind::try_from(3),
450            Ok(LineInfoChangeKind::Reconfigured)
451        );
452    }
453
454    #[test]
455    fn line_edge_event_kind_try_from() {
456        assert_eq!(
457            LineEdgeEventKind::try_from(LineEdgeEventKind::RisingEdge as u32),
458            Ok(LineEdgeEventKind::RisingEdge)
459        );
460        assert_eq!(
461            LineEdgeEventKind::try_from(LineEdgeEventKind::FallingEdge as u32),
462            Ok(LineEdgeEventKind::FallingEdge)
463        );
464        assert!(LineEdgeEventKind::try_from(0).is_err());
465        assert!(LineEdgeEventKind::try_from(3).is_err());
466        assert_eq!(
467            LineEdgeEventKind::try_from(2),
468            Ok(LineEdgeEventKind::FallingEdge)
469        );
470    }
471
472    #[test]
473    fn name_from_str() {
474        let mut x = [0u8; 32];
475        x[0] = 98;
476        x[1] = 97;
477        x[2] = 110;
478        x[3] = 97;
479        x[4] = 110;
480        x[5] = 97;
481        let mut a: Name = "banana".into();
482        assert_eq!(a.0, x);
483
484        a = "apple".into();
485        x[0] = 97;
486        x[1] = 112;
487        x[2] = 112;
488        x[3] = 108;
489        x[4] = 101;
490        x[5] = 0;
491        assert_eq!(a.0, x);
492
493        a = "an overly long truncated name -><- cut here".into();
494        assert_eq!(a.as_os_str(), "an overly long truncated name ->");
495    }
496
497    #[test]
498    fn name_is_empty() {
499        let mut a = Name::default();
500        assert!(a.is_empty());
501        a = "banana".into();
502        assert!(!a.is_empty());
503    }
504
505    #[test]
506    fn name_strlen() {
507        let mut a = Name::default();
508        assert_eq!(a.strlen(), 0);
509        a = "banana".into();
510        assert_eq!(a.strlen(), 6);
511        a = "an overly long truncated name -><- cut here".into();
512        assert_eq!(a.strlen(), 32);
513    }
514
515    #[test]
516    fn name_as_os_str() {
517        let mut a = Name::default();
518        assert_eq!(a.as_os_str(), "");
519        a = "banana".into();
520        assert_eq!(a.as_os_str(), "banana");
521    }
522
523    #[test]
524    fn name_from_bytes() {
525        // empty
526        let mut a = Name::from_bytes("some bytes".as_bytes());
527        assert_eq!(a.as_os_str(), "some bytes");
528
529        // word
530        a = Name::from_bytes("banana".as_bytes());
531        assert_eq!(a.as_os_str(), "banana");
532
533        // multiple words
534        let mut a = Name::from_bytes("some bytes".as_bytes());
535        assert_eq!(a.as_os_str(), "some bytes");
536
537        // truncated overlength
538        a = Name::from_bytes("an overly long truncated name -><- cut here".as_bytes());
539        assert_eq!(a.as_os_str(), "an overly long truncated name ->");
540
541        // truncated at UTF-8 code point - dangling 1 of 2
542        a = Name::from_bytes("an overly long truncated name->ó<- cut here".as_bytes());
543        assert_eq!(a.as_os_str(), "an overly long truncated name->");
544
545        // truncated at UTF-8 code point - trailing 2 byte
546        a = Name::from_bytes("an overly long truncated name ó<- cut here".as_bytes());
547        assert_eq!(a.as_os_str(), "an overly long truncated name ó");
548
549        // truncated at UTF-8 code point - dangling 1 of 3
550        a = Name::from_bytes("an overly long truncated name->€<- cut here".as_bytes());
551        assert_eq!(a.as_os_str(), "an overly long truncated name->");
552
553        // truncated at UTF-8 code point - dangling 2 of 3
554        a = Name::from_bytes("an overly long truncated name>€<- cut here".as_bytes());
555        assert_eq!(a.as_os_str(), "an overly long truncated name>");
556
557        // truncated at UTF-8 code point - trailing 3 byte
558        a = Name::from_bytes("overly long truncated name - €<- cut here".as_bytes());
559        assert_eq!(a.as_os_str(), "overly long truncated name - €");
560
561        // truncated at UTF-8 code point - dangling 1 of 4
562        a = Name::from_bytes("an overly long truncated name->𝄞<- cut here".as_bytes());
563        assert_eq!(a.as_os_str(), "an overly long truncated name->");
564
565        // truncated at UTF-8 code point - dangling 2 of 4
566        a = Name::from_bytes("an overly long truncated name>𝄞<- cut here".as_bytes());
567        assert_eq!(a.as_os_str(), "an overly long truncated name>");
568
569        // truncated at UTF-8 code point - dangling 3 of 4
570        a = Name::from_bytes("overly long truncated name ->𝄞<- cut here".as_bytes());
571        assert_eq!(a.as_os_str(), "overly long truncated name ->");
572
573        // truncated at UTF-8 code point - trailing 4 byte
574        a = Name::from_bytes("overly long truncated name -𝄞<- cut here".as_bytes());
575        assert_eq!(a.as_os_str(), "overly long truncated name -𝄞");
576    }
577
578    #[test]
579    fn name_default() {
580        assert_eq!(Name::default().0, [0u8; NAME_LEN_MAX]);
581    }
582
583    #[test]
584    fn offsets_from_slice() {
585        let mut x = [0u32; NUM_LINES_MAX];
586        x[0] = 1;
587        x[1] = 2;
588        x[2] = 3;
589        x[3] = 0;
590        x[4] = 5;
591        x[5] = 6;
592        let mut a = Offsets::from_slice(&[1, 2, 3, 0, 5, 6]);
593        assert_eq!(a.0, x);
594        a = Offsets::from_slice(&[0, 1, 2, 3]);
595        x[0] = 0;
596        x[1] = 1;
597        x[2] = 2;
598        x[3] = 3;
599        x[4] = 0;
600        x[5] = 0;
601        assert_eq!(a.0, x);
602        a = Offsets::from_slice(&[0, 3, 2, 3, 4]);
603        x[0] = 0;
604        x[1] = 3;
605        x[2] = 2;
606        x[3] = 3;
607        x[4] = 4;
608        x[5] = 0;
609        assert_eq!(a.0, x);
610    }
611
612    #[test]
613    fn offsets_default() {
614        assert_eq!(Offsets::default().0, [0u32; NUM_LINES_MAX]);
615    }
616
617    #[test]
618    fn padding_is_zeroed() {
619        let mut padding: Padding<3> = Padding::default();
620        assert!(padding.is_zeroed());
621        padding.0[1] = 3;
622        assert!(!padding.is_zeroed());
623    }
624
625    #[test]
626    fn size_of_name() {
627        assert_eq!(
628            std::mem::size_of::<Name>(),
629            NAME_LEN_MAX,
630            concat!("Size of: ", stringify!(Name))
631        );
632    }
633
634    #[test]
635    fn size_of_offsets() {
636        assert_eq!(
637            std::mem::size_of::<Offsets>(),
638            256usize,
639            concat!("Size of: ", stringify!(Offsets))
640        );
641    }
642
643    #[test]
644    fn size_of_padding() {
645        assert_eq!(
646            std::mem::size_of::<Padding<1>>(),
647            4usize,
648            concat!("Size of: ", stringify!(Padding<1>))
649        );
650        assert_eq!(
651            std::mem::size_of::<Padding<2>>(),
652            8usize,
653            concat!("Size of: ", stringify!(Padding<2>))
654        );
655        assert_eq!(
656            std::mem::size_of::<Padding<5>>(),
657            20usize,
658            concat!("Size of: ", stringify!(Padding<5>))
659        );
660    }
661}