1use super::line::Offset;
6use crate::{
7 line, line::InfoChangeEvent, AbiSupportKind, AbiVersion, AbiVersion::*, Error, Result, UapiCall,
8};
9#[cfg(all(feature = "uapi_v1", not(feature = "uapi_v2")))]
10use gpiocdev_uapi::v1 as uapi;
11#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
12use gpiocdev_uapi::v2 as uapi;
13#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
14use gpiocdev_uapi::{v1, v2};
15#[cfg(feature = "serde")]
16use serde_derive::{Deserialize, Serialize};
17#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
18use std::cell::Cell;
19use std::fmt;
20use std::fs;
21use std::mem;
22use std::ops::Range;
23#[cfg(target_os = "android")]
24use std::os::android::fs::MetadataExt;
25#[cfg(target_os = "linux")]
26use std::os::linux::fs::MetadataExt;
27use std::os::unix::prelude::{AsFd, AsRawFd, BorrowedFd, OsStrExt};
28use std::path::{Path, PathBuf};
29use std::time::Duration;
30
31const CHARDEV_MODE: u32 = 0x2000;
32
33pub fn is_chip<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
37 let pb = fs::canonicalize(&path)?;
38 if let Some(pbstr) = pb.to_str() {
40 if let Some(num) = pbstr.strip_prefix("/dev/gpiochip") {
41 if num.chars().all(|c| char::is_digit(c, 10)) {
42 return Ok(pb);
43 }
44 }
45 }
46
47 let m = fs::metadata(&pb)?;
49 if m.st_mode() & CHARDEV_MODE == 0 {
50 return Err(Error::GpioChip(pb, ErrorKind::NotCharacterDevice));
51 }
52 let mut sysfs_dev = PathBuf::from("/sys/bus/gpio/devices");
53 sysfs_dev.push(pb.file_name().unwrap());
54 sysfs_dev.push("dev");
55 if let Ok(rdev) = fs::read_to_string(sysfs_dev) {
56 let st_rdev = m.st_rdev();
57 let dev_str = format!("{}:{}", (st_rdev as u16 >> 8) as u8, st_rdev as u8);
58 if rdev.trim_end() == dev_str {
59 return Ok(pb);
60 }
61 }
62 Err(Error::GpioChip(pb, ErrorKind::NotGpioDevice))
63}
64
65pub fn path_compare(a: &Path, b: &Path) -> std::cmp::Ordering {
69 let a = a.as_os_str().as_bytes();
70 let b = b.as_os_str().as_bytes();
71
72 if a.len() == b.len() {
73 return a.cmp(b);
75 }
76 for it in a.iter().zip(b.iter()) {
77 let (ai, bi) = it;
78 if *ai != *bi {
79 if !ai.is_ascii_digit() || !bi.is_ascii_digit() {
80 return (*ai).cmp(bi);
82 }
83 break;
85 }
86 }
87 a.len().cmp(&b.len())
89}
90
91pub fn chips() -> Result<Vec<PathBuf>> {
96 let mut chips = std::fs::read_dir("/dev")?
97 .filter_map(|x| x.ok())
98 .flat_map(|de| is_chip(de.path()))
99 .collect::<Vec<PathBuf>>();
100 chips.sort_unstable_by(|a, b| path_compare(a, b));
101 chips.dedup();
102 Ok(chips)
103}
104
105pub struct LineInfoIterator<'a> {
107 chip: &'a Chip,
108 offsets: Range<Offset>,
109}
110
111impl Iterator for LineInfoIterator<'_> {
112 type Item = Result<line::Info>;
113
114 fn next(&mut self) -> Option<Result<line::Info>> {
115 self.offsets
116 .next()
117 .map(|offset| self.chip.line_info(offset))
118 }
119}
120
121#[derive(Debug)]
123pub struct Chip {
124 path: PathBuf,
126 pub(crate) f: fs::File,
128 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
129 abiv: Cell<Option<AbiVersion>>,
130}
131
132impl Chip {
133 pub fn from_path<P: AsRef<Path>>(p: P) -> Result<Chip> {
145 let path = is_chip(p.as_ref())?;
146 let f = fs::File::open(&path)?;
147 Ok(Chip {
148 path,
149 f,
150 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
151 abiv: Default::default(),
152 })
153 }
154
155 pub fn from_name(n: &str) -> Result<Chip> {
167 let path = is_chip(format!("/dev/{n}"))?;
168 let f = fs::File::open(&path)?;
169 Ok(Chip {
170 path,
171 f,
172 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
173 abiv: Default::default(),
174 })
175 }
176
177 pub fn info(&self) -> Result<Info> {
179 Ok(Info::from(
180 uapi::get_chip_info(&self.f).map_err(|e| Error::Uapi(UapiCall::GetChipInfo, e))?,
181 ))
182 }
183
184 pub fn name(&self) -> String {
191 String::from(self.path.file_name().unwrap().to_string_lossy())
193 }
194
195 pub fn path(&self) -> &Path {
197 self.path.as_ref()
198 }
199
200 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
202 fn actual_abi_version(&self) -> Result<AbiVersion> {
203 Ok(match self.abiv.get() {
204 Some(abiv) => abiv,
205 None => {
206 let abiv = self.detect_abi_version()?;
207 self.abiv.set(Some(abiv));
208 abiv
209 }
210 })
211 }
212
213 pub fn find_line_info(&self, name: &str) -> Option<line::Info> {
217 self.line_info_iter()
218 .ok()
219 .and_then(|iter| iter.filter_map(|x| x.ok()).find(|li| li.name == name))
220 }
221
222 pub fn line_info(&self, offset: Offset) -> Result<line::Info> {
224 self.do_line_info(offset)
225 }
226 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
227 fn do_line_info(&self, offset: Offset) -> Result<line::Info> {
228 match self.actual_abi_version()? {
229 V1 => v1::get_line_info(&self.f, offset)
230 .map(|li| line::Info::from(&li))
231 .map_err(|e| Error::Uapi(UapiCall::GetLineInfo, e)),
232 V2 => {
233 let uli = uapi::get_line_info(&self.f, offset)
234 .map_err(|e| Error::Uapi(UapiCall::GetLineInfo, e))?;
235 line::Info::try_from(&uli)
236 }
237 }
238 }
239 #[cfg(all(feature = "uapi_v1", not(feature = "uapi_v2")))]
240 fn do_line_info(&self, offset: Offset) -> Result<line::Info> {
241 uapi::get_line_info(&self.f, offset)
242 .map(|li| line::Info::from(&li))
243 .map_err(|e| Error::Uapi(UapiCall::GetLineInfo, e))
244 }
245 #[cfg(all(not(feature = "uapi_v1"), feature = "uapi_v2"))]
246 fn do_line_info(&self, offset: Offset) -> Result<line::Info> {
247 let uli = uapi::get_line_info(&self.f, offset)
248 .map_err(|e| Error::Uapi(UapiCall::GetLineInfo, e))?;
249 line::Info::try_from(&uli)
250 }
251
252 pub fn line_info_iter(&self) -> Result<LineInfoIterator<'_>> {
254 let cinfo = self.info()?;
255 Ok(LineInfoIterator {
256 chip: self,
257 offsets: Range {
258 start: 0,
259 end: cinfo.num_lines,
260 },
261 })
262 }
263
264 pub fn watch_line_info(&self, offset: Offset) -> Result<line::Info> {
268 self.do_watch_line_info(offset)
269 }
270 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
271 fn do_watch_line_info(&self, offset: Offset) -> Result<line::Info> {
272 match self.actual_abi_version()? {
273 V1 => v1::watch_line_info(&self.f, offset)
274 .map(|li| line::Info::from(&li))
275 .map_err(|e| Error::Uapi(UapiCall::WatchLineInfo, e)),
276 V2 => {
277 let uli = uapi::watch_line_info(&self.f, offset)
278 .map_err(|e| Error::Uapi(UapiCall::WatchLineInfo, e))?;
279 line::Info::try_from(&uli)
280 }
281 }
282 }
283 #[cfg(all(feature = "uapi_v1", not(feature = "uapi_v2")))]
284 fn do_watch_line_info(&self, offset: Offset) -> Result<line::Info> {
285 uapi::watch_line_info(&self.f, offset)
286 .map(|li| line::Info::from(&li))
287 .map_err(|e| Error::Uapi(UapiCall::WatchLineInfo, e))
288 }
289 #[cfg(all(not(feature = "uapi_v1"), feature = "uapi_v2"))]
290 fn do_watch_line_info(&self, offset: Offset) -> Result<line::Info> {
291 let uwli = uapi::watch_line_info(&self.f, offset)
292 .map_err(|e| Error::Uapi(UapiCall::WatchLineInfo, e))?;
293 line::Info::try_from(&uwli)
294 }
295
296 pub fn unwatch_line_info(&self, offset: Offset) -> Result<()> {
300 uapi::unwatch_line_info(&self.f, offset)
301 .map_err(|e| Error::Uapi(UapiCall::UnwatchLineInfo, e))
302 }
303
304 pub fn has_line_info_change_event(&self) -> Result<bool> {
306 gpiocdev_uapi::has_event(&self.f).map_err(|e| Error::Uapi(UapiCall::HasEvent, e))
307 }
308
309 pub fn wait_line_info_change_event(&self, timeout: Duration) -> Result<bool> {
311 gpiocdev_uapi::wait_event(&self.f, timeout).map_err(|e| Error::Uapi(UapiCall::WaitEvent, e))
312 }
313
314 pub fn read_line_info_change_event(&self) -> Result<InfoChangeEvent> {
318 self.do_read_line_info_change_event()
319 }
320 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
321 fn do_read_line_info_change_event(&self) -> Result<InfoChangeEvent> {
322 self.actual_abi_version()?;
323 debug_assert!(
325 mem::size_of::<v2::LineInfoChangeEvent>() >= mem::size_of::<v1::LineInfoChangeEvent>()
326 );
327 let mut bbuf = [0_u64; mem::size_of::<v2::LineInfoChangeEvent>() / 8];
328 let evt_u64_size = self.line_info_change_event_u64_size();
329 let buf = &mut bbuf[0..evt_u64_size];
331 let n = gpiocdev_uapi::read_event(&self.f, buf)
332 .map_err(|e| Error::Uapi(UapiCall::ReadEvent, e))?;
333 self.line_info_change_event_from_slice(&buf[0..n])
334 }
335 #[cfg(not(all(feature = "uapi_v1", feature = "uapi_v2")))]
336 fn do_read_line_info_change_event(&self) -> Result<InfoChangeEvent> {
337 let mut buf = [0_u64; mem::size_of::<uapi::LineInfoChangeEvent>() / 8];
338 let n = gpiocdev_uapi::read_event(&self.f, &mut buf)
339 .map_err(|e| Error::Uapi(UapiCall::ReadEvent, e))?;
340 self.line_info_change_event_from_slice(&buf[0..n])
341 }
342
343 pub fn info_change_events(&self) -> InfoChangeIterator<'_> {
345 InfoChangeIterator {
346 chip: self,
347 buf: vec![0_u64; self.line_info_change_event_u64_size()],
348 }
349 }
350
351 pub fn detect_abi_version(&self) -> Result<AbiVersion> {
353 for abiv in [V2, V1] {
355 if self.supports_abi_version(abiv).is_ok() {
356 return Ok(abiv);
357 }
358 }
359 Err(Error::NoAbiSupport())
360 }
361
362 pub fn supports_abi_version(&self, abiv: AbiVersion) -> Result<()> {
364 self.do_supports_abi_version(abiv)
365 }
366 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
367 fn do_supports_abi_version(&self, abiv: AbiVersion) -> Result<()> {
368 let res = match abiv {
369 V1 => v1::get_line_info(&self.f, 0).map(|_| ()),
370 V2 => v2::get_line_info(&self.f, 0).map(|_| ()),
371 };
372 res.map_err(|_| Error::UnsupportedAbi(abiv, AbiSupportKind::Kernel))
373 }
374 #[cfg(all(feature = "uapi_v1", not(feature = "uapi_v2")))]
375 fn do_supports_abi_version(&self, abiv: AbiVersion) -> Result<()> {
376 match abiv {
377 V1 => uapi::get_line_info(&self.f, 0)
378 .map(|_| ())
379 .map_err(|_| Error::UnsupportedAbi(V1, AbiSupportKind::Kernel)),
380 V2 => Err(Error::UnsupportedAbi(AbiVersion::V2, AbiSupportKind::Build)),
381 }
382 }
383 #[cfg(not(feature = "uapi_v1"))]
384 fn do_supports_abi_version(&self, abiv: AbiVersion) -> Result<()> {
385 match abiv {
386 V2 => uapi::get_line_info(&self.f, 0)
387 .map(|_| ())
388 .map_err(|_| Error::UnsupportedAbi(V2, AbiSupportKind::Kernel)),
389 V1 => Err(Error::UnsupportedAbi(AbiVersion::V1, AbiSupportKind::Build)),
390 }
391 }
392
393 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
395 pub fn using_abi_version(&mut self, abiv: AbiVersion) -> &mut Self {
396 self.abiv.set(Some(abiv));
397 self
398 }
399
400 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
401 fn line_info_change_event_from_slice(&self, d: &[u64]) -> Result<InfoChangeEvent> {
402 match self.actual_abi_version()? {
403 V1 => InfoChangeEvent::try_from(
404 v1::LineInfoChangeEvent::from_slice(d)
405 .map_err(|e| Error::Uapi(UapiCall::LICEFromBuf, e))?,
406 ),
407 V2 => InfoChangeEvent::try_from(
408 v2::LineInfoChangeEvent::from_slice(d)
409 .map_err(|e| Error::Uapi(UapiCall::LICEFromBuf, e))?,
410 ),
411 }
412 }
413 #[cfg(not(all(feature = "uapi_v1", feature = "uapi_v2")))]
414 fn line_info_change_event_from_slice(&self, d: &[u64]) -> Result<InfoChangeEvent> {
415 InfoChangeEvent::try_from(
416 uapi::LineInfoChangeEvent::from_slice(d)
417 .map_err(|e| Error::Uapi(UapiCall::LICEFromBuf, e))?,
418 )
419 }
420
421 fn line_info_change_event_u64_size(&self) -> usize {
422 self.line_info_change_event_size() / 8
423 }
424
425 #[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
426 fn line_info_change_event_size(&self) -> usize {
427 match self.abiv.get().unwrap_or(V2) {
430 V1 => mem::size_of::<v1::LineInfoChangeEvent>(),
431 V2 => mem::size_of::<v2::LineInfoChangeEvent>(),
432 }
433 }
434 #[cfg(not(all(feature = "uapi_v1", feature = "uapi_v2")))]
435 fn line_info_change_event_size(&self) -> usize {
436 mem::size_of::<uapi::LineInfoChangeEvent>()
437 }
438}
439
440impl AsFd for Chip {
441 #[inline]
442 fn as_fd(&self) -> BorrowedFd<'_> {
443 self.f.as_fd()
444 }
445}
446
447impl AsRawFd for Chip {
448 #[inline]
449 fn as_raw_fd(&self) -> i32 {
450 self.f.as_raw_fd()
451 }
452}
453
454impl AsRef<Chip> for Chip {
455 #[inline]
456 fn as_ref(&self) -> &Chip {
457 self
458 }
459}
460
461#[derive(Clone, Debug, Default, Eq, PartialEq)]
463#[cfg_attr(
464 feature = "serde",
465 derive(Serialize, Deserialize),
466 serde(rename_all = "camelCase")
467)]
468pub struct Info {
469 pub name: String,
471
472 pub label: String,
476
477 pub num_lines: u32,
479}
480
481impl From<uapi::ChipInfo> for Info {
482 fn from(ci: uapi::ChipInfo) -> Self {
483 Info {
484 name: String::from(&ci.name),
485 label: String::from(&ci.label),
486 num_lines: ci.num_lines,
487 }
488 }
489}
490
491pub struct InfoChangeIterator<'a> {
495 chip: &'a Chip,
496
497 buf: Vec<u64>,
499}
500
501impl InfoChangeIterator<'_> {
502 fn read_event(&mut self) -> Result<InfoChangeEvent> {
503 let n = gpiocdev_uapi::read_event(&self.chip.f, &mut self.buf)
504 .map_err(|e| Error::Uapi(UapiCall::ReadEvent, e))?;
505 self.chip.line_info_change_event_from_slice(&self.buf[0..n])
506 }
507}
508
509impl Iterator for InfoChangeIterator<'_> {
510 type Item = Result<InfoChangeEvent>;
511
512 fn next(&mut self) -> Option<Self::Item> {
513 Some(self.read_event())
514 }
515}
516
517#[derive(Clone, Copy, Debug, Eq, PartialEq)]
519pub enum ErrorKind {
520 NotCharacterDevice,
522
523 NotGpioDevice,
525}
526
527impl fmt::Display for ErrorKind {
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529 let msg = match self {
530 ErrorKind::NotCharacterDevice => "is not a character device",
531 ErrorKind::NotGpioDevice => "is not a GPIO character device",
532 };
533 write!(f, "{msg}")
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540 #[cfg(all(feature = "uapi_v1", not(feature = "uapi_v2")))]
541 use gpiocdev_uapi::v1 as uapi;
542 #[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
543 use gpiocdev_uapi::v2 as uapi;
544
545 mod info {
549 use super::{uapi, Info};
550
551 #[test]
552 fn from_uapi() {
553 let ui = uapi::ChipInfo {
554 name: "banana".into(),
555 label: "peel".into(),
556 num_lines: 42,
557 };
558 let i = Info::from(ui);
559 assert_eq!(i.num_lines, 42);
560 assert_eq!(i.name.as_str(), "banana");
561 assert_eq!(i.label.as_str(), "peel");
562 }
563 }
564
565 #[test]
566 fn path_compare() {
567 use super::path_compare;
568 use std::cmp::Ordering;
569
570 assert_eq!(
571 path_compare(Path::new("/dev/gpiochip0"), Path::new("/dev/gpiochip0")),
572 Ordering::Equal
573 );
574 assert_eq!(
575 path_compare(Path::new("/dev/gpiochip0"), Path::new("/dev/gpiochip1")),
576 Ordering::Less
577 );
578 assert_eq!(
579 path_compare(Path::new("/dev/gpiochip3"), Path::new("/dev/gpiochip10")),
580 Ordering::Less
581 );
582 assert_eq!(
583 path_compare(Path::new("/dev/gpiochip3"), Path::new("/dev/gpiochip30")),
584 Ordering::Less
585 );
586 assert_eq!(
587 path_compare(Path::new("/dev/gpiochip10"), Path::new("/dev/gpiochip3")),
588 Ordering::Greater
589 );
590 assert_eq!(
591 path_compare(Path::new("/dev/gpiochip"), Path::new("/dev/gpiochip1")),
592 Ordering::Less
593 );
594 assert_eq!(
595 path_compare(Path::new("/dev/gpiochip0"), Path::new("/dev/gpiochip1")),
596 Ordering::Less
597 );
598 }
599}