sysfs_pwm/
lib.rs

1// Copyright 2016, Paul Osborne <osbpau@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option.  This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// Portions of this implementation are based on work by Nat Pryce:
10// https://github.com/npryce/rusty-pi/blob/master/src/pi/gpio.rs
11
12#![crate_type = "lib"]
13#![crate_name = "sysfs_pwm"]
14
15//! PWM access under Linux using the PWM sysfs interface
16
17use std::io::prelude::*;
18use std::os::unix::prelude::*;
19use std::fs::File;
20use std::fs;
21use std::fs::OpenOptions;
22use std::str::FromStr;
23
24mod error;
25pub use error::Error;
26
27#[derive(Debug)]
28pub struct PwmChip {
29    pub number: u32,
30}
31
32#[derive(Debug)]
33pub struct Pwm {
34    chip: PwmChip,
35    number: u32,
36}
37
38#[derive(Debug)]
39pub enum Polarity {
40    Normal,
41    Inverse,
42}
43
44pub type Result<T> = ::std::result::Result<T, error::Error>;
45
46/// Open the specified entry name as a writable file
47fn pwm_file_wo(chip: &PwmChip, pin: u32, name: &str) -> Result<File> {
48    let f = try!(OpenOptions::new().write(true)
49                 .open(format!("/sys/class/pwm/pwmchip{}/pwm{}/{}",
50                               chip.number,
51                               pin,
52                               name)));
53    Ok(f)
54}
55
56/// Open the specified entry name as a readable file
57fn pwm_file_ro(chip: &PwmChip, pin: u32, name: &str) -> Result<File> {
58    let f = try!(File::open(format!("/sys/class/pwm/pwmchip{}/pwm{}/{}", chip.number, pin, name)));
59    Ok(f)
60}
61
62/// Get the u32 value from the given entry
63fn pwm_file_parse<T: FromStr>(chip: &PwmChip, pin: u32, name: &str) -> Result<T> {
64    let mut s = String::with_capacity(10);
65    let mut f = try!(pwm_file_ro(chip, pin, name));
66    try!(f.read_to_string(&mut s));
67    match s.trim().parse::<T>() {
68        Ok(r) => Ok(r),
69        Err(_) => Err(Error::Unexpected(format!("Unexpeted value file contents: {:?}", s))),
70    }
71}
72
73
74impl PwmChip {
75    pub fn new(number: u32) -> Result<PwmChip> {
76        try!(fs::metadata(&format!("/sys/class/pwm/pwmchip{}", number)));
77        Ok(PwmChip { number: number })
78    }
79
80    pub fn count(&self) -> Result<u32> {
81        let npwm_path = format!("/sys/class/pwm/pwmchip{}/npwm", self.number);
82        let mut npwm_file = try!(File::open(&npwm_path));
83        let mut s = String::new();
84        try!(npwm_file.read_to_string(&mut s));
85        match s.parse::<u32>() {
86            Ok(n) => Ok(n),
87            Err(_) => Err(Error::Unexpected(format!("Unexpected npwm contents: {:?}", s))),
88        }
89    }
90
91    pub fn export(&self, number: u32) -> Result<()> {
92        // only export if not already exported
93        if let Err(_) = fs::metadata(&format!("/sys/class/pwm/pwmchip{}/pwm{}",
94                                              self.number,
95                                              number)) {
96            let path = format!("/sys/class/pwm/pwmchip{}/export", self.number);
97            let mut export_file = try!(File::create(&path));
98            let _ = export_file.write_all(format!("{}", number).as_bytes());
99        }
100        Ok(())
101    }
102
103    pub fn unexport(&self, number: u32) -> Result<()> {
104        if let Ok(_) = fs::metadata(&format!("/sys/class/pwm/pwmchip{}/pwm{}",
105                                             self.number,
106                                             number)) {
107            let path = format!("/sys/class/pwm/pwmchip{}/unexport", self.number);
108            let mut export_file = try!(File::create(&path));
109            let _ = export_file.write_all(format!("{}", number).as_bytes());
110        }
111        Ok(())
112    }
113}
114
115impl Pwm {
116    /// Create a new Pwm wiht the provided chip/number
117    ///
118    /// This function does not export the Pwm pin
119    pub fn new(chip: u32, number: u32) -> Result<Pwm> {
120        let chip: PwmChip = try!(PwmChip::new(chip));
121        Ok(Pwm {
122            chip: chip,
123            number: number,
124        })
125    }
126
127    /// Run a closure with the GPIO exported
128    #[inline]
129    pub fn with_exported<F>(&self, closure: F) -> Result<()>
130        where F: FnOnce() -> Result<()>
131    {
132        try!(self.export());
133        match closure() {
134            Ok(()) => self.unexport(),
135            Err(_) => self.unexport(),
136        }
137    }
138
139    /// Export the Pwm for use
140    pub fn export(&self) -> Result<()> {
141        self.chip.export(self.number)
142    }
143
144    /// Unexport the PWM
145    pub fn unexport(&self) -> Result<()> {
146        self.chip.unexport(self.number)
147    }
148
149    /// Enable/Disable the PWM Signal
150    pub fn enable(&self, enable: bool) -> Result<()> {
151        let mut enable_file = try!(pwm_file_wo(&self.chip, self.number, "enable"));
152        let contents = if enable {
153            "1"
154        } else {
155            "0"
156        };
157        try!(enable_file.write_all(contents.as_bytes()));
158        Ok(())
159    }
160
161    /// Get the currently configured duty_cycle in nanoseconds
162    pub fn get_duty_cycle_ns(&self) -> Result<u32> {
163        pwm_file_parse::<u32>(&self.chip, self.number, "duty_cycle")
164    }
165
166    /// The active time of the PWM signal
167    ///
168    /// Value is in nanoseconds and must be less than the period.
169    pub fn set_duty_cycle_ns(&self, duty_cycle_ns: u32) -> Result<()> {
170        // we'll just let the kernel do the validation
171        let mut duty_cycle_file = try!(pwm_file_wo(&self.chip, self.number, "duty_cycle"));
172        try!(duty_cycle_file.write_all(format!("{}", duty_cycle_ns).as_bytes()));
173        Ok(())
174    }
175
176    /// Get the currently configured period in nanoseconds
177    pub fn get_period_ns(&self) -> Result<u32> {
178        pwm_file_parse::<u32>(&self.chip, self.number, "period")
179    }
180
181    /// The period of the PWM signal in Nanoseconds
182    pub fn set_period_ns(&self, period_ns: u32) -> Result<()> {
183        let mut period_file = try!(pwm_file_wo(&self.chip, self.number, "period"));
184        try!(period_file.write_all(format!("{}", period_ns).as_bytes()));
185        Ok(())
186    }
187}