libgpiod/
chip.rs

1// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2022 Linaro Ltd.
3// SPDX-FileCopyrightText: 2022 Viresh Kumar <viresh.kumar@linaro.org>
4
5pub mod info {
6    /// GPIO chip info event related definitions.
7    pub use crate::info_event::*;
8}
9
10use std::cmp::Ordering;
11use std::ffi::{CStr, CString};
12use std::os::{raw::c_char, unix::prelude::AsRawFd};
13use std::path::Path;
14use std::ptr;
15use std::str;
16use std::time::Duration;
17
18use super::{
19    Error, OperationType, Result, gpiod,
20    line::{self, Offset},
21    request,
22};
23
24/// GPIO chip
25///
26/// A GPIO chip object is associated with an open file descriptor to the GPIO
27/// character device. It exposes basic information about the chip and allows
28/// callers to retrieve information about each line, watch lines for state
29/// changes and make line requests.
30#[derive(Debug, Eq, PartialEq)]
31pub struct Chip {
32    chip: *mut gpiod::gpiod_chip,
33}
34
35// SAFETY: Safe as chip is modeling a owned chip instance that may be freely
36// moved to other threads
37unsafe impl Send for Chip {}
38
39impl Chip {
40    /// Find a chip by path.
41    pub fn open<P: AsRef<Path>>(path: &P) -> Result<Self> {
42        // Null-terminate the string
43        let path = path.as_ref().to_string_lossy() + "\0";
44
45        // SAFETY: The `gpiod_chip` returned by libgpiod is guaranteed to live as long
46        // as the `struct Internal`.
47        let chip = unsafe { gpiod::gpiod_chip_open(path.as_ptr() as *const c_char) };
48        if chip.is_null() {
49            return Err(Error::OperationFailed(
50                OperationType::ChipOpen,
51                errno::errno(),
52            ));
53        }
54
55        Ok(Self { chip })
56    }
57
58    /// Get the chip name as represented in the kernel.
59    pub fn info(&self) -> Result<Info> {
60        Info::new(self)
61    }
62
63    /// Get the path used to find the chip.
64    pub fn path(&self) -> Result<&str> {
65        // SAFETY: The string returned by libgpiod is guaranteed to live as long
66        // as the `struct Chip`.
67        let path = unsafe { gpiod::gpiod_chip_get_path(self.chip) };
68
69        // SAFETY: The string is guaranteed to be valid here by the C API.
70        unsafe { CStr::from_ptr(path) }
71            .to_str()
72            .map_err(Error::StringNotUtf8)
73    }
74
75    /// Get a snapshot of information about the line.
76    pub fn line_info(&self, offset: Offset) -> Result<line::Info> {
77        // SAFETY: The `gpiod_line_info` returned by libgpiod is guaranteed to live as long
78        // as the `struct Info`.
79        let info = unsafe { gpiod::gpiod_chip_get_line_info(self.chip, offset) };
80
81        if info.is_null() {
82            return Err(Error::OperationFailed(
83                OperationType::ChipGetLineInfo,
84                errno::errno(),
85            ));
86        }
87
88        // SAFETY: We verified that the pointer is valid. We own the pointer and
89        // no longer use it after converting it into a Info instance.
90        Ok(unsafe { line::Info::from_raw(info) })
91    }
92
93    /// Get the current snapshot of information about the line at given offset and start watching
94    /// it for future changes.
95    pub fn watch_line_info(&self, offset: Offset) -> Result<line::Info> {
96        // SAFETY: `gpiod_line_info` is guaranteed to be valid here.
97        let info = unsafe { gpiod::gpiod_chip_watch_line_info(self.chip, offset) };
98
99        if info.is_null() {
100            return Err(Error::OperationFailed(
101                OperationType::ChipWatchLineInfo,
102                errno::errno(),
103            ));
104        }
105
106        // SAFETY: We verified that the pointer is valid. We own the instance and
107        // no longer use it after converting it into a Info instance.
108        Ok(unsafe { line::Info::from_raw(info) })
109    }
110
111    /// Stop watching a line
112    pub fn unwatch(&self, offset: Offset) {
113        // SAFETY: `gpiod_chip` is guaranteed to be valid here.
114        unsafe {
115            gpiod::gpiod_chip_unwatch_line_info(self.chip, offset);
116        }
117    }
118
119    /// Wait for line status events on any of the watched lines on the chip.
120    pub fn wait_info_event(&self, timeout: Option<Duration>) -> Result<bool> {
121        let timeout = match timeout {
122            Some(x) => x.as_nanos() as i64,
123            // Block indefinitely
124            None => -1,
125        };
126
127        // SAFETY: `gpiod_chip` is guaranteed to be valid here.
128        let ret = unsafe { gpiod::gpiod_chip_wait_info_event(self.chip, timeout) };
129
130        match ret {
131            -1 => Err(Error::OperationFailed(
132                OperationType::ChipWaitInfoEvent,
133                errno::errno(),
134            )),
135            0 => Ok(false),
136            _ => Ok(true),
137        }
138    }
139
140    /// Read a single line status change event from the chip. If no events are
141    /// pending, this function will block.
142    pub fn read_info_event(&self) -> Result<info::Event> {
143        // SAFETY: The `gpiod_info_event` returned by libgpiod is guaranteed to live as long
144        // as the `struct Event`.
145        let event = unsafe { gpiod::gpiod_chip_read_info_event(self.chip) };
146        if event.is_null() {
147            return Err(Error::OperationFailed(
148                OperationType::ChipReadInfoEvent,
149                errno::errno(),
150            ));
151        }
152
153        // SAFETY: `gpiod_chip_read_info_event` returned a standalone `event`
154        // over which we have sole ownership. We won't use the raw pointer
155        // directly after passing it here.
156        Ok(unsafe { info::Event::from_raw(event) })
157    }
158
159    /// Map a GPIO line's name to its offset within the chip.
160    pub fn line_offset_from_name(&self, name: &str) -> Result<Offset> {
161        let name = CString::new(name).map_err(|_| Error::InvalidString)?;
162
163        // SAFETY: `gpiod_chip` is guaranteed to be valid here.
164        let ret = unsafe {
165            gpiod::gpiod_chip_get_line_offset_from_name(self.chip, name.as_ptr() as *const c_char)
166        };
167
168        if ret == -1 {
169            Err(Error::OperationFailed(
170                OperationType::ChipGetLineOffsetFromName,
171                errno::errno(),
172            ))
173        } else {
174            Ok(ret as u32)
175        }
176    }
177
178    /// Request a set of lines for exclusive usage.
179    pub fn request_lines(
180        &self,
181        rconfig: Option<&request::Config>,
182        lconfig: &line::Config,
183    ) -> Result<request::Request> {
184        let req_cfg = match rconfig {
185            Some(cfg) => cfg.config,
186            _ => ptr::null(),
187        } as *mut gpiod::gpiod_request_config;
188
189        // SAFETY: The `gpiod_line_request` returned by libgpiod is guaranteed to live as long
190        // as the `struct Request`.
191        let request =
192            unsafe { gpiod::gpiod_chip_request_lines(self.chip, req_cfg, lconfig.config) };
193
194        if request.is_null() {
195            return Err(Error::OperationFailed(
196                OperationType::ChipRequestLines,
197                errno::errno(),
198            ));
199        }
200
201        // SAFETY: `gpiod_chip_request_lines` returned an object over which we
202        // have sole ownership. We never use it again after constructing the
203        // wrapper.
204        unsafe { request::Request::from_raw(request) }
205    }
206}
207
208impl Drop for Chip {
209    /// Close the chip and release all associated resources.
210    fn drop(&mut self) {
211        // SAFETY: `gpiod_chip` is guaranteed to be valid here.
212        unsafe { gpiod::gpiod_chip_close(self.chip) }
213    }
214}
215
216impl AsRawFd for Chip {
217    /// Get the file descriptor associated with the chip.
218    ///
219    /// The returned file descriptor must not be closed by the caller, else other methods for the
220    /// `struct Chip` may fail.
221    fn as_raw_fd(&self) -> i32 {
222        // SAFETY: `gpiod_chip` is guaranteed to be valid here.
223        unsafe { gpiod::gpiod_chip_get_fd(self.chip) }
224    }
225}
226
227/// GPIO chip Information
228#[derive(Debug, Eq)]
229pub struct Info {
230    info: *mut gpiod::gpiod_chip_info,
231}
232
233impl Info {
234    /// Find a GPIO chip by path.
235    fn new(chip: &Chip) -> Result<Self> {
236        // SAFETY: `chip.chip` is guaranteed to be valid here.
237        let info = unsafe { gpiod::gpiod_chip_get_info(chip.chip) };
238        if info.is_null() {
239            return Err(Error::OperationFailed(
240                OperationType::ChipGetInfo,
241                errno::errno(),
242            ));
243        }
244
245        Ok(Self { info })
246    }
247
248    /// Get the GPIO chip name as represented in the kernel.
249    pub fn name(&self) -> Result<&str> {
250        // SAFETY: The string returned by libgpiod is guaranteed to live as long
251        // as the `struct Chip`.
252        let name = unsafe { gpiod::gpiod_chip_info_get_name(self.info) };
253
254        // SAFETY: The string is guaranteed to be valid here by the C API.
255        unsafe { CStr::from_ptr(name) }
256            .to_str()
257            .map_err(Error::StringNotUtf8)
258    }
259
260    /// Get the GPIO chip label as represented in the kernel.
261    pub fn label(&self) -> Result<&str> {
262        // SAFETY: The string returned by libgpiod is guaranteed to live as long
263        // as the `struct Chip`.
264        let label = unsafe { gpiod::gpiod_chip_info_get_label(self.info) };
265
266        // SAFETY: The string is guaranteed to be valid here by the C API.
267        unsafe { CStr::from_ptr(label) }
268            .to_str()
269            .map_err(Error::StringNotUtf8)
270    }
271
272    /// Get the number of GPIO lines exposed by the chip.
273    pub fn num_lines(&self) -> usize {
274        // SAFETY: `gpiod_chip` is guaranteed to be valid here.
275        unsafe { gpiod::gpiod_chip_info_get_num_lines(self.info) }
276    }
277}
278
279impl PartialEq for Info {
280    fn eq(&self, other: &Self) -> bool {
281        self.name().unwrap().eq(other.name().unwrap())
282    }
283}
284
285impl PartialOrd for Info {
286    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
287        let name = match self.name() {
288            Ok(name) => name,
289            _ => return None,
290        };
291
292        let other_name = match other.name() {
293            Ok(name) => name,
294            _ => return None,
295        };
296
297        name.partial_cmp(other_name)
298    }
299}
300
301impl Drop for Info {
302    /// Close the GPIO chip info and release all associated resources.
303    fn drop(&mut self) {
304        // SAFETY: `gpiod_chip` is guaranteed to be valid here.
305        unsafe { gpiod::gpiod_chip_info_free(self.info) }
306    }
307}