Skip to content

Commit b6904f1

Browse files
committed
export: changes related to getting export implemented
Most of these changes weren't in the export core code but were a result of needing features to be used by the export code. Just hammering out the design for the remainder of the commands. Signed-off-by: Paul Osborne <[email protected]>
1 parent 51596c6 commit b6904f1

File tree

5 files changed

+161
-68
lines changed

5 files changed

+161
-68
lines changed

src/commands/gpio_exportall.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
// Copyright (C) 2016, Paul Osborne <[email protected]>
22

3-
use options::{GpioExportAllOptions};
3+
use options::GpioExportAllOptions;
44
use config::GpioConfig;
55
use std::process::exit;
6+
use export;
67

7-
pub fn main(opts: &GpioExportAllOptions) {
8-
let config = match GpioConfig::load(&opts.gpio_opts.configs[..]) {
9-
Ok(config) => config,
10-
Err(e) => {
11-
println!("Error: {:?}", e);
12-
exit(1);
8+
pub fn main(config: &GpioConfig, opts: &GpioExportAllOptions) {
9+
for pin in config.get_pins() {
10+
let symlink_root = opts.symlink_root
11+
.clone()
12+
.unwrap_or(String::from(config.get_symlink_root()));
13+
match export::export(pin, Some(&symlink_root[..])) {
14+
Ok(_) => {}
15+
Err(e) => {
16+
println!("Error occurred while exporting pin: {:?}", pin);
17+
println!("{}", e);
18+
exit(1);
19+
}
1320
}
14-
};
15-
16-
for pin in config.pins {
17-
println!("{:?}", pin);
1821
}
1922
}

src/config.rs

Lines changed: 88 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
use glob::glob;
44
use rustc_serialize::{Decodable, Decoder};
55
use std::collections::BTreeSet;
6+
use std::fmt;
67
use std::fs::{self, File};
78
use std::io;
89
use std::io::prelude::*;
910
use std::path::Path;
1011
use sysfs_gpio;
1112
use toml;
1213

14+
const DEFAULT_SYMLINK_ROOT: &'static str = "/var/run/gpio";
15+
1316
#[derive(Debug, PartialEq, Clone)]
1417
pub struct Direction(pub sysfs_gpio::Direction);
1518

@@ -28,9 +31,10 @@ pub struct PinConfig {
2831
pub active_low: bool,
2932
}
3033

31-
#[derive(RustcDecodable, Clone, Debug)]
34+
#[derive(Clone, Debug, Default)]
3235
pub struct GpioConfig {
33-
pub pins: Vec<PinConfig>,
36+
pins: Vec<PinConfig>,
37+
symlink_root: Option<String>,
3438
}
3539

3640
#[derive(Debug)]
@@ -41,6 +45,22 @@ pub enum Error {
4145
NoConfigFound,
4246
}
4347

48+
impl fmt::Display for Error {
49+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50+
match *self {
51+
Error::IoError(ref e) => e.fmt(f),
52+
Error::ParserErrors(ref errors) => {
53+
for e in errors {
54+
try!(e.fmt(f));
55+
}
56+
Ok(())
57+
}
58+
Error::DecodingError(ref e) => e.fmt(f),
59+
Error::NoConfigFound => write!(f, "No Config Found"),
60+
}
61+
}
62+
}
63+
4464
impl From<io::Error> for Error {
4565
fn from(e: io::Error) -> Self {
4666
Error::IoError(e)
@@ -59,19 +79,37 @@ impl From<toml::DecodeError> for Error {
5979
}
6080
}
6181

82+
impl Decodable for GpioConfig {
83+
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
84+
// Get items under the [config] header if present
85+
let symlink_root: Option<String> = d.read_struct_field("config", 0, |cfg| {
86+
cfg.read_struct_field("symlink_root",
87+
0,
88+
Decodable::decode)
89+
})
90+
.ok();
91+
92+
Ok(GpioConfig {
93+
pins: try!(d.read_struct_field("pins", 0, Decodable::decode)),
94+
symlink_root: symlink_root,
95+
})
96+
}
97+
}
98+
6299
impl Decodable for PinConfig {
63100
fn decode<D: Decoder>(d: &mut D) -> Result<PinConfig, D::Error> {
64101
Ok(PinConfig {
65102
num: try!(d.read_struct_field("num", 0, Decodable::decode)),
66103
direction: d.read_struct_field("direction", 0, |dir_d| {
67-
match &try!(dir_d.read_str())[..] {
68-
"in" => Ok(sysfs_gpio::Direction::In),
69-
"out" => Ok(sysfs_gpio::Direction::Out),
70-
"high" => Ok(sysfs_gpio::Direction::High),
71-
"low" => Ok(sysfs_gpio::Direction::Low),
72-
_ => Err(dir_d.error("Expected one of: {in, out, high, low}")),
73-
}
74-
}).unwrap_or(sysfs_gpio::Direction::In), // default: In
104+
match &try!(dir_d.read_str())[..] {
105+
"in" => Ok(sysfs_gpio::Direction::In),
106+
"out" => Ok(sysfs_gpio::Direction::Out),
107+
"high" => Ok(sysfs_gpio::Direction::High),
108+
"low" => Ok(sysfs_gpio::Direction::Low),
109+
_ => Err(dir_d.error("Expected one of: {in, out, high, low}")),
110+
}
111+
})
112+
.unwrap_or(sysfs_gpio::Direction::In), // default: In
75113
names: d.read_struct_field("names", 0, Decodable::decode).unwrap_or(BTreeSet::new()),
76114
export: d.read_struct_field("export", 0, Decodable::decode).unwrap_or(true),
77115
active_low: d.read_struct_field("active_low", 0, Decodable::decode).unwrap_or(false),
@@ -80,7 +118,6 @@ impl Decodable for PinConfig {
80118
}
81119

82120
impl GpioConfig {
83-
84121
/// Load a GPIO Config from the system
85122
///
86123
/// This function will load the GPIO configuration from standard system
@@ -145,6 +182,17 @@ impl GpioConfig {
145182
GpioConfig::from_str(&contents[..])
146183
}
147184

185+
pub fn get_pins(&self) -> &[PinConfig] {
186+
&self.pins[..]
187+
}
188+
189+
pub fn get_symlink_root(&self) -> &str {
190+
match self.symlink_root {
191+
Some(ref root) => &root,
192+
None => DEFAULT_SYMLINK_ROOT,
193+
}
194+
}
195+
148196
/// Merge other into self (takes ownership of other)
149197
///
150198
/// If in conflict, the other GPIO config takes priority.
@@ -158,14 +206,13 @@ impl GpioConfig {
158206
pin.export = other_pin.export;
159207
pin.active_low = other_pin.active_low;
160208
true
161-
},
209+
}
162210
None => false,
163211
};
164212

165213
if !existing {
166214
self.pins.push(other_pin);
167215
}
168-
169216
}
170217
}
171218
}
@@ -177,8 +224,7 @@ mod test {
177224
use std::collections::BTreeSet;
178225
use sysfs_gpio::Direction as D;
179226

180-
static BASIC_CFG: &'static str = r#"
181-
# Basic Form
227+
const BASIC_CFG: &'static str = r#"
182228
[[pins]]
183229
num = 73
184230
names = ["reset_button"]
@@ -192,19 +238,22 @@ names = ["status_led", "A27", "green_led"]
192238
direction = "out"
193239
"#;
194240

195-
static COMPACT_CFG: &'static str = r#"
241+
const COMPACT_CFG: &'static str = r#"
196242
pins = [
197243
{ num = 73, names = ["reset_button"], direction = "in", active_low = true, export = true},
198244
{ num = 37, names = ["status_led", "A27", "green_led"], direction = "out"},
199245
]
246+
247+
[config]
248+
symlink_root = "/tmp/gpio"
200249
"#;
201250

202-
static MISSING_PINNUM_CFG: &'static str = r#"
251+
const MISSING_PINNUM_CFG: &'static str = r#"
203252
[[pins]]
204253
export = true
205254
"#;
206255

207-
static PARTIALLY_OVERLAPS_BASIC_CFG: &'static str = r#"
256+
const PARTIALLY_OVERLAPS_BASIC_CFG: &'static str = r#"
208257
# Add a new alias to pin 73
209258
[[pins]]
210259
num = 73
@@ -226,15 +275,16 @@ names = ["wildcard"]
226275
fn test_parse_basic() {
227276
let config = GpioConfig::from_str(BASIC_CFG).unwrap();
228277
let status_led = config.pins.get(1).unwrap();
229-
let names = BTreeSet::from_iter(
230-
vec!(String::from("status_led"),
231-
String::from("A27"),
232-
String::from("green_led")));
278+
let names = BTreeSet::from_iter(vec![String::from("status_led"),
279+
String::from("A27"),
280+
String::from("green_led")]);
281+
282+
assert_eq!(config.get_symlink_root(), "/var/run/gpio");
233283

234284
let reset_button = config.pins.get(0).unwrap();
235285
assert_eq!(reset_button.num, 73);
236286
assert_eq!(reset_button.names,
237-
BTreeSet::from_iter(vec!(String::from("reset_button"))));
287+
BTreeSet::from_iter(vec![String::from("reset_button")]));
238288
assert_eq!(reset_button.direction, D::In);
239289
assert_eq!(reset_button.active_low, true);
240290
assert_eq!(reset_button.export, true);
@@ -249,29 +299,29 @@ names = ["wildcard"]
249299
fn test_parser_compact() {
250300
let config = GpioConfig::from_str(COMPACT_CFG).unwrap();
251301
let status_led = config.pins.get(1).unwrap();
252-
let names = BTreeSet::from_iter(
253-
vec!(String::from("status_led"),
254-
String::from("A27"),
255-
String::from("green_led")));
302+
let names = BTreeSet::from_iter(vec![String::from("status_led"),
303+
String::from("A27"),
304+
String::from("green_led")]);
256305
assert_eq!(status_led.names, names);
257306
assert_eq!(status_led.direction, D::Out);
258307
assert_eq!(status_led.active_low, false);
259308
assert_eq!(status_led.export, true);
309+
assert_eq!(config.get_symlink_root(), "/tmp/gpio")
260310
}
261311

262312
#[test]
263313
fn test_parser_empty_toml() {
264314
let configstr = "";
265315
match GpioConfig::from_str(configstr) {
266-
Ok(pins) => { assert_eq!(pins.pins, vec!()) },
316+
Ok(pins) => assert_eq!(pins.pins, vec![]),
267317
_ => panic!("Expected a decoding error"),
268318
}
269319
}
270320

271321
#[test]
272322
fn test_parser_missing_pinnum() {
273323
match GpioConfig::from_str(MISSING_PINNUM_CFG) {
274-
Err(Error::DecodingError(_)) => {},
324+
Err(Error::DecodingError(_)) => {}
275325
_ => panic!("Expected a decoding error"),
276326
}
277327
}
@@ -281,7 +331,7 @@ names = ["wildcard"]
281331
// basically, just garbage data
282332
let configstr = r"[] -*-..asdf=-=-@#$%^&*()";
283333
match GpioConfig::from_str(configstr) {
284-
Err(Error::ParserErrors(_)) => {},
334+
Err(Error::ParserErrors(_)) => {}
285335
_ => panic!("Did not receive parse error when expected"),
286336
}
287337
}
@@ -296,25 +346,25 @@ names = ["wildcard"]
296346

297347
let reset_button = config.pins.get(0).unwrap();
298348
assert_eq!(reset_button.num, 73);
299-
assert_eq!(reset_button.names, BTreeSet::from_iter(
300-
vec!(String::from("reset_button"),
301-
String::from("new_name"))));
349+
assert_eq!(reset_button.names,
350+
BTreeSet::from_iter(vec![String::from("reset_button"),
351+
String::from("new_name")]));
302352
assert_eq!(reset_button.direction, D::In);
303353
assert_eq!(reset_button.active_low, false);
304354
assert_eq!(reset_button.export, true);
305355

306356
let status_led = config.pins.get(1).unwrap();
307-
let names = BTreeSet::from_iter(
308-
vec!(String::from("status_led"),
309-
String::from("A27"),
310-
String::from("green_led")));
357+
let names = BTreeSet::from_iter(vec![String::from("status_led"),
358+
String::from("A27"),
359+
String::from("green_led")]);
311360
assert_eq!(status_led.names, names);
312361
assert_eq!(status_led.direction, D::In);
313362
assert_eq!(status_led.active_low, false);
314363
assert_eq!(status_led.export, true);
315364

316365
let wildcard = config.pins.get(2).unwrap();
317366
assert_eq!(wildcard.num, 88);
318-
assert_eq!(wildcard.names, BTreeSet::from_iter(vec!(String::from("wildcard"))));
367+
assert_eq!(wildcard.names,
368+
BTreeSet::from_iter(vec![String::from("wildcard")]));
319369
}
320370
}

src/export.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
// Copyright (C) 2016, The gpio-utils Authors
22

3+
use std::path;
4+
use std::os::unix::fs;
35
use config::PinConfig;
46
use sysfs_gpio;
57

6-
#[derive(Debug)]
7-
pub enum ExportError {
8-
GpioError(sysfs_gpio::Error),
9-
}
10-
118
/// Export the pin specified in the provided config
129
///
1310
/// Exporting a pin (in this context) involves, a few different
1411
/// actions:
1512
///
16-
/// 1. The GPIO pin itself is exported (via /sys/class/gpoi/export)
13+
/// 1. The GPIO pin itself is exported (via /sys/class/gpio/export)
1714
/// 2. For each GPIO name/alias, a symlink is created from
1815
/// `/var/run/gpio/<name>` -> `/sys/class/gpio<num>`.
1916
///
2017
/// If the GPIO is already exported, this function will continue
2118
/// without an error as the desired end state is achieved.
22-
pub fn export(pin: &PinConfig) -> Result<(), ExportError> {
19+
pub fn export(pin: &PinConfig, symlink_root: Option<&str>) -> Result<(), sysfs_gpio::Error> {
20+
let sysfs_gpio_pin = sysfs_gpio::Pin::new(pin.num);
21+
try!(sysfs_gpio_pin.export());
22+
23+
// if there is a symlink root provided, create symlink
24+
if let Some(symroot) = symlink_root {
25+
for name in &pin.names {
26+
let mut dst = path::PathBuf::from(symroot);
27+
dst.push(name);
28+
try!(fs::symlink(format!("/sys/class/gpio{}", pin.num), dst));
29+
}
30+
}
31+
2332
Ok(())
2433
}

0 commit comments

Comments
 (0)