1mod config;
6pub use self::config::Config;
7
8mod event;
9pub use self::event::{EdgeEvent, EdgeKind, InfoChangeEvent, InfoChangeKind};
10
11mod info;
12pub use self::info::Info;
13
14mod value;
15pub use self::value::{Value, Values};
16
17#[cfg(feature = "uapi_v1")]
18use gpiocdev_uapi::v1;
19#[cfg(feature = "uapi_v2")]
20use gpiocdev_uapi::v2;
21#[cfg(feature = "serde")]
22use serde_derive::{Deserialize, Serialize};
23use std::collections::HashMap;
24use std::hash::{BuildHasherDefault, Hasher};
25
26pub type Offset = u32;
30
31pub type OffsetMap<T> = HashMap<Offset, T, BuildHasherDefault<OffsetHasher>>;
33
34#[derive(Default)]
36pub struct OffsetHasher(u64);
37
38impl Hasher for OffsetHasher {
39 fn finish(&self) -> u64 {
40 self.0
41 }
42
43 fn write(&mut self, _: &[u8]) {
44 panic!("OffsetHasher key must be u32")
45 }
46
47 fn write_u32(&mut self, n: u32) {
48 self.0 = n.into()
49 }
50}
51
52pub type Offsets = Vec<Offset>;
54
55#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58pub enum Direction {
59 #[default]
61 Input,
62
63 Output,
65}
66
67#[cfg(feature = "uapi_v1")]
68impl From<v1::LineInfoFlags> for Direction {
69 fn from(flags: v1::LineInfoFlags) -> Self {
70 if flags.contains(v1::LineInfoFlags::OUTPUT) {
71 return Direction::Output;
72 }
73 Direction::Input
74 }
75}
76#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
77impl From<v2::LineFlags> for Direction {
78 fn from(flags: v2::LineFlags) -> Self {
79 if flags.contains(v2::LineFlags::OUTPUT) {
80 return Direction::Output;
81 }
82 Direction::Input
83 }
84}
85
86#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89pub enum Bias {
90 PullUp,
92
93 PullDown,
95
96 Disabled,
98}
99
100#[cfg(feature = "uapi_v1")]
101impl TryFrom<v1::LineInfoFlags> for Bias {
102 type Error = ();
103
104 fn try_from(flags: v1::LineInfoFlags) -> Result<Self, Self::Error> {
105 if flags.contains(v1::LineInfoFlags::BIAS_PULL_UP) {
106 return Ok(Bias::PullUp);
107 }
108 if flags.contains(v1::LineInfoFlags::BIAS_PULL_DOWN) {
109 return Ok(Bias::PullDown);
110 }
111 if flags.contains(v1::LineInfoFlags::BIAS_DISABLED) {
112 return Ok(Bias::Disabled);
113 }
114 Err(())
115 }
116}
117#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
118impl TryFrom<v2::LineFlags> for Bias {
119 type Error = ();
120
121 fn try_from(flags: v2::LineFlags) -> Result<Self, Self::Error> {
122 if flags.contains(v2::LineFlags::BIAS_PULL_UP) {
123 return Ok(Bias::PullUp);
124 }
125 if flags.contains(v2::LineFlags::BIAS_PULL_DOWN) {
126 return Ok(Bias::PullDown);
127 }
128 if flags.contains(v2::LineFlags::BIAS_DISABLED) {
129 return Ok(Bias::Disabled);
130 }
131 Err(())
132 }
133}
134
135#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138pub enum Drive {
139 #[default]
143 PushPull,
144
145 OpenDrain,
147
148 OpenSource,
150}
151
152#[cfg(feature = "uapi_v1")]
153impl TryFrom<v1::LineInfoFlags> for Drive {
154 type Error = ();
155
156 fn try_from(flags: v1::LineInfoFlags) -> Result<Self, Self::Error> {
157 if flags.contains(v1::LineInfoFlags::OPEN_DRAIN) {
158 return Ok(Drive::OpenDrain);
159 }
160 if flags.contains(v1::LineInfoFlags::OPEN_SOURCE) {
161 return Ok(Drive::OpenSource);
162 }
163 if flags.contains(v1::LineInfoFlags::OUTPUT) {
164 return Ok(Drive::PushPull);
165 }
166 Err(())
167 }
168}
169#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
170impl TryFrom<v2::LineFlags> for Drive {
171 type Error = ();
172
173 fn try_from(flags: v2::LineFlags) -> Result<Self, Self::Error> {
174 if flags.contains(v2::LineFlags::OPEN_DRAIN) {
175 return Ok(Drive::OpenDrain);
176 }
177 if flags.contains(v2::LineFlags::OPEN_SOURCE) {
178 return Ok(Drive::OpenSource);
179 }
180 if flags.contains(v2::LineFlags::OUTPUT) {
181 return Ok(Drive::PushPull);
182 }
183 Err(())
184 }
185}
186
187#[derive(Clone, Copy, Debug, Eq, PartialEq)]
189#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
190pub enum EdgeDetection {
191 RisingEdge,
195
196 FallingEdge,
200
201 BothEdges,
203}
204#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
205impl TryFrom<v2::LineFlags> for EdgeDetection {
206 type Error = ();
207
208 fn try_from(flags: v2::LineFlags) -> Result<Self, Self::Error> {
209 if flags.contains(v2::LineFlags::EDGE_RISING | v2::LineFlags::EDGE_FALLING) {
210 return Ok(EdgeDetection::BothEdges);
211 }
212 if flags.contains(v2::LineFlags::EDGE_RISING) {
213 return Ok(EdgeDetection::RisingEdge);
214 }
215 if flags.contains(v2::LineFlags::EDGE_FALLING) {
216 return Ok(EdgeDetection::FallingEdge);
217 }
218 Err(())
219 }
220}
221
222#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
224#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
225pub enum EventClock {
226 #[default]
230 Monotonic,
231
232 Realtime,
234
235 Hte,
240}
241
242#[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
243impl From<v2::LineFlags> for EventClock {
244 fn from(flags: v2::LineFlags) -> Self {
245 if flags.contains(v2::LineFlags::EVENT_CLOCK_REALTIME) {
246 return EventClock::Realtime;
247 }
248 if flags.contains(v2::LineFlags::EVENT_CLOCK_HTE) {
249 return EventClock::Hte;
250 }
251 EventClock::Monotonic
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 mod direction {
259 use super::*;
260
261 #[test]
262 fn default() {
263 assert_eq!(Direction::default(), Direction::Input);
264 }
265
266 #[test]
267 #[cfg(feature = "uapi_v1")]
268 fn from_v1_line_info_flags() {
269 assert_eq!(
270 Direction::from(v1::LineInfoFlags::OUTPUT),
271 Direction::Output
272 );
273 assert_eq!(
274 Direction::from(v1::LineInfoFlags::ACTIVE_LOW),
275 Direction::Input
276 );
277 }
278
279 #[test]
280 #[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
281 fn from_v2_line_flags() {
282 assert_eq!(Direction::from(v2::LineFlags::OUTPUT), Direction::Output);
283 assert_eq!(Direction::from(v2::LineFlags::INPUT), Direction::Input);
284 }
285 }
286
287 mod bias {
288 use super::*;
289
290 #[test]
291 #[cfg(feature = "uapi_v1")]
292 fn try_from_v1_line_info_flags() {
293 assert_eq!(Bias::try_from(v1::LineInfoFlags::ACTIVE_LOW), Err(()));
294 assert_eq!(
295 Bias::try_from(v1::LineInfoFlags::BIAS_PULL_DOWN),
296 Ok(Bias::PullDown)
297 );
298 assert_eq!(
299 Bias::try_from(v1::LineInfoFlags::BIAS_PULL_UP),
300 Ok(Bias::PullUp)
301 );
302 assert_eq!(
303 Bias::try_from(v1::LineInfoFlags::BIAS_DISABLED),
304 Ok(Bias::Disabled)
305 );
306 }
307
308 #[test]
309 #[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
310 fn from_v2_line_flags() {
311 assert_eq!(Bias::try_from(v2::LineFlags::INPUT), Err(()));
312 assert_eq!(
313 Bias::try_from(v2::LineFlags::BIAS_PULL_DOWN),
314 Ok(Bias::PullDown)
315 );
316 assert_eq!(
317 Bias::try_from(v2::LineFlags::BIAS_PULL_UP),
318 Ok(Bias::PullUp)
319 );
320 assert_eq!(
321 Bias::try_from(v2::LineFlags::BIAS_DISABLED),
322 Ok(Bias::Disabled)
323 );
324 }
325 }
326
327 mod drive {
328 use super::*;
329
330 #[test]
331 fn default() {
332 assert_eq!(Drive::default(), Drive::PushPull);
333 }
334
335 #[test]
336 #[cfg(feature = "uapi_v1")]
337 fn try_from_v1_line_info_flags() {
338 assert_eq!(Drive::try_from(v1::LineInfoFlags::ACTIVE_LOW), Err(()));
339 assert_eq!(
340 Drive::try_from(v1::LineInfoFlags::OUTPUT),
341 Ok(Drive::PushPull)
342 );
343 assert_eq!(
344 Drive::try_from(v1::LineInfoFlags::OUTPUT | v1::LineInfoFlags::OPEN_DRAIN),
345 Ok(Drive::OpenDrain)
346 );
347 assert_eq!(
348 Drive::try_from(v1::LineInfoFlags::OUTPUT | v1::LineInfoFlags::OPEN_SOURCE),
349 Ok(Drive::OpenSource)
350 );
351 }
352
353 #[test]
354 #[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
355 fn try_from_v2_line_flags() {
356 assert_eq!(Drive::try_from(v2::LineFlags::INPUT), Err(()));
357 assert_eq!(Drive::try_from(v2::LineFlags::OUTPUT), Ok(Drive::PushPull));
358 assert_eq!(
359 Drive::try_from(v2::LineFlags::OUTPUT | v2::LineFlags::OPEN_DRAIN),
360 Ok(Drive::OpenDrain)
361 );
362 assert_eq!(
363 Drive::try_from(v2::LineFlags::OUTPUT | v2::LineFlags::OPEN_SOURCE),
364 Ok(Drive::OpenSource)
365 );
366 }
367 }
368
369 #[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
370 mod edge_detection {
371 use super::{v2, EdgeDetection};
372
373 #[test]
374 fn try_from_v2_line_flags() {
375 assert_eq!(EdgeDetection::try_from(v2::LineFlags::INPUT), Err(()));
376 assert_eq!(
377 EdgeDetection::try_from(v2::LineFlags::EDGE_RISING),
378 Ok(EdgeDetection::RisingEdge)
379 );
380 assert_eq!(
381 EdgeDetection::try_from(v2::LineFlags::EDGE_FALLING),
382 Ok(EdgeDetection::FallingEdge)
383 );
384 assert_eq!(
385 EdgeDetection::try_from(v2::LineFlags::EDGE_RISING | v2::LineFlags::EDGE_FALLING),
386 Ok(EdgeDetection::BothEdges)
387 );
388 }
389 }
390
391 #[cfg(any(feature = "uapi_v2", not(feature = "uapi_v1")))]
392 mod event_clock {
393 use super::{v2, EventClock};
394
395 #[test]
396 fn default() {
397 assert_eq!(EventClock::default(), EventClock::Monotonic);
398 }
399
400 #[test]
401 fn from_v2_line_flags() {
402 assert_eq!(
403 EventClock::from(v2::LineFlags::INPUT),
404 EventClock::Monotonic
405 );
406 assert_eq!(
407 EventClock::from(v2::LineFlags::EVENT_CLOCK_REALTIME),
408 EventClock::Realtime
409 );
410 }
411 }
412
413 mod offset_hasher {
414 use super::OffsetHasher;
415 use std::hash::Hasher;
416
417 #[test]
418 fn write_u32() {
419 let mut h = OffsetHasher::default();
420 h.write_u32(2042);
421 assert_eq!(2042, h.finish());
422 }
423
424 #[test]
425 #[should_panic]
426 fn write() {
427 let mut h = OffsetHasher::default();
428 h.write(&[42]);
429 }
430
431 #[test]
432 #[should_panic]
433 fn write_u64() {
434 let mut h = OffsetHasher::default();
435 h.write_u64(2042);
436 }
437
438 #[test]
439 #[should_panic]
440 fn write_u8() {
441 let mut h = OffsetHasher::default();
442 h.write_u8(42);
443 }
444 }
445}