libgpiod/
line_config.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 super::{
6    Error, OperationType, Result, gpiod,
7    line::{Offset, Settings, SettingsMap, Value},
8};
9
10/// Line configuration objects.
11///
12/// The line-config object contains the configuration for lines that can be
13/// used in two cases:
14///  - when making a line request
15///  - when reconfiguring a set of already requested lines.
16///
17/// A new line-config object is empty. Using it in a request will lead to an
18/// error. In order for a line-config to become useful, it needs to be assigned
19/// at least one offset-to-settings mapping by calling
20/// ::gpiod_line_config_add_line_settings.
21///
22/// When calling ::gpiod_chip_request_lines, the library will request all
23/// offsets that were assigned settings in the order that they were assigned.
24
25#[derive(Debug, Eq, PartialEq)]
26pub struct Config {
27    pub(crate) config: *mut gpiod::gpiod_line_config,
28}
29
30// SAFETY: Config models a wrapper around an owned gpiod_line_config and may be
31// safely sent to other threads.
32unsafe impl Send for Config {}
33
34impl Config {
35    /// Create a new line config object.
36    pub fn new() -> Result<Self> {
37        // SAFETY: The `gpiod_line_config` returned by libgpiod is guaranteed to live as long
38        // as the `struct Config`.
39        let config = unsafe { gpiod::gpiod_line_config_new() };
40
41        if config.is_null() {
42            return Err(Error::OperationFailed(
43                OperationType::LineConfigNew,
44                errno::errno(),
45            ));
46        }
47
48        Ok(Self { config })
49    }
50
51    /// Resets the entire configuration stored in the object. This is useful if
52    /// the user wants to reuse the object without reallocating it.
53    pub fn reset(&mut self) {
54        // SAFETY: `gpiod_line_config` is guaranteed to be valid here.
55        unsafe { gpiod::gpiod_line_config_reset(self.config) }
56    }
57
58    /// Add line settings for a set of offsets.
59    pub fn add_line_settings(
60        &mut self,
61        offsets: &[Offset],
62        settings: Settings,
63    ) -> Result<&mut Self> {
64        // SAFETY: `gpiod_line_config` is guaranteed to be valid here.
65        let ret = unsafe {
66            gpiod::gpiod_line_config_add_line_settings(
67                self.config,
68                offsets.as_ptr(),
69                offsets.len(),
70                settings.settings,
71            )
72        };
73
74        if ret == -1 {
75            Err(Error::OperationFailed(
76                OperationType::LineConfigAddSettings,
77                errno::errno(),
78            ))
79        } else {
80            Ok(self)
81        }
82    }
83
84    /// Set output values for a number of lines.
85    pub fn set_output_values(&mut self, values: &[Value]) -> Result<&mut Self> {
86        let mut mapped_values = Vec::new();
87        for value in values {
88            mapped_values.push(value.value());
89        }
90
91        let ret = unsafe {
92            gpiod::gpiod_line_config_set_output_values(
93                self.config,
94                mapped_values.as_ptr(),
95                values.len(),
96            )
97        };
98
99        if ret == -1 {
100            Err(Error::OperationFailed(
101                OperationType::LineConfigSetOutputValues,
102                errno::errno(),
103            ))
104        } else {
105            Ok(self)
106        }
107    }
108
109    /// Get a mapping of offsets to line settings stored by this object.
110    pub fn line_settings(&self) -> Result<SettingsMap> {
111        let mut map = SettingsMap::new();
112        // SAFETY: gpiod_line_config is guaranteed to be valid here
113        let num_lines = unsafe { gpiod::gpiod_line_config_get_num_configured_offsets(self.config) };
114        let mut offsets = vec![0; num_lines];
115
116        // SAFETY: gpiod_line_config is guaranteed to be valid here.
117        let num_stored = unsafe {
118            gpiod::gpiod_line_config_get_configured_offsets(
119                self.config,
120                offsets.as_mut_ptr(),
121                num_lines,
122            )
123        };
124
125        for offset in &offsets[0..num_stored] {
126            // SAFETY: `gpiod_line_config` is guaranteed to be valid here.
127            let settings =
128                unsafe { gpiod::gpiod_line_config_get_line_settings(self.config, *offset) };
129            if settings.is_null() {
130                return Err(Error::OperationFailed(
131                    OperationType::LineConfigGetSettings,
132                    errno::errno(),
133                ));
134            }
135
136            // SAFETY: The above `gpiod_line_config_get_line_settings` call
137            // returns a copy of the line_settings. We thus have sole ownership.
138            // We no longer use the pointer for any other purpose.
139            let settings = unsafe { Settings::from_raw(settings) };
140
141            map.insert(*offset as Offset, settings);
142        }
143
144        Ok(map)
145    }
146}
147
148impl Drop for Config {
149    /// Free the line config object and release all associated resources.
150    fn drop(&mut self) {
151        // SAFETY: `gpiod_line_config` is guaranteed to be valid here.
152        unsafe { gpiod::gpiod_line_config_free(self.config) }
153    }
154}