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}