1use 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#[inline]
17pub fn has_event(f: &File) -> Result<bool> {
18 wait_event(f, Duration::ZERO)
19}
20
21#[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#[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
65pub 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 #[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#[repr(C)]
107#[derive(Clone, Debug, Eq, PartialEq)]
108pub struct ChipInfo {
109 pub name: Name,
111
112 pub label: Name,
116
117 pub num_lines: u32,
119}
120
121pub 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
138pub 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#[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
166pub type Result<T> = std::result::Result<T, Error>;
170
171pub type ValidationResult = std::result::Result<(), ValidationError>;
173
174#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
178pub enum Error {
179 #[error(transparent)]
181 Os(Errno),
182
183 #[error(transparent)]
185 UnderRead(#[from] UnderReadError),
186
187 #[error(transparent)]
189 Validation(#[from] ValidationError),
190}
191
192impl Error {
193 #[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#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
210#[error("Reading {obj} returned {found} bytes, expected {expected}.")]
211pub struct UnderReadError {
212 pub obj: &'static str,
214 pub expected: usize,
216 pub found: usize,
218}
219
220impl UnderReadError {
221 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#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
235#[error("Kernel returned invalid {field}: {msg}")]
236pub struct ValidationError {
237 pub field: String,
239 pub msg: String,
241}
242
243impl ValidationError {
244 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
253pub const NAME_LEN_MAX: usize = 32;
255
256#[repr(C)]
258#[derive(Clone, Debug, Default, Eq, PartialEq)]
259pub struct Name([u8; NAME_LEN_MAX]);
260
261impl Name {
262 #[inline]
264 pub fn is_empty(&self) -> bool {
265 self.0[0] == 0
266 }
267
268 #[inline]
270 pub fn strlen(&self) -> usize {
271 self.0.iter().position(|&x| x == 0).unwrap_or(self.0.len())
272 }
273
274 #[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 pub fn from_bytes(s: &[u8]) -> Name {
286 let mut d: Name = Default::default();
287 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
316pub type Offset = u32;
320
321pub const NUM_LINES_MAX: usize = 64;
323
324#[repr(C)]
328#[derive(Clone, Debug, Eq, PartialEq)]
329pub struct Offsets([Offset; NUM_LINES_MAX]);
330
331impl Offsets {
332 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 #[inline]
343 pub fn get(&self, idx: usize) -> Offset {
344 self.0[idx]
345 }
346
347 #[inline]
349 pub fn set(&mut self, idx: usize, offset: Offset) {
350 self.0[idx] = offset;
351 }
352
353 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#[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#[repr(u32)]
395#[derive(Clone, Copy, Debug, Eq, PartialEq)]
396pub enum LineInfoChangeKind {
397 Requested = 1,
399
400 Released = 2,
402
403 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 pub(crate) fn validate(&self) -> std::result::Result<(), String> {
423 LineInfoChangeKind::try_from(*self as u32).map(|_i| ())
424 }
425}
426
427#[repr(u32)]
431#[derive(Clone, Copy, Debug, Eq, PartialEq)]
432pub enum LineEdgeEventKind {
433 RisingEdge = 1,
435
436 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 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 let mut a = Name::from_bytes("some bytes".as_bytes());
555 assert_eq!(a.as_os_str(), "some bytes");
556
557 a = Name::from_bytes("banana".as_bytes());
559 assert_eq!(a.as_os_str(), "banana");
560
561 let mut a = Name::from_bytes("some bytes".as_bytes());
563 assert_eq!(a.as_os_str(), "some bytes");
564
565 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 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 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 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 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 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 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 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 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 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}