1mod errors;
3
4use std::{cell::RefCell, convert::TryInto, ffi::CString};
5
6use gpio_cdev::{Chip, LineHandle, LineRequestFlags};
7use libc::c_int;
8use log;
9
10use kpal_plugin::{error_codes::*, *};
11
12use crate::errors::*;
13
14const DEFAULT_DEVICE_FILE: &str = "/dev/gpiochip0";
15
16const DEFAULT_OFFSET: u32 = 4;
18
19#[derive(Debug)]
21#[repr(C)]
22struct GPIOPlugin {
23 attributes: Attributes<Self, GPIOPluginError>,
25
26 chip: Option<RefCell<Chip>>,
28
29 line_handle: Option<LineHandle>,
31}
32
33impl PluginAPI<GPIOPluginError> for GPIOPlugin {
34 fn new() -> Result<GPIOPlugin, GPIOPluginError> {
36 let attributes = RefCell::new(multimap! {
37 0, "device file" => Attribute {
38 name: CString::new("Device file").unwrap(),
39 value: Value::String(CString::new(DEFAULT_DEVICE_FILE).unwrap()),
40 callbacks_init: Callbacks::Update,
41 callbacks_run: Callbacks::Constant,
42 },
43 1, "offset" => Attribute {
44 name: CString::new("Offset").unwrap(),
45 value: Value::Uint(DEFAULT_OFFSET),
46 callbacks_init: Callbacks::Update,
47 callbacks_run: Callbacks::Constant,
48 },
49 2, "pin state" => Attribute {
50 name: CString::new("Pin state").unwrap(),
51 value: Value::Int(0),
52 callbacks_init: Callbacks::Constant,
53 callbacks_run: Callbacks::GetAndSet(on_get_pin_state, on_set_pin_state),
54 },
55 });
56
57 Ok(GPIOPlugin {
58 attributes,
59 chip: None,
60 line_handle: None,
61 })
62 }
63
64 fn init(&mut self) -> Result<(), GPIOPluginError> {
66 let device_file = if let Value::String(device_file) = &self
67 .attributes
68 .borrow()
69 .get_alt(&"device file")
70 .ok_or(GPIOPluginError {
71 error_code: ATTRIBUTE_DOES_NOT_EXIST,
72 side: None,
73 })?
74 .value
75 {
76 device_file.clone().into_string()?
77 } else {
78 unreachable!()
79 };
80 let mut chip = Chip::new(device_file)?;
81
82 let offset = if let Value::Uint(offset) = self
83 .attributes
84 .borrow()
85 .get_alt(&"offset")
86 .ok_or(GPIOPluginError {
87 error_code: ATTRIBUTE_DOES_NOT_EXIST,
88 side: None,
89 })?
90 .value
91 {
92 offset
93 } else {
94 unreachable!()
95 };
96
97 let handle = chip
98 .get_line(offset)?
99 .request(LineRequestFlags::OUTPUT, 0, "set-output")?;
100
101 self.chip = Some(RefCell::new(chip));
102 self.line_handle = Some(handle);
103
104 Ok(())
105 }
106
107 fn attributes(&self) -> &Attributes<GPIOPlugin, GPIOPluginError> {
108 &self.attributes
109 }
110}
111
112fn on_get_pin_state(plugin: &GPIOPlugin, _cached: &Value) -> Result<Value, GPIOPluginError> {
119 let pin_value = plugin
120 .line_handle
121 .as_ref()
122 .ok_or_else(|| PluginUninitializedError {})?
123 .get_value()?;
124 let value = Value::Int(pin_value.try_into()?);
125
126 Ok(value)
127}
128
129fn on_set_pin_state(
137 plugin: &GPIOPlugin,
138 _cached: &Value,
139 val: &Val,
140) -> Result<(), GPIOPluginError> {
141 let pin_value = if let Val::Int(pin_value) = val {
142 pin_value.to_owned().try_into()?
143 } else {
144 unreachable!()
145 };
146
147 plugin
148 .line_handle
149 .as_ref()
150 .ok_or_else(|| PluginUninitializedError {})?
151 .set_value(pin_value)?;
152
153 Ok(())
154}
155
156declare_plugin!(GPIOPlugin, GPIOPluginError);