gpiocdev_uapi/v1.rs
1// SPDX-FileCopyrightText: 2021 Kent Gibson <warthog618@gmail.com>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5use bitflags::bitflags;
6use std::fs::File;
7use std::os::unix::prelude::{AsRawFd, FromRawFd};
8
9// common to ABI v1 and v2.
10pub use super::common::*;
11
12#[repr(u8)]
13enum Ioctl {
14 GetLineInfo = 2,
15 GetLineHandle = 3,
16 GetLineEvent = 4,
17 GetLineValues = 8,
18 SetLineValues = 9,
19 SetConfig = 0xA,
20 WatchLineInfo = 0xB,
21}
22
23/// Information about a certain GPIO line.
24#[repr(C)]
25#[derive(Clone, Debug, Default, Eq, PartialEq)]
26pub struct LineInfo {
27 /// The line offset on this GPIO device.
28 /// This is the identifier used when requesting the line from the kernel.
29 pub offset: Offset,
30
31 /// The configuration flags for this line.
32 pub flags: LineInfoFlags,
33
34 /// The name of this GPIO line, such as the output pin of the line on the
35 /// chip, a rail or a pin header name on a board, as specified by the GPIO
36 /// chip.
37 ///
38 /// May be empty.
39 pub name: Name,
40
41 /// A functional name for the consumer of this GPIO line as set by
42 /// whatever is using it.
43 ///
44 /// Will be empty if there is no current user but may
45 /// also be empty if the consumer doesn't set a consumer name.
46 pub consumer: Name,
47}
48
49bitflags! {
50 /// Flags indicating the configuration of a line.
51 #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
52 pub struct LineInfoFlags: u32 {
53 /// The line is in use and is not available for request.
54 const USED = 1;
55
56 /// The line is an output.
57 const OUTPUT = 2;
58
59 /// The line active state corresponds to a physical low.
60 const ACTIVE_LOW = 4;
61
62 /// The line is an open drain output.
63 const OPEN_DRAIN = 8;
64
65 /// The line is an open source output.
66 const OPEN_SOURCE = 16;
67
68 /// The line has pull-up bias enabled.
69 const BIAS_PULL_UP = 32;
70
71 /// The line has pull-down bias enabled.
72 const BIAS_PULL_DOWN = 64;
73
74 /// The line has bias disabled.
75 const BIAS_DISABLED = 128;
76 }
77}
78
79/// Get the publicly available information for a line.
80///
81/// This does not include the line value.
82/// The line must be requested to access the value.
83///
84/// * 'cf' - The open gpiochip device file.
85/// * `offset` - The offset of the line.
86#[inline]
87pub fn get_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
88 let mut li = LineInfo {
89 offset,
90 ..Default::default()
91 };
92 // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
93 match unsafe { libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::GetLineInfo, LineInfo), &mut li) } {
94 0 => Ok(li),
95 _ => Err(Error::from_errno()),
96 }
97}
98
99/// Add a watch on changes to the [`LineInfo`] for a line.
100///
101/// Returns the current state of that information.
102/// This does not include the line value.
103/// The line must be requested to access the value.
104///
105/// * 'cf' - The open gpiochip device file.
106/// * `offset` - The offset of the line to watch.
107#[inline]
108pub fn watch_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
109 let mut li = LineInfo {
110 offset,
111 ..Default::default()
112 };
113 // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
114 match unsafe {
115 libc::ioctl(
116 cf.as_raw_fd(),
117 iorw!(Ioctl::WatchLineInfo, LineInfo),
118 &mut li,
119 )
120 } {
121 0 => Ok(li),
122 _ => Err(Error::from_errno()),
123 }
124}
125
126/// Information about a change in status of a GPIO line.
127#[repr(C)]
128#[derive(Clone, Debug, Eq, PartialEq)]
129pub struct LineInfoChangeEvent {
130 /// Updated line information.
131 pub info: LineInfo,
132
133 /// An estimate of time of status change occurrence, in nanoseconds.
134 pub timestamp_ns: u64,
135
136 /// The kind of change event (LineInfoChangeKind).
137 pub kind: u32,
138
139 /// Reserved for future use.
140 #[doc(hidden)]
141 pub padding: Padding<5>,
142}
143
144impl LineInfoChangeEvent {
145 /// Read a LineInfoChangeEvent from a buffer.
146 ///
147 /// The buffer is assumed to have been populated by a read of the chip File,
148 /// so the content is initialised.
149 pub fn from_slice(d: &[u64]) -> Result<&LineInfoChangeEvent> {
150 debug_assert!(std::mem::size_of::<LineInfoChangeEvent>() % 8 == 0);
151 let len = d.len() * 8;
152 if len < std::mem::size_of::<LineInfoChangeEvent>() {
153 return Err(Error::from(UnderReadError::new(
154 "LineInfoChangeEvent",
155 std::mem::size_of::<LineInfoChangeEvent>(),
156 len,
157 )));
158 }
159 // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
160 let le = unsafe { &*(d as *const [u64] as *const LineInfoChangeEvent) };
161 Ok(le)
162 }
163
164 /// The number of u64 words required to store a LineInfoChangeEvent.
165 pub fn u64_size() -> usize {
166 std::mem::size_of::<LineInfoChangeEvent>() / 8
167 }
168}
169
170/// Information about a GPIO line handle request.
171#[repr(C)]
172#[derive(Clone, Debug, Default, Eq, PartialEq)]
173pub struct HandleRequest {
174 /// An array of requested lines, identified by offset on the associated GPIO device.
175 pub offsets: Offsets,
176
177 /// The requested flags for the requested GPIO lines.
178 ///
179 /// Note that even if multiple lines are requested, the same flags must be applicable
180 /// to all of them, if you want lines with individual flags set, request them one by one.
181 /// It is possible to select a batch of input or output lines, but they must all
182 /// have the same characteristics, i.e. all inputs or all outputs, all active low etc.
183 pub flags: HandleRequestFlags,
184
185 /// If the [`HandleRequestFlags::OUTPUT`] is set for a requested line, this specifies the
186 /// output value for each offset. Should be 0 (*inactive*) or 1 (*active*).
187 /// Anything other than 0 or 1 is interpreted as 1 (*active*).
188 pub values: LineValues,
189
190 /// A requested consumer label for the selected GPIO line(s) such as "*my-bitbanged-relay*".
191 pub consumer: Name,
192
193 /// The number of lines requested in this request, i.e. the number of valid fields in
194 /// the `offsets` and `values` arrays.
195 ///
196 /// Set to 1 to request a single line.
197 pub num_lines: u32,
198
199 /// This field is only present for the underlying ioctl call and is only used internally.
200 //
201 // This is actually specified as an int in gpio.h, but that presents problems
202 // as it is not fixed width. It is usually i32, so that is what we go with here,
203 // but that may cause issues on platforms.
204 #[doc(hidden)]
205 pub fd: i32,
206}
207
208bitflags! {
209 /// Configuration flags for requested lines.
210 ///
211 /// Note that several of the flags, such as BIAS_PULL_UP and BIAS_PULL_DOWN are mutually
212 /// exclusive. The kernel will reject requests with flag combinations that do not make
213 /// sense.
214 #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
215 pub struct HandleRequestFlags: u32 {
216 /// Requests line as an input.
217 const INPUT = 1;
218
219 /// Requests line as an output.
220 const OUTPUT = 2;
221
222 /// Requests line as active low.
223 const ACTIVE_LOW = 4;
224
225 /// Requests line as open drain.
226 const OPEN_DRAIN = 8;
227
228 /// Requests line as open source.
229 const OPEN_SOURCE = 16;
230
231 /// Requests line with pull-up bias.
232 const BIAS_PULL_UP = 32;
233
234 /// Requests line with pull-down bias.
235 const BIAS_PULL_DOWN = 64;
236
237 /// Requests line with bias disabled.
238 const BIAS_DISABLED = 128;
239 }
240}
241
242/// Request a line or set of lines for exclusive access.
243///
244/// * 'cf' - The open gpiochip device file.
245/// * `hr` - The line handle request.
246#[inline]
247pub fn get_line_handle(cf: &File, mut hr: HandleRequest) -> Result<File> {
248 // SAFETY: hr is consumed and the returned file is drawn from the returned fd.
249 unsafe {
250 match libc::ioctl(
251 cf.as_raw_fd(),
252 iorw!(Ioctl::GetLineHandle, HandleRequest),
253 &mut hr,
254 ) {
255 0 => Ok(File::from_raw_fd(hr.fd)),
256 _ => Err(Error::from_errno()),
257 }
258 }
259}
260
261/// Updated configuration for an existing GPIO handle request.
262#[repr(C)]
263#[derive(Clone, Debug, Default, Eq, PartialEq)]
264pub struct HandleConfig {
265 /// Updated flags for the requested GPIO lines.
266 ///
267 /// The flags will be applied to all lines in the existing request.
268 pub flags: HandleRequestFlags,
269
270 /// If the [`HandleRequestFlags::OUTPUT`] is set in flags, this specifies the
271 /// output value, should be 0 (*inactive*) or 1 (*active*).
272 ///
273 /// All other values are interpreted as active.
274 pub values: LineValues,
275
276 /// Reserved for future use and should be zero filled.
277 #[doc(hidden)]
278 pub padding: Padding<4>,
279}
280
281/// Update the configuration of an existing handle or event request.
282///
283/// * `lf` - The request file returned by [`get_line_handle`].
284/// * `hc` - The configuration to be applied.
285#[inline]
286pub fn set_line_config(lf: &File, hc: HandleConfig) -> Result<()> {
287 // SAFETY: hc is consumed.
288 unsafe {
289 match libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::SetConfig, HandleConfig), &hc) {
290 0 => Ok(()),
291 _ => Err(Error::from_errno()),
292 }
293 }
294}
295
296/// The logical values of the requested lines.
297///
298/// Values are stored as u8, as that is what the uAPI specifies.
299///
300/// 0 is *inactive* with 1 and all other values taken as *active*.
301///
302/// Values are stored in the same order as the offsets in the [`HandleRequest.offsets`].
303///
304/// Values for input lines are ignored.
305///
306/// [`HandleRequest.offsets`]: struct@HandleRequest
307#[repr(C)]
308#[derive(Clone, Copy, Debug, Eq, PartialEq)]
309pub struct LineValues(pub [u8; 64usize]);
310
311impl LineValues {
312 /// Create values from a slice.
313 ///
314 /// The values are in the same order as [`HandleRequest.offsets`].
315 ///
316 /// [`HandleRequest.offsets`]: struct@HandleRequest
317 pub fn from_slice(s: &[u8]) -> Self {
318 let mut n: LineValues = Default::default();
319 for (src, dst) in s.iter().zip(n.0.iter_mut()) {
320 *dst = *src;
321 }
322 n
323 }
324
325 /// Copy values from an iterable list - in order of requested offsets.
326 pub fn copy_from_slice(&mut self, s: &[u8]) {
327 let extent = std::cmp::min(64usize, s.len());
328 self.0[0..extent].copy_from_slice(s);
329 }
330
331 /// Return the value of a line.
332 ///
333 /// Note that the [`LineValues`] need to be populated via a call to [`get_line_values`]
334 /// to get values from the underlying hardware.
335 ///
336 /// * `idx` - The index into the [`HandleRequest.offsets`] for the line of interest.
337 ///
338 /// [`HandleRequest.offsets`]: struct@HandleRequest
339 #[inline]
340 pub fn get(&self, idx: usize) -> u8 {
341 self.0[idx]
342 }
343
344 /// Set the value of a line.
345 ///
346 /// Note that this is not applied to hardware until these values are passed to
347 /// [`set_line_values`].
348 ///
349 /// * `idx` - The index into the [`HandleRequest.offsets`] for the line of interest.
350 /// * `value` - The logical state of the line to be set.
351 ///
352 /// [`HandleRequest.offsets`]: struct@HandleRequest
353 #[inline]
354 pub fn set(&mut self, idx: usize, value: u8) {
355 self.0[idx] = value;
356 }
357}
358impl Default for LineValues {
359 fn default() -> Self {
360 LineValues([0; 64])
361 }
362}
363
364/// Read the values of requested lines.
365///
366/// * `lf` - The request file returned by [`get_line_handle`] or [`get_line_event`].
367/// * `vals` - The line values to be populated.
368#[inline]
369pub fn get_line_values(lf: &File, vals: &mut LineValues) -> Result<()> {
370 // SAFETY: vals are raw integers that are safe to decode.
371 match unsafe {
372 libc::ioctl(
373 lf.as_raw_fd(),
374 iorw!(Ioctl::GetLineValues, LineValues),
375 vals.0.as_mut_ptr(),
376 )
377 } {
378 0 => Ok(()),
379 _ => Err(Error::from_errno()),
380 }
381}
382
383/// Set the values of requested lines.
384///
385/// * `lf` - The request file returned by [`get_line_handle`].
386/// * `vals` - The line values to be set.
387#[inline]
388pub fn set_line_values(lf: &File, vals: &LineValues) -> Result<()> {
389 // SAFETY: vals is not modified.
390 match unsafe {
391 libc::ioctl(
392 lf.as_raw_fd(),
393 iorw!(Ioctl::SetLineValues, LineValues),
394 vals.0.as_ptr(),
395 )
396 } {
397 0 => Ok(()),
398 _ => Err(Error::from_errno()),
399 }
400}
401
402/// Information about a GPIO event request.
403#[repr(C)]
404#[derive(Clone, Debug, Default, Eq, PartialEq)]
405pub struct EventRequest {
406 /// The line to request edge events from, identified by its offset
407 /// on the associated GPIO device.
408 pub offset: Offset,
409
410 /// The requested handle flags for the GPIO line.
411 pub handleflags: HandleRequestFlags,
412
413 /// The requested event flags for the GPIO line.
414 pub eventflags: EventRequestFlags,
415
416 /// A requested consumer label for the selected GPIO line(s) such as "*my-listener*".
417 pub consumer: Name,
418
419 /// This field is only present for the underlying ioctl call and is only used internally.
420 ///
421 // This is actually specified as an int in gpio.h, but that presents problems
422 // as it is not fixed width. It is usually i32, so that is what we go with here,
423 // though this may cause issues on platforms with a differently sized int.
424 #[doc(hidden)]
425 pub fd: i32,
426}
427
428bitflags! {
429 /// Additional configuration flags for event requests.
430 #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
431 pub struct EventRequestFlags: u32 {
432 /// Report rising edge events on the requested line.
433 const RISING_EDGE = 1;
434
435 /// Report falling edge events on the requested line.
436 const FALLING_EDGE = 2;
437
438 /// Report both rising and falling edge events on the requested line.
439 const BOTH_EDGES = Self::RISING_EDGE.bits() | Self::FALLING_EDGE.bits();
440 }
441}
442
443/// Request a line with edge detection enabled.
444///
445/// Detected events can be read from the returned file.
446///
447/// * 'cf' - The open gpiochip device file.
448/// * `er` - The line event request.
449#[inline]
450pub fn get_line_event(cf: &File, mut er: EventRequest) -> Result<File> {
451 // SAFETY: er is consumed and the returned file is drawn from the returned fd.
452 unsafe {
453 match libc::ioctl(
454 cf.as_raw_fd(),
455 iorw!(Ioctl::GetLineEvent, EventRequest),
456 &mut er,
457 ) {
458 0 => Ok(File::from_raw_fd(er.fd)),
459 _ => Err(Error::from_errno()),
460 }
461 }
462}
463
464/// Information about an edge event on a requested line.
465#[repr(C)]
466#[derive(Clone, Debug, Eq, PartialEq)]
467pub struct LineEdgeEvent {
468 /// The best estimate of time of event occurrence, in nanoseconds.
469 pub timestamp_ns: u64,
470 /// The kind of line event. (LineEdgeEventKind)
471 pub kind: u32,
472}
473
474impl LineEdgeEvent {
475 /// Read a LineEdgeEvent from a buffer.
476 ///
477 /// The buffer is assumed to have been populated by a read of the line request File,
478 /// so the content is initialised.
479 pub fn from_slice(d: &[u64]) -> Result<&LineEdgeEvent> {
480 debug_assert!(std::mem::size_of::<LineEdgeEvent>() % 8 == 0);
481 let len = d.len() * 8;
482 if len < std::mem::size_of::<LineEdgeEvent>() {
483 return Err(Error::from(UnderReadError::new(
484 "LineEdgeEvent",
485 std::mem::size_of::<LineEdgeEvent>(),
486 len,
487 )));
488 }
489 // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
490 let le = unsafe { &*(d as *const [u64] as *const LineEdgeEvent) };
491 Ok(le)
492 }
493
494 /// The number of u64 words required to store a LineEdgeEvent.
495 pub fn u64_size() -> usize {
496 std::mem::size_of::<LineEdgeEvent>() / 8
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503
504 mod line_info {
505 use super::LineInfo;
506
507 #[test]
508 fn size() {
509 assert_eq!(
510 std::mem::size_of::<LineInfo>(),
511 72usize,
512 concat!("Size of: ", stringify!(LineInfo))
513 );
514 }
515 }
516
517 mod line_info_changed {
518 use super::LineInfoChangeEvent;
519
520 #[test]
521 fn size() {
522 assert_eq!(
523 std::mem::size_of::<LineInfoChangeEvent>(),
524 104usize,
525 concat!("Size of: ", stringify!(LineInfoChangeEvent))
526 );
527 }
528 }
529
530 mod handle_request {
531 use super::HandleRequest;
532
533 #[test]
534 fn size() {
535 assert_eq!(
536 std::mem::size_of::<HandleRequest>(),
537 364usize,
538 concat!("Size of: ", stringify!(HandleRequest))
539 );
540 }
541 }
542
543 mod handle_config {
544 use super::HandleConfig;
545
546 #[test]
547 fn size() {
548 assert_eq!(
549 std::mem::size_of::<HandleConfig>(),
550 84usize,
551 concat!("Size of: ", stringify!(HandleConfig))
552 );
553 }
554 }
555
556 mod event_request {
557 use super::EventRequest;
558
559 #[test]
560 fn size() {
561 assert_eq!(
562 std::mem::size_of::<EventRequest>(),
563 48usize,
564 concat!("Size of: ", stringify!(EventRequest))
565 );
566 }
567 }
568
569 mod line_event {
570 use super::LineEdgeEvent;
571
572 #[test]
573 fn size() {
574 assert_eq!(
575 std::mem::size_of::<LineEdgeEvent>(),
576 16usize,
577 concat!("Size of: ", stringify!(LineEdgeEvent))
578 );
579 }
580 }
581
582 mod line_values {
583 use super::LineValues;
584
585 #[test]
586 fn get() {
587 let mut a = LineValues::default();
588 for idx in [0, 2] {
589 assert_eq!(a.0[idx], 0, "idx: {idx}");
590 assert_eq!(a.get(idx), 0, "idx: {idx}");
591 a.0[idx] = 1;
592 assert_eq!(a.get(idx), 1, "idx: {idx}");
593 a.0[idx] = 42;
594 assert_eq!(a.get(idx), 42, "idx: {idx}");
595 }
596 }
597
598 #[test]
599 fn set() {
600 let mut a = LineValues::default();
601 for idx in [0, 2] {
602 a.set(idx, 0);
603 assert_eq!(a.0[idx], 0, "idx: {idx}");
604 a.set(idx, 1);
605 assert_eq!(a.0[idx], 1, "idx: {idx}");
606 a.set(idx, 42);
607 assert_eq!(a.0[idx], 42, "idx: {idx}");
608 }
609 }
610
611 #[test]
612 fn size() {
613 assert_eq!(
614 std::mem::size_of::<LineValues>(),
615 64usize,
616 concat!("Size of: ", stringify!(LineValues))
617 );
618 }
619 }
620}