libgpiod/
line_request.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
5#[cfg(feature = "v2_1")]
6use std::ffi::CStr;
7use std::os::unix::prelude::AsRawFd;
8use std::time::Duration;
9
10use super::{
11    Error, OperationType, Result, gpiod,
12    line::{self, Offset, Value, ValueMap},
13    request,
14};
15
16/// Line request operations
17///
18/// Allows interaction with a set of requested lines.
19#[derive(Debug, Eq, PartialEq)]
20pub struct Request {
21    pub(crate) request: *mut gpiod::gpiod_line_request,
22}
23
24// SAFETY: Request models a wrapper around an owned gpiod_line_request and may
25// be safely sent to other threads.
26unsafe impl Send for Request {}
27
28impl Request {
29    /// Request a set of lines for exclusive usage.
30    ///
31    /// SAFETY: The pointer must point to an instance that is valid. After
32    /// constructing a [Request] the pointer MUST NOT be used for any other
33    /// purpose anymore. All interactions with the libgpiod API have to happen
34    /// through this object.
35    pub(crate) unsafe fn from_raw(request: *mut gpiod::gpiod_line_request) -> Result<Self> {
36        Ok(Self { request })
37    }
38
39    /// Get the name of the chip this request was made on.
40    #[cfg(feature = "v2_1")]
41    pub fn chip_name(&self) -> Result<&str> {
42        // SAFETY: The `gpiod_line_request` is guaranteed to be live as long
43        // as `&self`
44        let name = unsafe { gpiod::gpiod_line_request_get_chip_name(self.request) };
45
46        // SAFETY: The string is guaranteed to be valid, non-null and immutable
47        // by the C API for the lifetime of the `gpiod_line_request`.
48        unsafe { CStr::from_ptr(name) }
49            .to_str()
50            .map_err(Error::StringNotUtf8)
51    }
52
53    /// Get the number of lines in the request.
54    pub fn num_lines(&self) -> usize {
55        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
56        unsafe { gpiod::gpiod_line_request_get_num_requested_lines(self.request) }
57    }
58
59    /// Get the offsets of lines in the request.
60    pub fn offsets(&self) -> Vec<Offset> {
61        let mut offsets = vec![0; self.num_lines()];
62
63        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
64        let num_offsets = unsafe {
65            gpiod::gpiod_line_request_get_requested_offsets(
66                self.request,
67                offsets.as_mut_ptr(),
68                self.num_lines(),
69            )
70        };
71        offsets.shrink_to(num_offsets);
72        offsets
73    }
74
75    /// Get the value (0 or 1) of a single line associated with the request.
76    pub fn value(&self, offset: Offset) -> Result<Value> {
77        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
78        let value = unsafe { gpiod::gpiod_line_request_get_value(self.request, offset) };
79
80        if value != 0 && value != 1 {
81            Err(Error::OperationFailed(
82                OperationType::LineRequestGetVal,
83                errno::errno(),
84            ))
85        } else {
86            Value::new(value)
87        }
88    }
89
90    /// Get values of a subset of lines associated with the request.
91    pub fn values_subset(&self, offsets: &[Offset]) -> Result<ValueMap> {
92        let mut values = vec![0; offsets.len()];
93
94        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
95        let ret = unsafe {
96            gpiod::gpiod_line_request_get_values_subset(
97                self.request,
98                offsets.len(),
99                offsets.as_ptr(),
100                values.as_mut_ptr(),
101            )
102        };
103
104        if ret == -1 {
105            Err(Error::OperationFailed(
106                OperationType::LineRequestGetValSubset,
107                errno::errno(),
108            ))
109        } else {
110            let mut map = ValueMap::new();
111
112            for (i, val) in values.iter().enumerate() {
113                map.insert(offsets[i], Value::new(*val)?);
114            }
115
116            Ok(map)
117        }
118    }
119
120    /// Get values of all lines associated with the request.
121    pub fn values(&self) -> Result<ValueMap> {
122        self.values_subset(&self.offsets())
123    }
124
125    /// Set the value of a single line associated with the request.
126    pub fn set_value(&mut self, offset: Offset, value: Value) -> Result<&mut Self> {
127        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
128        let ret =
129            unsafe { gpiod::gpiod_line_request_set_value(self.request, offset, value.value()) };
130
131        if ret == -1 {
132            Err(Error::OperationFailed(
133                OperationType::LineRequestSetVal,
134                errno::errno(),
135            ))
136        } else {
137            Ok(self)
138        }
139    }
140
141    /// Set values of a subset of lines associated with the request.
142    pub fn set_values_subset(&mut self, map: ValueMap) -> Result<&mut Self> {
143        let mut offsets = Vec::new();
144        let mut values = Vec::new();
145
146        for (offset, value) in map {
147            offsets.push(offset);
148            values.push(value.value());
149        }
150
151        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
152        let ret = unsafe {
153            gpiod::gpiod_line_request_set_values_subset(
154                self.request,
155                offsets.len(),
156                offsets.as_ptr(),
157                values.as_ptr(),
158            )
159        };
160
161        if ret == -1 {
162            Err(Error::OperationFailed(
163                OperationType::LineRequestSetValSubset,
164                errno::errno(),
165            ))
166        } else {
167            Ok(self)
168        }
169    }
170
171    /// Set values of all lines associated with the request.
172    pub fn set_values(&mut self, values: &[Value]) -> Result<&mut Self> {
173        if values.len() != self.num_lines() {
174            return Err(Error::InvalidArguments);
175        }
176
177        let mut new_values = Vec::new();
178        for value in values {
179            new_values.push(value.value());
180        }
181
182        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
183        let ret =
184            unsafe { gpiod::gpiod_line_request_set_values(self.request, new_values.as_ptr()) };
185
186        if ret == -1 {
187            Err(Error::OperationFailed(
188                OperationType::LineRequestSetVal,
189                errno::errno(),
190            ))
191        } else {
192            Ok(self)
193        }
194    }
195
196    /// Update the configuration of lines associated with the line request.
197    pub fn reconfigure_lines(&mut self, lconfig: &line::Config) -> Result<&mut Self> {
198        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
199        let ret =
200            unsafe { gpiod::gpiod_line_request_reconfigure_lines(self.request, lconfig.config) };
201
202        if ret == -1 {
203            Err(Error::OperationFailed(
204                OperationType::LineRequestReconfigLines,
205                errno::errno(),
206            ))
207        } else {
208            Ok(self)
209        }
210    }
211
212    /// Wait for edge events on any of the lines associated with the request.
213    pub fn wait_edge_events(&self, timeout: Option<Duration>) -> Result<bool> {
214        let timeout = match timeout {
215            Some(x) => x.as_nanos() as i64,
216            // Block indefinitely
217            None => -1,
218        };
219
220        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
221        let ret = unsafe { gpiod::gpiod_line_request_wait_edge_events(self.request, timeout) };
222
223        match ret {
224            -1 => Err(Error::OperationFailed(
225                OperationType::LineRequestWaitEdgeEvent,
226                errno::errno(),
227            )),
228            0 => Ok(false),
229            _ => Ok(true),
230        }
231    }
232
233    /// Get a number of edge events from a line request.
234    ///
235    /// This function will block if no event was queued for the line.
236    pub fn read_edge_events<'a>(
237        &self,
238        buffer: &'a mut request::Buffer,
239    ) -> Result<request::Events<'a>> {
240        buffer.read_edge_events(self)
241    }
242}
243
244impl AsRawFd for Request {
245    /// Get the file descriptor associated with the line request.
246    fn as_raw_fd(&self) -> i32 {
247        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
248        unsafe { gpiod::gpiod_line_request_get_fd(self.request) }
249    }
250}
251
252impl Drop for Request {
253    /// Release the requested lines and free all associated resources.
254    fn drop(&mut self) {
255        // SAFETY: `gpiod_line_request` is guaranteed to be valid here.
256        unsafe { gpiod::gpiod_line_request_release(self.request) }
257    }
258}