1#![doc = include_str!("../README.md")]
2
3use std::{
4 fs::{File, OpenOptions},
5 io::Read,
6 marker::PhantomData,
7 ops::Deref,
8 os::unix::{
9 fs::{FileTypeExt, MetadataExt},
10 io::{AsRawFd, FromRawFd},
11 },
12 path::{Path, PathBuf},
13 sync::Arc,
14};
15
16use gpiod_core::{invalid_input, major, minor, set_nonblock, AsDevicePath, Internal, Result};
17
18pub use gpiod_core::{
19 Active, AsValues, AsValuesMut, Bias, BitId, ChipInfo, Direction, DirectionType, Drive, Edge,
20 EdgeDetect, Event, Input, LineId, LineInfo, Masked, Options, Output, Values, ValuesInfo,
21 MAX_BITS, MAX_VALUES,
22};
23
24use async_fs as fs;
25use async_io::Async;
26use blocking::unblock as asyncify;
27use futures_lite::stream::StreamExt;
28
29pub struct Lines<Direction> {
34 dir: PhantomData<Direction>,
35 info: Arc<Internal<ValuesInfo>>,
36 file: Async<File>,
38}
39
40impl<Direction> Deref for Lines<Direction> {
41 type Target = ValuesInfo;
42
43 fn deref(&self) -> &Self::Target {
44 &self.info
45 }
46}
47
48impl<Direction: DirectionType> Lines<Direction> {
49 pub async fn get_values<T: AsValuesMut + Send + 'static>(&self, mut values: T) -> Result<T> {
55 let fd = self.file.as_raw_fd();
56 let info = self.info.clone();
57 asyncify(move || info.get_values(fd, &mut values).map(|_| values)).await
58 }
59}
60
61impl Lines<Input> {
62 pub async fn read_event(&mut self) -> Result<Event> {
67 #[cfg(not(feature = "v2"))]
68 {
69 todo!();
70 }
71
72 #[cfg(feature = "v2")]
73 {
74 self.file.readable().await?;
75
76 let mut event = gpiod_core::RawEvent::default();
77 let len = self.file.get_ref().read(event.as_mut())?;
78
79 gpiod_core::check_size(len, &event)?;
80
81 event.as_event(self.info.index())
82 }
83 }
84}
85
86impl Lines<Output> {
87 pub async fn set_values<T: AsValues + Send + 'static>(&self, values: T) -> Result<()> {
92 let fd = self.file.as_raw_fd();
93 let info = self.info.clone();
94 asyncify(move || info.set_values(fd, values)).await
95 }
96}
97
98pub struct Chip {
103 info: Arc<Internal<ChipInfo>>,
104 file: Async<File>,
106}
107
108impl Deref for Chip {
109 type Target = ChipInfo;
110
111 fn deref(&self) -> &Self::Target {
112 &self.info
113 }
114}
115
116impl core::fmt::Display for Chip {
117 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118 self.info.fmt(f)
119 }
120}
121
122impl Chip {
123 pub async fn new(device: impl AsDevicePath) -> Result<Chip> {
125 let path = device.as_device_path();
126
127 Chip::check_device(path.as_ref()).await?;
128
129 let file = Async::new(
130 asyncify(move || OpenOptions::new().read(true).write(true).open(path)).await?,
131 )?;
132
133 let fd = file.as_raw_fd();
134 let info = Arc::new(asyncify(move || Internal::<ChipInfo>::from_fd(fd)).await?);
135
136 Ok(Chip { info, file })
137 }
138
139 pub async fn list_devices() -> Result<Vec<PathBuf>> {
141 let mut devices = Vec::new();
142 let mut dir = fs::read_dir("/dev").await?;
143
144 while let Some(ent) = dir.next().await {
145 let path = ent?.path();
146 if Self::check_device(&path).await.is_ok() {
147 devices.push(path);
148 }
149 }
150
151 Ok(devices)
152 }
153
154 async fn check_device(path: &Path) -> Result<()> {
155 let metadata = fs::symlink_metadata(&path).await?;
156
157 if !metadata.file_type().is_char_device() {
159 return Err(invalid_input("File is not character device"));
160 }
161
162 let rdev = metadata.rdev();
163
164 if fs::canonicalize(format!(
166 "/sys/dev/char/{}:{}/subsystem",
167 major(rdev),
168 minor(rdev)
169 ))
170 .await?
171 != Path::new("/sys/bus/gpio")
172 {
173 return Err(invalid_input("Character device is not a GPIO"));
174 }
175
176 Ok(())
177 }
178
179 pub async fn line_info(&self, line: LineId) -> Result<LineInfo> {
181 let fd = self.file.as_raw_fd();
182 let info = self.info.clone();
183 asyncify(move || info.line_info(fd, line)).await
184 }
185
186 pub async fn request_lines<Direction: DirectionType>(
191 &self,
192 options: Options<Direction, impl AsRef<[LineId]>, impl AsRef<str>>,
193 ) -> Result<Lines<Direction>> {
194 let fd = self.file.as_raw_fd();
195 let options = options.to_owned();
196 let info = self.info.clone();
197
198 let (info, fd) = asyncify(move || -> Result<_> {
199 let (info, fd) = info.request_lines(fd, options)?;
200 set_nonblock(fd)?;
201 Ok((info, fd))
202 })
203 .await?;
204
205 let file = Async::new(unsafe { File::from_raw_fd(fd) })?;
206 let info = Arc::new(info);
207
208 Ok(Lines {
209 dir: PhantomData,
210 info,
211 file,
212 })
213 }
214}