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, 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 = std::mem::MaybeUninit::<ChipInfo>::uninit();
126    unsafe {
127        match libc::ioctl(
128            cf.as_raw_fd(),
129            ior!(Ioctl::GetChipInfo, ChipInfo),
130            chip.as_mut_ptr(),
131        ) {
132            0 => Ok(chip.assume_init()),
133            _ => Err(Error::from_errno()),
134        }
135    }
136}
137
138/// Remove any watch on changes to the [`LineInfo`] for a line.
139///
140/// * `cf` - The open gpiochip device file.
141/// * `offset` - The offset of the line to unwatch.
142///
143/// [`LineInfo`]: struct.LineInfo.html
144pub fn unwatch_line_info(cf: &File, offset: Offset) -> Result<()> {
145    match unsafe { libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::UnwatchLineInfo, u32), &offset) } {
146        0 => Ok(()),
147        _ => Err(Error::from_errno()),
148    }
149}
150
151/// An error number returned by a kernel ioctl call.
152#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
153pub struct Errno(pub i32);
154
155impl From<&std::io::Error> for Errno {
156    fn from(e: &std::io::Error) -> Self {
157        Errno(e.raw_os_error().unwrap_or(0))
158    }
159}
160impl std::fmt::Display for Errno {
161    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
162        write!(f, "{}", std::io::Error::from_raw_os_error(self.0))
163    }
164}
165
166/// The result returned by [`gpiocdev_uapi`] functions.
167///
168/// [`gpiocdev_uapi`]: crate
169pub type Result<T> = std::result::Result<T, Error>;
170
171/// Result returned by struct validators.
172pub type ValidationResult = std::result::Result<(), ValidationError>;
173
174/// Errors returned by [`gpiocdev_uapi`] functions.
175///
176/// [`gpiocdev_uapi`]: crate
177#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
178pub enum Error {
179    /// An error returned from an underlying system call.
180    #[error(transparent)]
181    Os(Errno),
182
183    /// An error indicating insufficient data read for the expected object.
184    #[error(transparent)]
185    UnderRead(#[from] UnderReadError),
186
187    /// An error validating an data structure retuned from the kernel
188    #[error(transparent)]
189    Validation(#[from] ValidationError),
190}
191
192impl Error {
193    /// Create an error from the current errno value.
194    #[inline]
195    #[cfg(target_os = "linux")]
196    pub fn from_errno() -> Error {
197        Error::Os(Errno(unsafe { *libc::__errno_location() }))
198    }
199    #[inline]
200    #[cfg(target_os = "android")]
201    pub fn from_errno() -> Error {
202        Error::Os(Errno(std::io::Error::last_os_error().raw_os_error().unwrap()))
203    }
204}
205
206/// A failure to read sufficient bytes to construct an object.
207//
208// This should never happen - but is checked to be safe.
209#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
210#[error("Reading {obj} returned {found} bytes, expected {expected}.")]
211pub struct UnderReadError {
212    /// The struct that under read.
213    pub obj: &'static str,
214    /// The number of bytes expected.
215    pub expected: usize,
216    /// The number of bytes read.
217    pub found: usize,
218}
219
220impl UnderReadError {
221    /// Create an UnderReadError.
222    pub(crate) fn new(obj: &'static str, expected: usize, found: usize) -> UnderReadError {
223        UnderReadError {
224            obj,
225            expected,
226            found,
227        }
228    }
229}
230
231/// A failure to validate a struct returned from a system call.
232//
233// Should only be seen if a kernel update adds an enum value we are unaware of.
234#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
235#[error("Kernel returned invalid {field}: {msg}")]
236pub struct ValidationError {
237    /// The field that failed to validate.
238    pub field: String,
239    /// The details of the validation failure.
240    pub msg: String,
241}
242
243impl ValidationError {
244    /// Create a ValidationError.
245    pub(crate) fn new<S: Into<String>, T: Into<String>>(field: S, msg: T) -> ValidationError {
246        ValidationError {
247            field: field.into(),
248            msg: msg.into(),
249        }
250    }
251}
252
253/// The maximum number of bytes stored in a Name.
254pub const NAME_LEN_MAX: usize = 32;
255
256/// A uAPI name string, common to ABI v1 and v2.
257#[repr(C)]
258#[derive(Clone, Debug, Default, Eq, PartialEq)]
259pub struct Name([u8; NAME_LEN_MAX]);
260
261impl Name {
262    /// Checks whether the Name is empty.
263    #[inline]
264    pub fn is_empty(&self) -> bool {
265        self.0[0] == 0
266    }
267
268    /// The length of the contained name.
269    #[inline]
270    pub fn strlen(&self) -> usize {
271        self.0.iter().position(|&x| x == 0).unwrap_or(self.0.len())
272    }
273
274    /// Convert the contained name to a OsString slice.
275    #[inline]
276    pub fn as_os_str(&self) -> &OsStr {
277        unsafe { OsStr::from_bytes(slice::from_raw_parts(&self.0[0], self.strlen())) }
278    }
279
280    /// Construct a Name from byte slice.
281    ///
282    /// Slice will be truncated if longer than the Name size.
283    /// Truncation occurs on UTF-8 codepoint boundaries so the resulting
284    /// name is still valid UTF-8.
285    pub fn from_bytes(s: &[u8]) -> Name {
286        let mut d: Name = Default::default();
287        // drop any truncated UTF-8 codepoint
288        let len = if s.len() < NAME_LEN_MAX {
289            s.len()
290        } else if s[NAME_LEN_MAX - 3] >= 0xf0 {
291            NAME_LEN_MAX - 3
292        } else if s[NAME_LEN_MAX - 2] >= 0xe0 {
293            NAME_LEN_MAX - 2
294        } else if s[NAME_LEN_MAX - 1] >= 0xc0 {
295            NAME_LEN_MAX - 1
296        } else {
297            NAME_LEN_MAX
298        };
299        for (src, dst) in s.iter().take(len).zip(d.0.iter_mut()) {
300            *dst = *src;
301        }
302        d
303    }
304}
305impl From<&Name> for String {
306    fn from(s: &Name) -> Self {
307        String::from(s.as_os_str().to_string_lossy())
308    }
309}
310impl From<&str> for Name {
311    fn from(s: &str) -> Self {
312        Name::from_bytes(s.as_bytes())
313    }
314}
315
316/// An identifier for a line on a particular chip.
317///
318/// Valid offsets are in the range 0..`num_lines` as reported in the [`ChipInfo`].
319pub type Offset = u32;
320
321/// The maximum number of lines that may be requested in a single request.
322pub const NUM_LINES_MAX: usize = 64;
323
324/// A collection of line offsets.
325///
326/// Typically used to identify the lines belonging to a particular request.
327#[repr(C)]
328#[derive(Clone, Debug, Eq, PartialEq)]
329pub struct Offsets([Offset; NUM_LINES_MAX]);
330
331impl Offsets {
332    /// Create offsets from an iterable list.
333    pub fn from_slice(s: &[u32]) -> Self {
334        let mut n: Offsets = Default::default();
335        for (src, dst) in s.iter().zip(n.0.iter_mut()) {
336            *dst = *src;
337        }
338        n
339    }
340
341    /// Get the indexed offset from the set.
342    #[inline]
343    pub fn get(&self, idx: usize) -> Offset {
344        self.0[idx]
345    }
346
347    /// Set the indexed offset in the set.
348    #[inline]
349    pub fn set(&mut self, idx: usize, offset: Offset) {
350        self.0[idx] = offset;
351    }
352
353    /// Copy offsets from an iterable list.
354    pub fn copy_from_slice(&mut self, s: &[u32]) {
355        let extent = std::cmp::min(NUM_LINES_MAX, s.len());
356        self.0[0..extent].copy_from_slice(s);
357    }
358}
359
360impl Default for Offsets {
361    fn default() -> Self {
362        Offsets([0; NUM_LINES_MAX])
363    }
364}
365
366/// Space reserved for future use.
367///
368/// Sized in multiples of u32 words.
369#[repr(C)]
370#[derive(Clone, Copy, Debug, Eq, PartialEq)]
371#[doc(hidden)]
372pub struct Padding<const SIZE: usize>([u32; SIZE]);
373
374impl<const SIZE: usize> Default for Padding<SIZE> {
375    fn default() -> Self {
376        Padding([0; SIZE])
377    }
378}
379
380impl<const SIZE: usize> Padding<SIZE> {
381    pub fn is_zeroed(&self) -> bool {
382        for x in self.0.iter() {
383            if *x != 0 {
384                return false;
385            }
386        }
387        true
388    }
389}
390
391/// The trigger identifier for a [`LineInfoChangeEvent`].
392///
393/// [`LineInfoChangeEvent`]: struct.LineInfoChangeEvent.html
394#[repr(u32)]
395#[derive(Clone, Copy, Debug, Eq, PartialEq)]
396pub enum LineInfoChangeKind {
397    /// The line has been requested.
398    Requested = 1,
399
400    /// The line has been released.
401    Released = 2,
402
403    /// The line has been reconfigured.
404    Reconfigured = 3,
405}
406
407impl TryFrom<u32> for LineInfoChangeKind {
408    type Error = String;
409
410    fn try_from(v: u32) -> std::result::Result<Self, Self::Error> {
411        Ok(match v {
412            x if x == LineInfoChangeKind::Requested as u32 => LineInfoChangeKind::Requested,
413            x if x == LineInfoChangeKind::Released as u32 => LineInfoChangeKind::Released,
414            x if x == LineInfoChangeKind::Reconfigured as u32 => LineInfoChangeKind::Reconfigured,
415            x => return Err(format!("invalid value: {}", x)),
416        })
417    }
418}
419
420impl LineInfoChangeKind {
421    /// Confirm that the value read from the kernel is valid in Rust.
422    pub(crate) fn validate(&self) -> std::result::Result<(), String> {
423        LineInfoChangeKind::try_from(*self as u32).map(|_i| ())
424    }
425}
426
427/// The trigger identifier for a [`LineEdgeEvent`].
428///
429/// [`LineEdgeEvent`]: struct.LineEdgeEvent.html
430#[repr(u32)]
431#[derive(Clone, Copy, Debug, Eq, PartialEq)]
432pub enum LineEdgeEventKind {
433    /// Indicates the line transitioned from *inactive* to *active*.
434    RisingEdge = 1,
435
436    /// Indicates the line transitioned from *active* to *inactive*.
437    FallingEdge = 2,
438}
439
440impl TryFrom<u32> for LineEdgeEventKind {
441    type Error = String;
442
443    fn try_from(v: u32) -> std::result::Result<Self, Self::Error> {
444        Ok(match v {
445            x if x == LineEdgeEventKind::RisingEdge as u32 => LineEdgeEventKind::RisingEdge,
446            x if x == LineEdgeEventKind::FallingEdge as u32 => LineEdgeEventKind::FallingEdge,
447            _ => return Err(format!("invalid value: {}", v)),
448        })
449    }
450}
451
452impl LineEdgeEventKind {
453    /// Confirm that the value read from the kernel is valid in Rust.
454    pub(crate) fn validate(&self) -> std::result::Result<(), String> {
455        LineEdgeEventKind::try_from(*self as u32).map(|_i| ())
456    }
457}
458
459#[cfg(test)]
460mod tests {
461    use super::*;
462
463    #[test]
464    fn size_of_chip_info() {
465        assert_eq!(
466            std::mem::size_of::<ChipInfo>(),
467            68usize,
468            concat!("Size of: ", stringify!(ChipInfo))
469        );
470    }
471
472    #[test]
473    fn line_info_changed_kind_validate() {
474        let mut a = LineInfoChangeKind::Requested;
475        assert!(a.validate().is_ok());
476        unsafe {
477            a = *(&0 as *const i32 as *const LineInfoChangeKind);
478            assert_eq!(a.validate().unwrap_err(), "invalid value: 0");
479            a = *(&4 as *const i32 as *const LineInfoChangeKind);
480            assert_eq!(a.validate().unwrap_err(), "invalid value: 4");
481            a = *(&3 as *const i32 as *const LineInfoChangeKind);
482            assert!(a.validate().is_ok());
483        }
484    }
485
486    #[test]
487    fn line_event_kind_validate() {
488        let mut a = LineInfoChangeKind::Requested;
489        assert!(a.validate().is_ok());
490        unsafe {
491            a = *(&0 as *const i32 as *const LineInfoChangeKind);
492            assert_eq!(a.validate().unwrap_err(), "invalid value: 0");
493            a = *(&4 as *const i32 as *const LineInfoChangeKind);
494            assert_eq!(a.validate().unwrap_err(), "invalid value: 4");
495            a = *(&3 as *const i32 as *const LineInfoChangeKind);
496            assert!(a.validate().is_ok());
497        }
498    }
499
500    #[test]
501    fn name_from_str() {
502        let mut x = [0u8; 32];
503        x[0] = 98;
504        x[1] = 97;
505        x[2] = 110;
506        x[3] = 97;
507        x[4] = 110;
508        x[5] = 97;
509        let mut a: Name = "banana".into();
510        assert_eq!(a.0, x);
511
512        a = "apple".into();
513        x[0] = 97;
514        x[1] = 112;
515        x[2] = 112;
516        x[3] = 108;
517        x[4] = 101;
518        x[5] = 0;
519        assert_eq!(a.0, x);
520
521        a = "an overly long truncated name -><- cut here".into();
522        assert_eq!(a.as_os_str(), "an overly long truncated name ->");
523    }
524
525    #[test]
526    fn name_is_empty() {
527        let mut a = Name::default();
528        assert!(a.is_empty());
529        a = "banana".into();
530        assert!(!a.is_empty());
531    }
532
533    #[test]
534    fn name_strlen() {
535        let mut a = Name::default();
536        assert_eq!(a.strlen(), 0);
537        a = "banana".into();
538        assert_eq!(a.strlen(), 6);
539        a = "an overly long truncated name -><- cut here".into();
540        assert_eq!(a.strlen(), 32);
541    }
542
543    #[test]
544    fn name_as_os_str() {
545        let mut a = Name::default();
546        assert_eq!(a.as_os_str(), "");
547        a = "banana".into();
548        assert_eq!(a.as_os_str(), "banana");
549    }
550
551    #[test]
552    fn name_from_bytes() {
553        // empty
554        let mut a = Name::from_bytes("some bytes".as_bytes());
555        assert_eq!(a.as_os_str(), "some bytes");
556
557        // word
558        a = Name::from_bytes("banana".as_bytes());
559        assert_eq!(a.as_os_str(), "banana");
560
561        // multiple words
562        let mut a = Name::from_bytes("some bytes".as_bytes());
563        assert_eq!(a.as_os_str(), "some bytes");
564
565        // truncated overlength
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 1 of 2
570        a = Name::from_bytes("an overly long truncated name->ó<- cut here".as_bytes());
571        assert_eq!(a.as_os_str(), "an overly long truncated name->");
572
573        // truncated at UTF-8 code point - trailing 2 byte
574        a = Name::from_bytes("an overly long truncated name ó<- cut here".as_bytes());
575        assert_eq!(a.as_os_str(), "an overly long truncated name ó");
576
577        // truncated at UTF-8 code point - dangling 1 of 3
578        a = Name::from_bytes("an overly long truncated name->€<- cut here".as_bytes());
579        assert_eq!(a.as_os_str(), "an overly long truncated name->");
580
581        // truncated at UTF-8 code point - dangling 2 of 3
582        a = Name::from_bytes("an overly long truncated name>€<- cut here".as_bytes());
583        assert_eq!(a.as_os_str(), "an overly long truncated name>");
584
585        // truncated at UTF-8 code point - trailing 3 byte
586        a = Name::from_bytes("overly long truncated name - €<- cut here".as_bytes());
587        assert_eq!(a.as_os_str(), "overly long truncated name - €");
588
589        // truncated at UTF-8 code point - dangling 1 of 4
590        a = Name::from_bytes("an overly long truncated name->𝄞<- cut here".as_bytes());
591        assert_eq!(a.as_os_str(), "an overly long truncated name->");
592
593        // truncated at UTF-8 code point - dangling 2 of 4
594        a = Name::from_bytes("an overly long truncated name>𝄞<- cut here".as_bytes());
595        assert_eq!(a.as_os_str(), "an overly long truncated name>");
596
597        // truncated at UTF-8 code point - dangling 3 of 4
598        a = Name::from_bytes("overly long truncated name ->𝄞<- cut here".as_bytes());
599        assert_eq!(a.as_os_str(), "overly long truncated name ->");
600
601        // truncated at UTF-8 code point - trailing 4 byte
602        a = Name::from_bytes("overly long truncated name -𝄞<- cut here".as_bytes());
603        assert_eq!(a.as_os_str(), "overly long truncated name -𝄞");
604    }
605
606    #[test]
607    fn name_default() {
608        assert_eq!(Name::default().0, [0u8; NAME_LEN_MAX]);
609    }
610
611    #[test]
612    fn offsets_from_slice() {
613        let mut x = [0u32; NUM_LINES_MAX];
614        x[0] = 1;
615        x[1] = 2;
616        x[2] = 3;
617        x[3] = 0;
618        x[4] = 5;
619        x[5] = 6;
620        let mut a = Offsets::from_slice(&[1, 2, 3, 0, 5, 6]);
621        assert_eq!(a.0, x);
622        a = Offsets::from_slice(&[0, 1, 2, 3]);
623        x[0] = 0;
624        x[1] = 1;
625        x[2] = 2;
626        x[3] = 3;
627        x[4] = 0;
628        x[5] = 0;
629        assert_eq!(a.0, x);
630        a = Offsets::from_slice(&[0, 3, 2, 3, 4]);
631        x[0] = 0;
632        x[1] = 3;
633        x[2] = 2;
634        x[3] = 3;
635        x[4] = 4;
636        x[5] = 0;
637        assert_eq!(a.0, x);
638    }
639
640    #[test]
641    fn offsets_default() {
642        assert_eq!(Offsets::default().0, [0u32; NUM_LINES_MAX]);
643    }
644
645    #[test]
646    fn padding_is_zeroed() {
647        let mut padding: Padding<3> = Padding::default();
648        assert!(padding.is_zeroed());
649        padding.0[1] = 3;
650        assert!(!padding.is_zeroed());
651    }
652
653    #[test]
654    fn size_of_name() {
655        assert_eq!(
656            std::mem::size_of::<Name>(),
657            NAME_LEN_MAX,
658            concat!("Size of: ", stringify!(Name))
659        );
660    }
661
662    #[test]
663    fn size_of_offsets() {
664        assert_eq!(
665            std::mem::size_of::<Offsets>(),
666            256usize,
667            concat!("Size of: ", stringify!(Offsets))
668        );
669    }
670
671    #[test]
672    fn size_of_padding() {
673        assert_eq!(
674            std::mem::size_of::<Padding<1>>(),
675            4usize,
676            concat!("Size of: ", stringify!(Padding<1>))
677        );
678        assert_eq!(
679            std::mem::size_of::<Padding<2>>(),
680            8usize,
681            concat!("Size of: ", stringify!(Padding<2>))
682        );
683        assert_eq!(
684            std::mem::size_of::<Padding<5>>(),
685            20usize,
686            concat!("Size of: ", stringify!(Padding<5>))
687        );
688    }
689}