libgpiod/
line_settings.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
5use std::time::Duration;
6
7use super::{
8    Error, OperationType, Result, gpiod,
9    line::{Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value},
10};
11
12/// Line settings objects.
13///
14/// Line settings object contains a set of line properties that can be used
15/// when requesting lines or reconfiguring an existing request.
16///
17/// Mutators in general can only fail if the new property value is invalid. The
18/// return values can be safely ignored - the object remains valid even after
19/// a mutator fails and simply uses the sane default appropriate for given
20/// property.
21
22#[derive(Debug, Eq, PartialEq)]
23pub struct Settings {
24    pub(crate) settings: *mut gpiod::gpiod_line_settings,
25}
26
27// SAFETY: Settings models a wrapper around an owned gpiod_line_settings and may
28// be safely sent to other threads.
29unsafe impl Send for Settings {}
30
31impl Settings {
32    /// Create a new line settings object.
33    pub fn new() -> Result<Self> {
34        // SAFETY: The `gpiod_line_settings` returned by libgpiod is guaranteed to live as long
35        // as the `struct Settings`.
36        let settings = unsafe { gpiod::gpiod_line_settings_new() };
37
38        if settings.is_null() {
39            return Err(Error::OperationFailed(
40                OperationType::LineSettingsNew,
41                errno::errno(),
42            ));
43        }
44
45        Ok(Self { settings })
46    }
47
48    /// Converts a owned pointer into an owned instance
49    ///
50    /// Assumes sole ownership over a [gpiod::gpiod_line_settings] instance.
51    ///
52    /// SAFETY: The pointer must point to an instance that is valid. After
53    /// constructing a [Settings] the pointer MUST NOT be used for any other
54    /// purpose anymore. All interactions with the libgpiod API have to happen
55    /// through this object.
56    pub(crate) unsafe fn from_raw(settings: *mut gpiod::gpiod_line_settings) -> Self {
57        Self { settings }
58    }
59
60    /// Resets the line settings object to its default values.
61    pub fn reset(&mut self) {
62        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
63        unsafe { gpiod::gpiod_line_settings_reset(self.settings) }
64    }
65
66    /// Makes a copy of the settings object.
67    pub fn try_clone(&self) -> Result<Self> {
68        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
69        let settings = unsafe { gpiod::gpiod_line_settings_copy(self.settings) };
70        if settings.is_null() {
71            return Err(Error::OperationFailed(
72                OperationType::LineSettingsCopy,
73                errno::errno(),
74            ));
75        }
76
77        Ok(Self { settings })
78    }
79
80    /// Set line prop setting.
81    pub fn set_prop(&mut self, props: &[SettingVal]) -> Result<&mut Self> {
82        for property in props {
83            match property {
84                SettingVal::Direction(prop) => self.set_direction(*prop)?,
85                SettingVal::EdgeDetection(prop) => self.set_edge_detection(*prop)?,
86                SettingVal::Bias(prop) => self.set_bias(*prop)?,
87                SettingVal::Drive(prop) => self.set_drive(*prop)?,
88                SettingVal::ActiveLow(prop) => self.set_active_low(*prop),
89                SettingVal::DebouncePeriod(prop) => self.set_debounce_period(*prop),
90                SettingVal::EventClock(prop) => self.set_event_clock(*prop)?,
91                SettingVal::OutputValue(prop) => self.set_output_value(*prop)?,
92            };
93        }
94
95        Ok(self)
96    }
97
98    /// Get the line prop setting.
99    pub fn prop(&self, property: SettingKind) -> Result<SettingVal> {
100        Ok(match property {
101            SettingKind::Direction => SettingVal::Direction(self.direction()?),
102            SettingKind::EdgeDetection => SettingVal::EdgeDetection(self.edge_detection()?),
103            SettingKind::Bias => SettingVal::Bias(self.bias()?),
104            SettingKind::Drive => SettingVal::Drive(self.drive()?),
105            SettingKind::ActiveLow => SettingVal::ActiveLow(self.active_low()),
106            SettingKind::DebouncePeriod => SettingVal::DebouncePeriod(self.debounce_period()?),
107            SettingKind::EventClock => SettingVal::EventClock(self.event_clock()?),
108            SettingKind::OutputValue => SettingVal::OutputValue(self.output_value()?),
109        })
110    }
111
112    /// Set the line direction.
113    pub fn set_direction(&mut self, direction: Direction) -> Result<&mut Self> {
114        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
115        let ret = unsafe {
116            gpiod::gpiod_line_settings_set_direction(self.settings, direction.gpiod_direction())
117        };
118
119        if ret == -1 {
120            Err(Error::OperationFailed(
121                OperationType::LineSettingsSetDirection,
122                errno::errno(),
123            ))
124        } else {
125            Ok(self)
126        }
127    }
128
129    /// Get the direction setting.
130    pub fn direction(&self) -> Result<Direction> {
131        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
132        Direction::new(unsafe { gpiod::gpiod_line_settings_get_direction(self.settings) })
133    }
134
135    /// Set the edge event detection setting.
136    pub fn set_edge_detection(&mut self, edge: Option<Edge>) -> Result<&mut Self> {
137        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
138        let ret = unsafe {
139            gpiod::gpiod_line_settings_set_edge_detection(self.settings, Edge::gpiod_edge(edge))
140        };
141
142        if ret == -1 {
143            Err(Error::OperationFailed(
144                OperationType::LineSettingsSetEdgeDetection,
145                errno::errno(),
146            ))
147        } else {
148            Ok(self)
149        }
150    }
151
152    /// Get the edge event detection setting.
153    pub fn edge_detection(&self) -> Result<Option<Edge>> {
154        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
155        Edge::new(unsafe { gpiod::gpiod_line_settings_get_edge_detection(self.settings) })
156    }
157
158    /// Set the bias setting.
159    pub fn set_bias(&mut self, bias: Option<Bias>) -> Result<&mut Self> {
160        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
161        let ret =
162            unsafe { gpiod::gpiod_line_settings_set_bias(self.settings, Bias::gpiod_bias(bias)) };
163
164        if ret == -1 {
165            Err(Error::OperationFailed(
166                OperationType::LineSettingsSetBias,
167                errno::errno(),
168            ))
169        } else {
170            Ok(self)
171        }
172    }
173
174    /// Get the bias setting.
175    pub fn bias(&self) -> Result<Option<Bias>> {
176        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
177        Bias::new(unsafe { gpiod::gpiod_line_settings_get_bias(self.settings) })
178    }
179
180    /// Set the drive setting.
181    pub fn set_drive(&mut self, drive: Drive) -> Result<&mut Self> {
182        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
183        let ret =
184            unsafe { gpiod::gpiod_line_settings_set_drive(self.settings, drive.gpiod_drive()) };
185
186        if ret == -1 {
187            Err(Error::OperationFailed(
188                OperationType::LineSettingsSetDrive,
189                errno::errno(),
190            ))
191        } else {
192            Ok(self)
193        }
194    }
195
196    /// Get the drive setting.
197    pub fn drive(&self) -> Result<Drive> {
198        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
199        Drive::new(unsafe { gpiod::gpiod_line_settings_get_drive(self.settings) })
200    }
201
202    /// Set active-low setting.
203    pub fn set_active_low(&mut self, active_low: bool) -> &mut Self {
204        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
205        unsafe {
206            gpiod::gpiod_line_settings_set_active_low(self.settings, active_low);
207        }
208        self
209    }
210
211    /// Check the active-low setting.
212    pub fn active_low(&self) -> bool {
213        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
214        unsafe { gpiod::gpiod_line_settings_get_active_low(self.settings) }
215    }
216
217    /// Set the debounce period setting.
218    pub fn set_debounce_period(&mut self, period: Duration) -> &mut Self {
219        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
220        unsafe {
221            gpiod::gpiod_line_settings_set_debounce_period_us(
222                self.settings,
223                period.as_micros().try_into().unwrap(),
224            );
225        }
226
227        self
228    }
229
230    /// Get the debounce period.
231    pub fn debounce_period(&self) -> Result<Duration> {
232        // c_ulong may be 32bit OR 64bit, clippy reports a false-positive here:
233        // https://github.com/rust-lang/rust-clippy/issues/10555
234        #[allow(clippy::unnecessary_cast)]
235        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
236        Ok(Duration::from_micros(unsafe {
237            gpiod::gpiod_line_settings_get_debounce_period_us(self.settings) as u64
238        }))
239    }
240
241    /// Set the event clock setting.
242    pub fn set_event_clock(&mut self, clock: EventClock) -> Result<&mut Self> {
243        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
244        let ret = unsafe {
245            gpiod::gpiod_line_settings_set_event_clock(self.settings, clock.gpiod_clock())
246        };
247
248        if ret == -1 {
249            Err(Error::OperationFailed(
250                OperationType::LineSettingsSetEventClock,
251                errno::errno(),
252            ))
253        } else {
254            Ok(self)
255        }
256    }
257
258    /// Get the event clock setting.
259    pub fn event_clock(&self) -> Result<EventClock> {
260        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
261        EventClock::new(unsafe { gpiod::gpiod_line_settings_get_event_clock(self.settings) })
262    }
263
264    /// Set the output value setting.
265    pub fn set_output_value(&mut self, value: Value) -> Result<&mut Self> {
266        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
267        let ret =
268            unsafe { gpiod::gpiod_line_settings_set_output_value(self.settings, value.value()) };
269
270        if ret == -1 {
271            Err(Error::OperationFailed(
272                OperationType::LineSettingsSetOutputValue,
273                errno::errno(),
274            ))
275        } else {
276            Ok(self)
277        }
278    }
279
280    /// Get the output value, 0 or 1.
281    pub fn output_value(&self) -> Result<Value> {
282        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
283        let value = unsafe { gpiod::gpiod_line_settings_get_output_value(self.settings) };
284
285        if value != 0 && value != 1 {
286            Err(Error::OperationFailed(
287                OperationType::LineSettingsGetOutVal,
288                errno::errno(),
289            ))
290        } else {
291            Value::new(value)
292        }
293    }
294}
295
296impl Drop for Settings {
297    /// Free the line settings object and release all associated resources.
298    fn drop(&mut self) {
299        // SAFETY: `gpiod_line_settings` is guaranteed to be valid here.
300        unsafe { gpiod::gpiod_line_settings_free(self.settings) }
301    }
302}