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, Default, 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 = ChipInfo::default();
126 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
139pub 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#[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
167pub type Result<T> = std::result::Result<T, Error>;
171
172#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
176pub enum Error {
177 #[error(transparent)]
179 Os(Errno),
180
181 #[error(transparent)]
183 UnderRead(#[from] UnderReadError),
184}
185
186impl Error {
187 #[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#[derive(Clone, Debug, thiserror::Error, Eq, PartialEq)]
206#[error("Reading {obj} returned {found} bytes, expected {expected}.")]
207pub struct UnderReadError {
208 pub obj: &'static str,
210 pub expected: usize,
212 pub found: usize,
214}
215
216impl UnderReadError {
217 pub(crate) fn new(obj: &'static str, expected: usize, found: usize) -> UnderReadError {
219 UnderReadError {
220 obj,
221 expected,
222 found,
223 }
224 }
225}
226
227pub const NAME_LEN_MAX: usize = 32;
229
230#[repr(C)]
232#[derive(Clone, Debug, Default, Eq, PartialEq)]
233pub struct Name(pub [u8; NAME_LEN_MAX]);
234
235impl Name {
236 #[inline]
238 pub fn is_empty(&self) -> bool {
239 self.0[0] == 0
240 }
241
242 #[inline]
244 pub fn strlen(&self) -> usize {
245 self.0.iter().position(|&x| x == 0).unwrap_or(self.0.len())
246 }
247
248 #[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 pub fn from_bytes(s: &[u8]) -> Name {
260 let mut d: Name = Default::default();
261 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
290pub type Offset = u32;
294
295pub const NUM_LINES_MAX: usize = 64;
297
298#[repr(C)]
302#[derive(Clone, Debug, Eq, PartialEq)]
303pub struct Offsets(pub [Offset; NUM_LINES_MAX]);
304
305impl Offsets {
306 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 #[inline]
317 pub fn get(&self, idx: usize) -> Offset {
318 self.0[idx]
319 }
320
321 #[inline]
323 pub fn set(&mut self, idx: usize, offset: Offset) {
324 self.0[idx] = offset;
325 }
326
327 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#[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#[repr(u32)]
369#[derive(Clone, Copy, Debug, Eq, PartialEq)]
370pub enum LineInfoChangeKind {
371 Requested = 1,
373
374 Released = 2,
376
377 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#[repr(u32)]
398#[derive(Clone, Copy, Debug, Eq, PartialEq)]
399pub enum LineEdgeEventKind {
400 RisingEdge = 1,
402
403 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 let mut a = Name::from_bytes("some bytes".as_bytes());
527 assert_eq!(a.as_os_str(), "some bytes");
528
529 a = Name::from_bytes("banana".as_bytes());
531 assert_eq!(a.as_os_str(), "banana");
532
533 let mut a = Name::from_bytes("some bytes".as_bytes());
535 assert_eq!(a.as_os_str(), "some bytes");
536
537 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 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 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 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 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 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 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 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("overly long truncated name ->𝄞<- cut here".as_bytes());
571 assert_eq!(a.as_os_str(), "overly long truncated name ->");
572
573 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}