gpiod_core/
types.rs

1use crate::{utils::*, Error, Result, Time, MAX_BITS};
2use std::{fmt, str};
3
4/// Line offset
5pub type LineId = u32;
6
7/// Bit offset
8pub type BitId = u8;
9
10/// Line offset to bit offset mapping
11#[derive(Debug, Clone)]
12pub struct LineMap {
13    map: Vec<BitId>,
14}
15
16impl LineMap {
17    const NOT_LINE: BitId = MAX_BITS;
18
19    /// Create line map
20    pub fn new(lines: &[LineId]) -> Self {
21        let mut map: Vec<BitId> = (0..=lines.iter().max().copied().unwrap_or(0))
22            .map(|_| Self::NOT_LINE)
23            .collect();
24        for i in 0..lines.len() {
25            map[lines[i] as usize] = i as _;
26        }
27        Self { map }
28    }
29
30    /// Get bit position by line offset
31    pub fn get(&self, line: LineId) -> Result<BitId> {
32        let line = line as usize;
33        if line < self.map.len() {
34            let val = self.map[line];
35            if val != Self::NOT_LINE {
36                return Ok(val as _);
37            }
38        }
39        Err(invalid_data("Unknown line offset"))
40    }
41}
42
43/// The information of a specific GPIO line
44#[derive(Debug, Clone)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub struct LineInfo {
47    /// GPIO line direction
48    pub direction: Direction,
49
50    /// GPIO line active state
51    pub active: Active,
52
53    /// GPIO line edge detection
54    pub edge: EdgeDetect,
55
56    /// GPIO line usage status
57    ///
58    /// `true` means that kernel uses this line for some purposes.
59    pub used: bool,
60
61    /// GPIO line input bias
62    pub bias: Bias,
63
64    /// GPIO line output drive mode
65    pub drive: Drive,
66
67    /// GPIO line name
68    pub name: String,
69
70    /// GPIO line consumer name
71    pub consumer: String,
72}
73
74impl fmt::Display for LineInfo {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        if self.name.is_empty() {
77            write!(f, "\t unnamed")?;
78        } else {
79            write!(f, "\t {:?}", self.name)?;
80        }
81        if self.consumer.is_empty() {
82            write!(f, "\t unused")?;
83        } else {
84            write!(f, "\t {:?}", self.consumer)?;
85        }
86        write!(f, "\t {}", self.direction)?;
87        write!(f, "\t active-{}", self.active)?;
88        if !matches!(self.edge, EdgeDetect::Disable) {
89            write!(f, "\t {}-edge", self.edge)?;
90        }
91        if !matches!(self.bias, Bias::Disable) {
92            write!(f, "\t {}", self.edge)?;
93        }
94        if !matches!(self.drive, Drive::PushPull) {
95            write!(f, "\t {}", self.drive)?;
96        }
97        if self.used {
98            write!(f, "\t [used]")?;
99        }
100        Ok(())
101    }
102}
103
104/// Direction of a GPIO line
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
108#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
109#[repr(u8)]
110pub enum Direction {
111    /// Line acts as input (default)
112    #[cfg_attr(feature = "clap", clap(aliases = ["i", "in"]))]
113    Input,
114    /// Line acts as output
115    #[cfg_attr(feature = "clap", clap(aliases = ["o", "out"]))]
116    Output,
117}
118
119impl Default for Direction {
120    fn default() -> Self {
121        Self::Input
122    }
123}
124
125impl AsRef<str> for Direction {
126    fn as_ref(&self) -> &str {
127        match self {
128            Self::Input => "input",
129            Self::Output => "output",
130        }
131    }
132}
133
134impl fmt::Display for Direction {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        self.as_ref().fmt(f)
137    }
138}
139
140impl str::FromStr for Direction {
141    type Err = Error;
142
143    fn from_str(s: &str) -> Result<Self> {
144        Ok(match s {
145            "i" | "in" | "input" => Self::Input,
146            "o" | "out" | "output" => Self::Output,
147            _ => return Err(invalid_input("Not recognized direction")),
148        })
149    }
150}
151
152/// Active state condition of a line
153///
154/// If active state of line is **high** then physical and logical levels is same.
155/// Otherwise if it is **low** then physical level will be inverted from logical.
156///
157/// Also this may be treated as polarity.
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
159#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
160#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
161#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
162#[repr(u8)]
163pub enum Active {
164    /// Active level is low
165    #[cfg_attr(feature = "clap", clap(aliases = ["l", "lo"]))]
166    Low,
167    /// Active level is high (default)
168    #[cfg_attr(feature = "clap", clap(aliases = ["h", "hi"]))]
169    High,
170}
171
172impl Default for Active {
173    fn default() -> Self {
174        Self::High
175    }
176}
177
178impl AsRef<str> for Active {
179    fn as_ref(&self) -> &str {
180        match self {
181            Self::Low => "low",
182            Self::High => "high",
183        }
184    }
185}
186
187impl fmt::Display for Active {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        self.as_ref().fmt(f)
190    }
191}
192
193impl str::FromStr for Active {
194    type Err = Error;
195
196    fn from_str(s: &str) -> Result<Self> {
197        Ok(match s {
198            "l" | "lo" | "low" | "active-low" => Self::Low,
199            "h" | "hi" | "high" | "active-high" => Self::High,
200            _ => return Err(invalid_input("Not recognized active state")),
201        })
202    }
203}
204
205/// Signal edge or level transition of a GPIO line
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
207#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
208#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
209#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
210#[repr(u8)]
211pub enum Edge {
212    /// Rising edge detected
213    #[cfg_attr(feature = "clap", clap(aliases = ["r", "rise"]))]
214    Rising,
215    /// Falling edge detected
216    #[cfg_attr(feature = "clap", clap(aliases = ["f", "fall"]))]
217    Falling,
218}
219
220impl AsRef<str> for Edge {
221    fn as_ref(&self) -> &str {
222        match self {
223            Self::Rising => "rising",
224            Self::Falling => "falling",
225        }
226    }
227}
228
229impl fmt::Display for Edge {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        self.as_ref().fmt(f)
232    }
233}
234
235impl str::FromStr for Edge {
236    type Err = Error;
237
238    fn from_str(s: &str) -> Result<Self> {
239        Ok(match s {
240            "r" | "rise" | "rising" => Self::Rising,
241            "f" | "fall" | "falling" => Self::Falling,
242            _ => return Err(invalid_input("Not recognized edge")),
243        })
244    }
245}
246
247/// Signal edge detection event
248#[derive(Debug, Clone, Copy)]
249#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
250pub struct Event {
251    /// GPIO line where edge detected
252    pub line: BitId,
253    /// Detected edge or level transition
254    pub edge: Edge,
255    /// Time when edge actually detected
256    pub time: Time,
257}
258
259impl fmt::Display for Event {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        '#'.fmt(f)?;
262        self.line.fmt(f)?;
263        ' '.fmt(f)?;
264        self.edge.fmt(f)?;
265        ' '.fmt(f)?;
266        self.time.as_nanos().fmt(f)
267    }
268}
269
270/// Edge detection setting for GPIO line
271#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
272#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
273#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
274#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
275#[repr(u8)]
276pub enum EdgeDetect {
277    /// Detection disabled (default)
278    #[cfg_attr(feature = "clap", clap(aliases = ["d", "dis"]))]
279    Disable,
280    /// Detect rising edge only
281    #[cfg_attr(feature = "clap", clap(aliases = ["r", "rise"]))]
282    Rising,
283    /// Detect falling edge only
284    #[cfg_attr(feature = "clap", clap(aliases = ["f", "fall"]))]
285    Falling,
286    /// Detect both rising and falling edges
287    #[cfg_attr(feature = "clap", clap(aliases = ["b"]))]
288    Both,
289}
290
291impl Default for EdgeDetect {
292    fn default() -> Self {
293        Self::Disable
294    }
295}
296
297impl AsRef<str> for EdgeDetect {
298    fn as_ref(&self) -> &str {
299        match self {
300            Self::Disable => "disable",
301            Self::Rising => "rising",
302            Self::Falling => "falling",
303            Self::Both => "both",
304        }
305    }
306}
307
308impl fmt::Display for EdgeDetect {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        self.as_ref().fmt(f)
311    }
312}
313
314impl str::FromStr for EdgeDetect {
315    type Err = Error;
316
317    fn from_str(s: &str) -> Result<Self> {
318        Ok(match s {
319            "d" | "dis" | "disable" => Self::Disable,
320            "r" | "rise" | "rising" => Self::Rising,
321            "f" | "fall" | "falling" => Self::Falling,
322            "b" | "both" | "rise-fall" | "rising-falling" => Self::Both,
323            _ => return Err(invalid_input("Not recognized edge-detect")),
324        })
325    }
326}
327
328/// Input bias of a GPIO line
329///
330/// Sometimes GPIO lines shall be pulled to up (power rail) or down (ground)
331/// through resistor to avoid floating level on it.
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
333#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
334#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
335#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
336#[repr(u8)]
337pub enum Bias {
338    /// Disabled bias (default)
339    #[cfg_attr(feature = "clap", clap(aliases = ["d", "dis"]))]
340    Disable,
341    /// Pull line up
342    #[cfg_attr(feature = "clap", clap(aliases = ["pu"]))]
343    PullUp,
344    /// Pull line down
345    #[cfg_attr(feature = "clap", clap(aliases = ["pd"]))]
346    PullDown,
347}
348
349impl Default for Bias {
350    fn default() -> Self {
351        Self::Disable
352    }
353}
354
355impl AsRef<str> for Bias {
356    fn as_ref(&self) -> &str {
357        match self {
358            Self::Disable => "disable",
359            Self::PullUp => "pull-up",
360            Self::PullDown => "pull-down",
361        }
362    }
363}
364
365impl fmt::Display for Bias {
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        self.as_ref().fmt(f)
368    }
369}
370
371impl str::FromStr for Bias {
372    type Err = Error;
373
374    fn from_str(s: &str) -> Result<Self> {
375        Ok(match s {
376            "d" | "dis" | "disable" => Self::Disable,
377            "pu" | "pull-up" => Self::PullUp,
378            "pd" | "pull-down" => Self::PullUp,
379            _ => return Err(invalid_input("Not recognized input bias")),
380        })
381    }
382}
383
384/// Output drive mode of a GPIO line
385///
386/// Usually GPIO lines configured as push-pull but sometimes it required to drive via open drain or source.
387#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
388#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
389#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
390#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
391#[repr(u8)]
392pub enum Drive {
393    /// Drive push-pull (default)
394    #[cfg_attr(feature = "clap", clap(aliases = ["pp"]))]
395    PushPull,
396    /// Drive with open-drain
397    #[cfg_attr(feature = "clap", clap(aliases = ["od"]))]
398    OpenDrain,
399    /// Drive with open-source
400    #[cfg_attr(feature = "clap", clap(aliases = ["os"]))]
401    OpenSource,
402}
403
404impl Default for Drive {
405    fn default() -> Self {
406        Self::PushPull
407    }
408}
409
410impl AsRef<str> for Drive {
411    fn as_ref(&self) -> &str {
412        match self {
413            Self::PushPull => "push-pull",
414            Self::OpenDrain => "open-drain",
415            Self::OpenSource => "open-source",
416        }
417    }
418}
419
420impl fmt::Display for Drive {
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        self.as_ref().fmt(f)
423    }
424}
425
426impl str::FromStr for Drive {
427    type Err = Error;
428
429    fn from_str(s: &str) -> Result<Self> {
430        Ok(match s {
431            "pp" | "push-pull" => Self::PushPull,
432            "od" | "open-drain" => Self::OpenDrain,
433            "os" | "open-source" => Self::OpenSource,
434            _ => return Err(invalid_input("Not recognized output drive")),
435        })
436    }
437}