1#![doc = include_str!("../README.md")]
2
3#[cfg(not(target_os = "linux"))]
4compile_error!("This crate support Linux only");
5
6mod iop;
7mod raw;
8mod types;
9mod utils;
10mod values;
11
12use std::{fmt, os::unix::io::RawFd};
13
14pub use iop::RawEvent;
15pub use std::{
16 io::{Error, Result},
17 path::{Path, PathBuf},
18 time::Duration as Time,
19};
20pub use types::{
21 Active, Bias, BitId, Direction, Drive, Edge, EdgeDetect, Event, LineId, LineInfo, LineMap,
22};
23pub use utils::*;
24pub use values::{AsValues, AsValuesMut, Bits, Masked, Values, MAX_BITS, MAX_VALUES};
25
26macro_rules! unsafe_call {
27 ($res:expr) => {
28 unsafe { $res }.map_err(Error::from)
29 };
30}
31
32pub trait AsDevicePath {
34 fn as_device_path(&self) -> PathBuf;
36}
37
38impl AsDevicePath for Path {
39 fn as_device_path(&self) -> PathBuf {
40 if self.is_absolute() {
41 self.to_path_buf()
42 } else {
43 Path::new("/dev").join(self)
44 }
45 }
46}
47
48impl AsDevicePath for &Path {
49 fn as_device_path(&self) -> PathBuf {
50 if self.is_absolute() {
51 self.to_path_buf()
52 } else {
53 Path::new("/dev").join(self)
54 }
55 }
56}
57
58impl AsDevicePath for PathBuf {
59 fn as_device_path(&self) -> PathBuf {
60 let path: &Path = self;
61 path.as_device_path()
62 }
63}
64
65impl AsDevicePath for &PathBuf {
66 fn as_device_path(&self) -> PathBuf {
67 let path: &Path = self;
68 path.as_device_path()
69 }
70}
71
72impl AsDevicePath for &str {
73 fn as_device_path(&self) -> PathBuf {
74 Path::new(self).as_device_path()
75 }
76}
77
78impl AsDevicePath for String {
79 fn as_device_path(&self) -> PathBuf {
80 let s: &str = self;
81 s.as_device_path()
82 }
83}
84
85impl AsDevicePath for &String {
86 fn as_device_path(&self) -> PathBuf {
87 let s: &str = self;
88 s.as_device_path()
89 }
90}
91
92impl AsDevicePath for usize {
93 fn as_device_path(&self) -> PathBuf {
94 format!("/dev/gpiochip{self}").as_device_path()
95 }
96}
97
98macro_rules! as_device_path {
99 ($($type:ty),*) => {
100 $(
101 impl AsDevicePath for $type {
102 fn as_device_path(&self) -> PathBuf {
103 (*self as usize).as_device_path()
104 }
105 }
106 )*
107 };
108}
109
110as_device_path!(u8, u16, u32, u64, i8, i16, i32, i64, isize);
111
112#[derive(Clone, Copy, Default)]
114pub struct Internal<T>(T);
115
116impl<T> core::ops::Deref for Internal<T> {
117 type Target = T;
118
119 fn deref(&self) -> &Self::Target {
120 &self.0
121 }
122}
123
124impl<T> core::ops::DerefMut for Internal<T> {
125 fn deref_mut(&mut self) -> &mut Self::Target {
126 &mut self.0
127 }
128}
129
130pub struct ValuesInfo {
132 chip_name: String,
133 consumer: String,
134 lines: Vec<LineId>,
135 index: LineMap,
136}
137
138impl fmt::Display for ValuesInfo {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 write!(f, "{} {:?} {:?}", self.chip_name, self.consumer, self.lines)
141 }
142}
143
144impl ValuesInfo {
145 pub fn chip_name(&self) -> &str {
147 &self.chip_name
148 }
149
150 pub fn consumer(&self) -> &str {
152 &self.consumer
153 }
154
155 pub fn lines(&self) -> &[LineId] {
157 &self.lines
158 }
159
160 pub fn index(&self) -> &LineMap {
162 &self.index
163 }
164}
165
166impl Internal<ValuesInfo> {
167 fn new(chip_name: &str, consumer: &str, lines: &[LineId]) -> Self {
168 let chip_name = chip_name.into();
169 let consumer = consumer.into();
170 let index = LineMap::new(lines);
171 let lines = lines.to_owned();
172
173 Self(ValuesInfo {
174 chip_name,
175 consumer,
176 lines,
177 index,
178 })
179 }
180
181 pub fn get_values<T: AsValuesMut>(&self, fd: RawFd, values: &mut T) -> Result<()> {
182 #[cfg(not(feature = "v2"))]
183 {
184 let mut data = raw::v1::GpioHandleData::default();
185
186 unsafe_call!(raw::v1::gpio_get_line_values(fd, &mut data))?;
187
188 data.fill_values(self.lines.len(), values);
189 }
190
191 #[cfg(feature = "v2")]
192 {
193 let mut data = values.convert::<Values>();
194 data.truncate(self.lines.len() as _);
195
196 unsafe_call!(raw::v2::gpio_line_get_values(fd, data.as_mut()))?;
197
198 data.copy_into(values);
199 }
200
201 Ok(())
202 }
203
204 pub fn set_values<T: AsValues>(&self, fd: RawFd, values: T) -> Result<()> {
205 #[cfg(not(feature = "v2"))]
206 {
207 let mut data = raw::v1::GpioHandleData::from_values(self.lines.len(), values);
208
209 unsafe_call!(raw::v1::gpio_set_line_values(fd, &mut data))?;
210 }
211
212 #[cfg(feature = "v2")]
213 {
214 let mut data = values.convert::<Values>();
215 data.truncate(self.lines.len() as _);
216
217 unsafe_call!(raw::v2::gpio_line_set_values(fd, data.as_mut()))?;
218 }
219
220 Ok(())
221 }
222}
223
224pub trait DirectionType: Send + Sync + 'static {
226 const DIR: Direction;
227}
228
229pub struct Input;
231
232impl DirectionType for Input {
233 const DIR: Direction = Direction::Input;
234}
235
236pub struct Output;
238
239impl DirectionType for Output {
240 const DIR: Direction = Direction::Output;
241}
242
243pub struct Options<Direction = (), Lines = (), Consumer = ()> {
274 lines: Lines,
275 direction: core::marker::PhantomData<Direction>,
276 active: Active,
277 edge: Option<EdgeDetect>,
278 bias: Option<Bias>,
279 drive: Option<Drive>,
280 values: Option<Values>,
281 consumer: Consumer,
282}
283
284impl Options {
285 pub fn input<Lines: AsRef<[LineId]>>(lines: Lines) -> Options<Input, Lines, &'static str> {
287 Options::<Input, Lines, &'static str> {
288 lines,
289 direction: Default::default(),
290 active: Default::default(),
291 edge: Default::default(),
292 bias: Default::default(),
293 drive: Default::default(),
294 values: Default::default(),
295 consumer: "",
296 }
297 }
298
299 pub fn output<Lines: AsRef<[LineId]>>(lines: Lines) -> Options<Output, Lines, &'static str> {
301 Options::<Output, Lines, &'static str> {
302 lines,
303 direction: Default::default(),
304 active: Default::default(),
305 edge: Default::default(),
306 bias: Default::default(),
307 drive: Default::default(),
308 values: Default::default(),
309 consumer: "",
310 }
311 }
312}
313
314impl<Direction, Lines, OldConsumer> Options<Direction, Lines, OldConsumer> {
315 pub fn consumer<Consumer: AsRef<str>>(
317 self,
318 consumer: Consumer,
319 ) -> Options<Direction, Lines, Consumer> {
320 Options::<Direction, Lines, Consumer> {
321 lines: self.lines,
322 direction: self.direction,
323 active: self.active,
324 edge: self.edge,
325 bias: self.bias,
326 drive: self.drive,
327 values: self.values,
328 consumer,
329 }
330 }
331}
332
333impl<Direction, Lines, Consumer> Options<Direction, Lines, Consumer> {
334 pub fn active(mut self, active: Active) -> Self {
338 self.active = active;
339 self
340 }
341
342 pub fn bias(mut self, bias: Bias) -> Self {
346 self.bias = Some(bias);
347 self
348 }
349}
350
351impl<Direction, Lines: AsRef<[LineId]>, Consumer: AsRef<str>> Options<Direction, Lines, Consumer> {
352 pub fn to_owned(&self) -> Options<Direction, Vec<LineId>, String> {
354 Options::<Direction, Vec<LineId>, String> {
355 lines: self.lines.as_ref().to_owned(),
356 direction: self.direction,
357 active: self.active,
358 edge: self.edge,
359 bias: self.bias,
360 drive: self.drive,
361 values: self.values,
362 consumer: self.consumer.as_ref().to_owned(),
363 }
364 }
365}
366
367impl<Lines, Consumer> Options<Input, Lines, Consumer> {
368 pub fn edge(mut self, edge: EdgeDetect) -> Self {
372 self.edge = Some(edge);
373 self
374 }
375}
376
377impl<Lines, Consumer> Options<Output, Lines, Consumer> {
378 pub fn drive(mut self, drive: Drive) -> Self {
382 self.drive = Some(drive);
383 self
384 }
385
386 pub fn values<T: AsValues>(mut self, values: T) -> Self {
390 self.values = Some(values.convert());
391 self
392 }
393}
394
395pub struct ChipInfo {
397 name: String,
398 label: String,
399 num_lines: LineId,
400}
401
402impl fmt::Display for ChipInfo {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 write!(
405 f,
406 "{} [{}] ({} lines)",
407 self.name, self.label, self.num_lines
408 )
409 }
410}
411
412impl ChipInfo {
413 pub fn name(&self) -> &str {
415 &self.name
416 }
417
418 pub fn label(&self) -> &str {
420 &self.label
421 }
422
423 pub fn num_lines(&self) -> LineId {
425 self.num_lines
426 }
427}
428
429impl Internal<ChipInfo> {
430 pub fn from_fd(fd: RawFd) -> Result<Self> {
431 let mut info = raw::GpioChipInfo::default();
432
433 unsafe_call!(raw::gpio_get_chip_info(fd, &mut info))?;
434
435 Ok(Self(ChipInfo {
436 name: safe_get_str(&info.name)?.into(),
437 label: safe_get_str(&info.label)?.into(),
438 num_lines: info.lines,
439 }))
440 }
441
442 pub fn line_info(&self, fd: RawFd, line: LineId) -> Result<LineInfo> {
444 #[cfg(not(feature = "v2"))]
445 {
446 let mut info = raw::v1::GpioLineInfo {
447 line_offset: line,
448 ..Default::default()
449 };
450
451 unsafe_call!(raw::v1::gpio_get_line_info(fd, &mut info))?;
452
453 info.as_info()
454 }
455
456 #[cfg(feature = "v2")]
457 {
458 let mut info = raw::v2::GpioLineInfo::default();
459
460 info.offset = line;
461
462 unsafe_call!(raw::v2::gpio_get_line_info(fd, &mut info))?;
463
464 info.as_info()
465 }
466 }
467
468 pub fn request_lines<Direction: DirectionType>(
473 &self,
474 fd: RawFd,
475 options: Options<Direction, impl AsRef<[LineId]>, impl AsRef<str>>,
476 ) -> Result<(Internal<ValuesInfo>, RawFd)> {
477 let Options {
478 lines,
479 direction: _,
480 active,
481 edge,
482 bias,
483 drive,
484 values,
485 consumer,
486 } = options;
487
488 let direction = Direction::DIR;
489 let lines = lines.as_ref();
490 let consumer = consumer.as_ref();
491
492 #[cfg(not(feature = "v2"))]
493 let fd = {
494 let mut request =
495 raw::v1::GpioHandleRequest::new(lines, direction, active, bias, drive, consumer)?;
496
497 unsafe_call!(raw::v1::gpio_get_line_handle(fd, &mut request))?;
500
501 if let Some(values) = values {
502 let mut data = raw::v1::GpioHandleData::from_values(lines.len(), &values);
503
504 unsafe_call!(raw::v1::gpio_set_line_values(fd, &mut data))?;
505 }
506
507 request.fd
508 };
509
510 #[cfg(feature = "v2")]
511 let fd = {
512 let mut request = raw::v2::GpioLineRequest::new(
513 lines, direction, active, edge, bias, drive, values, consumer,
514 )?;
515
516 unsafe_call!(raw::v2::gpio_get_line(fd, &mut request))?;
517
518 request.fd
519 };
520
521 Ok((Internal::<ValuesInfo>::new(&self.name, consumer, lines), fd))
522 }
523}
524
525#[cfg(test)]
526mod test {
527 use super::*;
528
529 #[test]
530 fn input_options() {
531 let _ = Options::input([27, 1, 19])
532 .bias(Bias::PullUp)
533 .active(Active::Low)
534 .edge(EdgeDetect::Both)
535 .consumer("gpin");
536 }
537
538 #[test]
539 fn output_options() {
540 let _ = Options::output([11, 2])
541 .bias(Bias::PullUp)
542 .active(Active::Low)
543 .consumer("gpout")
544 .drive(Drive::OpenDrain)
545 .values([true, false]);
546 }
547}