Skip to content

Commit 632a47f

Browse files
committed
Merge pull request #4 from rust-embedded/implement-unexport
unexport and poll functionality
2 parents 6571ca2 + 8c83ec3 commit 632a47f

File tree

10 files changed

+213
-24
lines changed

10 files changed

+213
-24
lines changed

README.md

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,27 @@ how you can configure your GPIOs.
5555
# the following keys:
5656
#
5757
# - `num`: Required. The GPIO number.
58+
# - `names`: Required. One or more names for the GPIO
5859
# - `direction`: Default: `"in"`. Must be either "in" or "out"
59-
# - `active_low`: Default: `false`. If set to true, the polatiry of the pin will
60+
# - `active_low`: Default: `false`. If set to true, the polarity of the pin will
6061
# be reversed.
6162
# - `export`: Default: `true`. If true, this GPIO will be automatically
6263
# exported when `gpio export-all` is run (e.g. by an init script).
63-
# - `aliases`: Additional names for the same GPIO
6464
#
6565

66-
# Basic Form
67-
[pins.reset_button]
68-
num = 73 # required
69-
direction = "in" # default: in
70-
active_low = true # default: false
71-
export = true # default: true
66+
[[pins]]
67+
num = 73 # required
68+
names = ["reset_button"] # required (may have multiple)
69+
direction = "in" # default: in
70+
active_low = false # default: false (really means invert logic)
71+
export = true # default: true
7272

73-
[pins.status_led]
73+
[[pins]]
7474
num = 37
75-
aliases = ["A27", "green_led"]
75+
names = ["status_led", "A27", "green_led"]
7676
direction = "out"
7777

78-
# Compact Form
79-
[pins]
80-
error_led = { num = 11, direction = "in", export = false}
78+
# ...
8179
```
8280

8381
## Implementation Notes

src/commands/gpio_exportall.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ pub fn main(config: &GpioConfig, opts: &GpioExportAllOptions) {
1111
None => config.get_symlink_root(),
1212
};
1313

14-
for pin in config.get_pins() {
14+
// export all pins except those for which export is set to false
15+
for pin in config.get_pins().iter().filter(|p| p.export) {
1516
if let Err(e) = export::export(pin, Some(symlink_root)) {
1617
println!("Error occurred while exporting pin: {:?}", pin);
1718
println!("{}", e);

src/commands/gpio_poll.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (C) 2016, The gpio-utils Authors
2+
3+
use options::GpioPollOptions;
4+
use config::GpioConfig;
5+
use sysfs_gpio::Edge;
6+
use std::process::exit;
7+
8+
pub fn main(config: &GpioConfig, opts: &GpioPollOptions) {
9+
let timeout = opts.timeout.unwrap_or(-1);
10+
let pin_config = match config.get_pin(&opts.pin[..]) {
11+
Some(pin) => pin,
12+
None => {
13+
println!("Unable to find config entry for pin '{}'", opts.pin);
14+
exit(1)
15+
}
16+
};
17+
let pin = pin_config.get_pin();
18+
let edge = match &opts.edge[..] {
19+
"rising" => Edge::RisingEdge,
20+
"falling" => Edge::FallingEdge,
21+
"both" => Edge::BothEdges,
22+
other => {
23+
println!("Unexpected edge value: {}", other);
24+
exit(1);
25+
}
26+
};
27+
28+
// set the pin direction
29+
pin.set_edge(edge).unwrap_or_else(|e| {
30+
println!("Error setting edge on pin: {:?}", e);
31+
exit(1);
32+
});
33+
34+
let mut poller = pin.get_poller().unwrap_or_else(|e| {
35+
println!("Error creating pin poller: {:?}", e);
36+
exit(1);
37+
});
38+
match poller.poll(timeout) {
39+
Ok(Some(value)) => {
40+
println!("{}", value);
41+
exit(0);
42+
}
43+
Ok(None) => {
44+
println!("TIMEOUT");
45+
exit(2)
46+
}
47+
Err(e) => {
48+
println!("Error on Poll: {:?}", e);
49+
exit(1);
50+
}
51+
}
52+
}

src/commands/gpio_read.rs

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

3-
use options::{GpioReadOptions};
3+
use options::GpioReadOptions;
44
use config::GpioConfig;
55
use std::process::exit;
66

src/commands/gpio_unexport.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (C) 2016, The gpio-utils Authors
2+
3+
use options::GpioUnexportOptions;
4+
use config::GpioConfig;
5+
use std::process::exit;
6+
use export;
7+
8+
pub fn main(config: &GpioConfig, opts: &GpioUnexportOptions) {
9+
let pin_config = config.get_pin(&opts.pin[..]).unwrap_or_else(|| {
10+
println!("Unable to find config entry for pin '{}'", opts.pin);
11+
exit(1)
12+
});
13+
14+
let symlink_root = match opts.symlink_root {
15+
Some(ref slr) => &slr[..],
16+
None => config.get_symlink_root(),
17+
};
18+
19+
if let Err(e) = export::unexport(pin_config, Some(symlink_root)) {
20+
println!("Error occurred while unexport pin {:?}", pin_config);
21+
println!("{}", e);
22+
exit(1);
23+
}
24+
}

src/commands/gpio_unexportall.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (C) 2016, The gpio-utils Authors
2+
3+
use options::GpioUnexportAllOptions;
4+
use config::GpioConfig;
5+
use std::process::exit;
6+
use export;
7+
8+
pub fn main(config: &GpioConfig, opts: &GpioUnexportAllOptions) {
9+
let symlink_root = match opts.symlink_root {
10+
Some(ref slr) => &slr[..],
11+
None => config.get_symlink_root(),
12+
};
13+
14+
for pin in config.get_pins().iter().filter(|p| p.export) {
15+
if let Err(e) = export::unexport(pin, Some(symlink_root)) {
16+
println!("Error occurred while exporting pin: {:?}", pin);
17+
println!("{}", e);
18+
exit(1);
19+
}
20+
}
21+
}

src/commands/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ pub mod gpio_read;
44
pub mod gpio_export;
55
pub mod gpio_exportall;
66
pub mod gpio_write;
7+
pub mod gpio_unexport;
8+
pub mod gpio_unexportall;
9+
pub mod gpio_poll;

src/export.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,45 @@ use std::io::ErrorKind;
77
use config::PinConfig;
88
use sysfs_gpio;
99

10+
/// Unexport the pin specified in the provided config
11+
///
12+
/// Unexporting a config (in this context) involves a few different
13+
/// actions:
14+
///
15+
/// 1. For each GPIO name/alias, the corresponding symlink is remvoed from
16+
/// `/var/run/gpio/<name>` (or an alternate configured symlink_root).
17+
/// 2. The GPIO pin istself is unexported (vai /sys/class/gpio/unexport)
18+
///
19+
/// If the GPIO was already unexported, this function will continue
20+
/// without an error as the desired end state is achieved.
21+
pub fn unexport(pin_config: &PinConfig,
22+
symlink_root: Option<&str>)
23+
-> Result<(), sysfs_gpio::Error> {
24+
if let Some(symroot) = symlink_root {
25+
// create symlink for each name
26+
for name in &pin_config.names {
27+
let mut dst = path::PathBuf::from(symroot);
28+
dst.push(name);
29+
try!(match fs::remove_file(dst) {
30+
Ok(_) => Ok(()),
31+
Err(ref e) if e.kind() == ErrorKind::NotFound => Ok(()),
32+
Err(e) => Err(e),
33+
});
34+
}
35+
}
36+
37+
// unexport the pin itself. On many boards, it turns out, some pins are
38+
// exported by the kernel itself but we might still be assigning names. In
39+
// those cases we will get an error here. We handle that rather than
40+
// exposing the error up the chain. (EINVAL)
41+
let pin = pin_config.get_pin();
42+
match pin.unexport() {
43+
Ok(_) => Ok(()),
44+
Err(sysfs_gpio::Error::Io(ref e)) if e.kind() == ErrorKind::InvalidInput => Ok(()),
45+
Err(e) => Err(e)
46+
}
47+
}
48+
1049
/// Export the pin specified in the provided config
1150
///
1251
/// Exporting a pin (in this context) involves, a few different

src/main.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,19 @@ fn main() {
4444
.arg(Arg::with_name("pin")
4545
.help("The pin name (or number)")
4646
.index(1)
47-
.required(true)))
47+
.required(true))
48+
.arg(Arg::with_name("timeout")
49+
.help("Timeout (in ms) for the poll operation (-1 to wait forever, default)")
50+
.takes_value(true)
51+
.short("t")
52+
.long("timeout")
53+
.required(false))
54+
.arg(Arg::with_name("edge")
55+
.help("The edge to poll on")
56+
.takes_value(true)
57+
.short("e")
58+
.long("edge")
59+
.required(false)))
4860

4961
// gpio write
5062
.subcommand(SubCommand::with_name("write")
@@ -88,15 +100,23 @@ fn main() {
88100
.arg(Arg::with_name("pin")
89101
.help("The pin name (or number)")
90102
.index(1)
91-
.required(true)))
103+
.required(true))
104+
.arg(Arg::with_name("symlink-root")
105+
.help("root directory for export symlinks")
106+
.takes_value(true)
107+
.short("r")
108+
.long("symlink-root")
109+
.required(false)))
92110

93111
// gpio unexport-all
94112
.subcommand(SubCommand::with_name("unexport-all")
95113
.about("Unexport all configured, exported GPIOs")
96-
.arg(Arg::with_name("pin")
97-
.help("The pin name (or number)")
98-
.index(1)
99-
.required(true)))
114+
.arg(Arg::with_name("symlink-root")
115+
.help("root directory for export symlinks")
116+
.takes_value(true)
117+
.short("r")
118+
.long("symlink-root")
119+
.required(false)))
100120

101121
// gpio status
102122
.subcommand(SubCommand::with_name("status")
@@ -132,7 +152,21 @@ fn main() {
132152
};
133153
gpio_read::main(&cfg, &read_options);
134154
}
135-
("poll", Some(_)) => {}
155+
("poll", Some(m)) => {
156+
let timeout = m.value_of("timeout").map(|timeout| {
157+
timeout.parse::<isize>().unwrap_or_else(|_| {
158+
println!("Unable to parse timeout value {:?} as integer", timeout);
159+
exit(1);
160+
})
161+
});
162+
let poll_options = GpioPollOptions {
163+
gpio_opts: gpio_options,
164+
edge: String::from(m.value_of("edge").unwrap_or("both")),
165+
timeout: timeout,
166+
pin: String::from(m.value_of("pin").unwrap()),
167+
};
168+
gpio_poll::main(&cfg, &poll_options);
169+
}
136170
("write", Some(m)) => {
137171
let write_options = GpioWriteOptions {
138172
gpio_opts: gpio_options,
@@ -169,8 +203,21 @@ fn main() {
169203
};
170204
gpio_exportall::main(&cfg, &exportall_options);
171205
}
172-
("unexport", Some(_)) => {}
173-
("unexport-all", Some(_)) => {}
206+
("unexport", Some(m)) => {
207+
let unexport_options = GpioUnexportOptions {
208+
gpio_opts: gpio_options,
209+
pin: String::from(m.value_of("pin").unwrap()),
210+
symlink_root: m.value_of("symlink-root").map(|slr| String::from(slr)),
211+
};
212+
gpio_unexport::main(&cfg, &unexport_options);
213+
}
214+
("unexport-all", Some(m)) => {
215+
let unexportall_options = GpioUnexportAllOptions {
216+
gpio_opts: gpio_options,
217+
symlink_root: m.value_of("symlink-root").map(|slr| String::from(slr)),
218+
};
219+
gpio_unexportall::main(&cfg, &unexportall_options);
220+
}
174221
("status", Some(_)) => {}
175222
_ => {}
176223
}

src/options.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pub struct GpioWriteOptions {
2121
#[derive(Debug)]
2222
pub struct GpioPollOptions {
2323
pub gpio_opts: GpioOptions,
24+
pub timeout: Option<isize>,
25+
pub edge: String,
2426
pub pin: String,
2527
}
2628

@@ -40,12 +42,14 @@ pub struct GpioExportAllOptions {
4042
#[derive(Debug)]
4143
pub struct GpioUnexportOptions {
4244
pub gpio_opts: GpioOptions,
45+
pub symlink_root: Option<String>,
4346
pub pin: String,
4447
}
4548

4649
#[derive(Debug)]
4750
pub struct GpioUnexportAllOptions {
4851
pub gpio_opts: GpioOptions,
52+
pub symlink_root: Option<String>,
4953
}
5054

5155
#[derive(Debug)]

0 commit comments

Comments
 (0)