| // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause |
| // SPDX-FileCopyrightText: 2022 Linaro Ltd. |
| // SPDX-FileCopyrightText: 2022 Viresh Kumar <viresh.kumar@linaro.org> |
| |
| use std::time::Duration; |
| |
| use super::{ |
| gpiod, |
| line::{Bias, Direction, Drive, Edge, EventClock, SettingKind, SettingVal, Value}, |
| Error, OperationType, Result, |
| }; |
| |
| /// Line settings objects. |
| /// |
| /// Line settings object contains a set of line properties that can be used |
| /// when requesting lines or reconfiguring an existing request. |
| /// |
| /// Mutators in general can only fail if the new property value is invalid. The |
| /// return values can be safely ignored - the object remains valid even after |
| /// a mutator fails and simply uses the sane default appropriate for given |
| /// property. |
| |
| #[derive(Debug, Eq, PartialEq)] |
| pub struct Settings { |
| pub(crate) settings: *mut gpiod::gpiod_line_settings, |
| } |
| |
| impl Settings { |
| /// Create a new line settings object. |
| pub fn new() -> Result<Self> { |
| // SAFETY: The `gpiod_line_settings` returned by libgpiod is guaranteed to live as long |
| // as the `struct Settings`. |
| let settings = unsafe { gpiod::gpiod_line_settings_new() }; |
| |
| if settings.is_null() { |
| return Err(Error::OperationFailed( |
| OperationType::LineSettingsNew, |
| errno::errno(), |
| )); |
| } |
| |
| Ok(Self { settings }) |
| } |
| |
| pub fn new_with_settings(settings: *mut gpiod::gpiod_line_settings) -> Self { |
| Self { settings } |
| } |
| |
| /// Resets the line settings object to its default values. |
| pub fn reset(&mut self) { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| unsafe { gpiod::gpiod_line_settings_reset(self.settings) } |
| } |
| |
| /// Makes copy of the settings object. |
| pub fn settings_clone(&self) -> Result<Self> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let settings = unsafe { gpiod::gpiod_line_settings_copy(self.settings) }; |
| if settings.is_null() { |
| return Err(Error::OperationFailed( |
| OperationType::LineSettingsCopy, |
| errno::errno(), |
| )); |
| } |
| |
| Ok(Self { settings }) |
| } |
| |
| /// Set line prop setting. |
| pub fn set_prop(&mut self, props: &[SettingVal]) -> Result<&mut Self> { |
| for property in props { |
| match property { |
| SettingVal::Direction(prop) => self.set_direction(*prop)?, |
| SettingVal::EdgeDetection(prop) => self.set_edge_detection(*prop)?, |
| SettingVal::Bias(prop) => self.set_bias(*prop)?, |
| SettingVal::Drive(prop) => self.set_drive(*prop)?, |
| SettingVal::ActiveLow(prop) => self.set_active_low(*prop), |
| SettingVal::DebouncePeriod(prop) => self.set_debounce_period(*prop), |
| SettingVal::EventClock(prop) => self.set_event_clock(*prop)?, |
| SettingVal::OutputValue(prop) => self.set_output_value(*prop)?, |
| }; |
| } |
| |
| Ok(self) |
| } |
| |
| /// Get the line prop setting. |
| pub fn prop(&self, property: SettingKind) -> Result<SettingVal> { |
| Ok(match property { |
| SettingKind::Direction => SettingVal::Direction(self.direction()?), |
| SettingKind::EdgeDetection => SettingVal::EdgeDetection(self.edge_detection()?), |
| SettingKind::Bias => SettingVal::Bias(self.bias()?), |
| SettingKind::Drive => SettingVal::Drive(self.drive()?), |
| SettingKind::ActiveLow => SettingVal::ActiveLow(self.active_low()), |
| SettingKind::DebouncePeriod => SettingVal::DebouncePeriod(self.debounce_period()?), |
| SettingKind::EventClock => SettingVal::EventClock(self.event_clock()?), |
| SettingKind::OutputValue => SettingVal::OutputValue(self.output_value()?), |
| }) |
| } |
| |
| /// Set the line direction. |
| pub fn set_direction(&mut self, direction: Direction) -> Result<&mut Self> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let ret = unsafe { |
| gpiod::gpiod_line_settings_set_direction(self.settings, direction.gpiod_direction()) |
| }; |
| |
| if ret == -1 { |
| Err(Error::OperationFailed( |
| OperationType::LineSettingsSetDirection, |
| errno::errno(), |
| )) |
| } else { |
| Ok(self) |
| } |
| } |
| |
| /// Get the direction setting. |
| pub fn direction(&self) -> Result<Direction> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| Direction::new(unsafe { gpiod::gpiod_line_settings_get_direction(self.settings) }) |
| } |
| |
| /// Set the edge event detection setting. |
| pub fn set_edge_detection(&mut self, edge: Option<Edge>) -> Result<&mut Self> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let ret = unsafe { |
| gpiod::gpiod_line_settings_set_edge_detection(self.settings, Edge::gpiod_edge(edge)) |
| }; |
| |
| if ret == -1 { |
| Err(Error::OperationFailed( |
| OperationType::LineSettingsSetEdgeDetection, |
| errno::errno(), |
| )) |
| } else { |
| Ok(self) |
| } |
| } |
| |
| /// Get the edge event detection setting. |
| pub fn edge_detection(&self) -> Result<Option<Edge>> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| Edge::new(unsafe { gpiod::gpiod_line_settings_get_edge_detection(self.settings) }) |
| } |
| |
| /// Set the bias setting. |
| pub fn set_bias(&mut self, bias: Option<Bias>) -> Result<&mut Self> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let ret = |
| unsafe { gpiod::gpiod_line_settings_set_bias(self.settings, Bias::gpiod_bias(bias)) }; |
| |
| if ret == -1 { |
| Err(Error::OperationFailed( |
| OperationType::LineSettingsSetBias, |
| errno::errno(), |
| )) |
| } else { |
| Ok(self) |
| } |
| } |
| |
| /// Get the bias setting. |
| pub fn bias(&self) -> Result<Option<Bias>> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| Bias::new(unsafe { gpiod::gpiod_line_settings_get_bias(self.settings) }) |
| } |
| |
| /// Set the drive setting. |
| pub fn set_drive(&mut self, drive: Drive) -> Result<&mut Self> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let ret = |
| unsafe { gpiod::gpiod_line_settings_set_drive(self.settings, drive.gpiod_drive()) }; |
| |
| if ret == -1 { |
| Err(Error::OperationFailed( |
| OperationType::LineSettingsSetDrive, |
| errno::errno(), |
| )) |
| } else { |
| Ok(self) |
| } |
| } |
| |
| /// Get the drive setting. |
| pub fn drive(&self) -> Result<Drive> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| Drive::new(unsafe { gpiod::gpiod_line_settings_get_drive(self.settings) }) |
| } |
| |
| /// Set active-low setting. |
| pub fn set_active_low(&mut self, active_low: bool) -> &mut Self { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| unsafe { |
| gpiod::gpiod_line_settings_set_active_low(self.settings, active_low); |
| } |
| self |
| } |
| |
| /// Check the active-low setting. |
| pub fn active_low(&self) -> bool { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| unsafe { gpiod::gpiod_line_settings_get_active_low(self.settings) } |
| } |
| |
| /// Set the debounce period setting. |
| pub fn set_debounce_period(&mut self, period: Duration) -> &mut Self { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| unsafe { |
| gpiod::gpiod_line_settings_set_debounce_period_us( |
| self.settings, |
| period.as_micros().try_into().unwrap(), |
| ); |
| } |
| |
| self |
| } |
| |
| /// Get the debounce period. |
| pub fn debounce_period(&self) -> Result<Duration> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| Ok(Duration::from_micros(unsafe { |
| gpiod::gpiod_line_settings_get_debounce_period_us(self.settings) as u64 |
| })) |
| } |
| |
| /// Set the event clock setting. |
| pub fn set_event_clock(&mut self, clock: EventClock) -> Result<&mut Self> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let ret = unsafe { |
| gpiod::gpiod_line_settings_set_event_clock(self.settings, clock.gpiod_clock()) |
| }; |
| |
| if ret == -1 { |
| Err(Error::OperationFailed( |
| OperationType::LineSettingsSetEventClock, |
| errno::errno(), |
| )) |
| } else { |
| Ok(self) |
| } |
| } |
| |
| /// Get the event clock setting. |
| pub fn event_clock(&self) -> Result<EventClock> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| EventClock::new(unsafe { gpiod::gpiod_line_settings_get_event_clock(self.settings) } as u32) |
| } |
| |
| /// Set the output value setting. |
| pub fn set_output_value(&mut self, value: Value) -> Result<&mut Self> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let ret = |
| unsafe { gpiod::gpiod_line_settings_set_output_value(self.settings, value.value()) }; |
| |
| if ret == -1 { |
| Err(Error::OperationFailed( |
| OperationType::LineSettingsSetOutputValue, |
| errno::errno(), |
| )) |
| } else { |
| Ok(self) |
| } |
| } |
| |
| /// Get the output value, 0 or 1. |
| pub fn output_value(&self) -> Result<Value> { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| let value = unsafe { gpiod::gpiod_line_settings_get_output_value(self.settings) }; |
| |
| if value != 0 && value != 1 { |
| Err(Error::OperationFailed( |
| OperationType::LineSettingsGetOutVal, |
| errno::errno(), |
| )) |
| } else { |
| Value::new(value) |
| } |
| } |
| } |
| |
| impl Drop for Settings { |
| /// Free the line settings object and release all associated resources. |
| fn drop(&mut self) { |
| // SAFETY: `gpiod_line_settings` is guaranteed to be valid here. |
| unsafe { gpiod::gpiod_line_settings_free(self.settings) } |
| } |
| } |