#![allow(dead_code)]
use std::error;
use std::fmt;
use std::io;
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::result;
use std::thread::sleep;
use std::time::Duration;
use std::ptr;
use libc;
use num::FromPrimitive;
use system::System;
const GPIO_MEM_SIZE: usize = 164; const GPIO_MAX_PINS: u8 = 54; const GPIO_OFFSET_GPFSEL: usize = 0; const GPIO_OFFSET_GPSET: usize = 7;
const GPIO_OFFSET_GPCLR: usize = 10;
const GPIO_OFFSET_GPLEV: usize = 13;
const GPIO_OFFSET_GPPUD: usize = 37;
const GPIO_OFFSET_GPPUDCLK: usize = 38;
quick_error! {
#[derive(Debug)]
pub enum Error {
InvalidPin(pin: u8) { description("invalid GPIO pin number") }
UnknownMode(mode: u8) { description("unknown mode") }
UnknownSoC { description("unknown Raspberry Pi model or SoC") }
DevGPIOMemNotFound { description("/dev/gpiomem not found") }
DevGPIOMemPermissionDenied { description("/dev/gpiomem insufficient permissions") }
DevGPIOMemIOError(err: io::Error) {
description(err.description())
display("/dev/gpiomem IO error ({})", error::Error::description(err))
cause(err)
}
DevGPIOMemMapFailed { description("/dev/gpiomem map failed") }
DevMemNotFound { description("/dev/mem not found") }
DevMemPermissionDenied { description("/dev/mem insufficient permissions") }
DevMemIOError(err: io::Error) {
description(err.description())
display("/dev/mem IO error ({})", error::Error::description(err))
cause(err)
}
DevMemMapFailed { description("/dev/mem map failed") }
NotInitialized { description("not initialized") }
}
}
pub type Result<T> = result::Result<T, Error>;
struct GPIOMem {
mapped: bool,
mem_ptr: *mut u32,
}
impl GPIOMem {
pub fn new() -> GPIOMem {
GPIOMem {
mapped: false,
mem_ptr: ptr::null_mut(),
}
}
pub fn open(&mut self) -> Result<()> {
if self.mapped {
return Ok(());
}
self.mem_ptr = match self.map_devgpiomem() {
Ok(ptr) => ptr,
Err(e @ Error::DevGPIOMemPermissionDenied) => {
return Err(e);
}
Err(_) => {
match self.map_devmem() {
Ok(ptr) => ptr,
Err(e) => {
return Err(e);
}
}
}
};
self.mapped = true;
Ok(())
}
fn map_devgpiomem(&mut self) -> Result<*mut u32> {
let gpiomem_file = match OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_SYNC)
.open("/dev/gpiomem") {
Err(e) => {
match e.kind() {
io::ErrorKind::NotFound => return Err(Error::DevGPIOMemNotFound),
io::ErrorKind::PermissionDenied => {
return Err(Error::DevGPIOMemPermissionDenied)
}
_ => return Err(Error::DevGPIOMemIOError(e)),
}
}
Ok(file) => file,
};
let gpiomem_ptr = unsafe {
libc::mmap(ptr::null_mut(),
GPIO_MEM_SIZE,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
gpiomem_file.as_raw_fd(),
0)
};
if gpiomem_ptr == libc::MAP_FAILED {
return Err(Error::DevGPIOMemMapFailed);
}
Ok(gpiomem_ptr as *mut u32)
}
fn map_devmem(&mut self) -> Result<*mut u32> {
let system = match System::new() {
Ok(s) => s,
Err(_) => return Err(Error::UnknownSoC),
};
let mem_file = match OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_SYNC)
.open("/dev/mem") {
Err(e) => {
match e.kind() {
io::ErrorKind::NotFound => return Err(Error::DevMemNotFound),
io::ErrorKind::PermissionDenied => return Err(Error::DevMemPermissionDenied),
_ => return Err(Error::DevMemIOError(e)),
}
}
Ok(file) => file,
};
let mem_ptr = unsafe {
libc::mmap(ptr::null_mut(),
GPIO_MEM_SIZE,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
mem_file.as_raw_fd(),
(system.peripheral_base + system.gpio_offset) as libc::off_t)
};
if mem_ptr == libc::MAP_FAILED {
return Err(Error::DevMemMapFailed);
}
Ok(mem_ptr as *mut u32)
}
pub fn close(&mut self) {
if !self.mapped {
return;
}
unsafe {
libc::munmap(self.mem_ptr as *mut libc::c_void,
GPIO_MEM_SIZE as libc::size_t);
}
self.mapped = false;
}
pub fn read(&mut self, offset: usize) -> u32 {
if !self.mapped || offset >= GPIO_MEM_SIZE {
return 0;
}
unsafe { ptr::read_volatile(self.mem_ptr.offset(offset as isize)) }
}
pub fn write(&self, offset: usize, value: u32) {
if !self.mapped || offset >= GPIO_MEM_SIZE {
return;
}
unsafe {
ptr::write_volatile(self.mem_ptr.offset(offset as isize), value);
}
}
}
impl Drop for GPIOMem {
fn drop(&mut self) {
self.close();
}
}
unsafe impl Send for GPIOMem {}
enum_from_primitive! {
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011,
Alt5 = 0b010,
}
}
impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Mode::Input => write!(f, "In"),
Mode::Output => write!(f, "Out"),
Mode::Alt0 => write!(f, "Alt0"),
Mode::Alt1 => write!(f, "Alt1"),
Mode::Alt2 => write!(f, "Alt2"),
Mode::Alt3 => write!(f, "Alt3"),
Mode::Alt4 => write!(f, "Alt4"),
Mode::Alt5 => write!(f, "Alt5"),
}
}
}
enum_from_primitive! {
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Level {
Low = 0,
High = 1,
}
}
impl fmt::Display for Level {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Level::Low => write!(f, "Low"),
Level::High => write!(f, "High"),
}
}
}
enum_from_primitive! {
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PullUpDown {
Off = 0b00,
PullDown = 0b01,
PullUp = 0b10,
}
}
impl fmt::Display for PullUpDown {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
PullUpDown::Off => write!(f, "Off"),
PullUpDown::PullDown => write!(f, "PullDown"),
PullUpDown::PullUp => write!(f, "PullUp"),
}
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
struct PinState {
pin: u8,
mode: Mode,
changed: bool,
}
impl PinState {
fn new(pin: u8, mode: Mode, changed: bool) -> PinState {
PinState {
pin: pin,
mode: mode,
changed: changed,
}
}
}
pub struct GPIO {
initialized: bool,
gpio_mem: GPIOMem,
orig_pin_state: Vec<PinState>,
}
impl GPIO {
pub fn new() -> Result<GPIO> {
let mut gpio = GPIO {
initialized: true,
gpio_mem: GPIOMem::new(),
orig_pin_state: Vec::with_capacity(GPIO_MAX_PINS as usize),
};
try!(gpio.gpio_mem.open());
for n in 0..GPIO_MAX_PINS {
match gpio.mode(n) {
Ok(mode) => gpio.orig_pin_state.push(PinState::new(n, mode, false)),
Err(e) => return Err(e),
}
}
Ok(gpio)
}
pub fn cleanup(&mut self) {
if self.initialized {
for pin_state in &self.orig_pin_state.clone() {
if pin_state.changed {
self.set_mode(pin_state.pin, pin_state.mode);
}
}
self.gpio_mem.close();
self.initialized = false;
}
}
pub fn mode(&mut self, pin: u8) -> Result<Mode> {
if !self.initialized {
return Err(Error::NotInitialized);
}
if pin >= GPIO_MAX_PINS {
return Err(Error::InvalidPin(pin));
}
let reg_addr: usize = GPIO_OFFSET_GPFSEL + (pin / 10) as usize;
let reg_value = self.gpio_mem.read(reg_addr);
let mode_value = (reg_value >> ((pin % 10) * 3)) & 0b111;
if let Some(mode) = Mode::from_u32(mode_value) {
Ok(mode)
} else {
Err(Error::UnknownMode(mode_value as u8))
}
}
pub fn set_mode(&mut self, pin: u8, mode: Mode) {
if !self.initialized || (pin >= GPIO_MAX_PINS) {
return;
}
if let Some(pin_state) = self.orig_pin_state.get_mut(pin as usize) {
if pin_state.mode != mode {
pin_state.changed = true;
}
}
let reg_addr: usize = GPIO_OFFSET_GPFSEL + (pin / 10) as usize;
let reg_value = self.gpio_mem.read(reg_addr);
self.gpio_mem.write(reg_addr,
(reg_value & !(0b111 << ((pin % 10) * 3))) |
((mode as u32 & 0b111) << ((pin % 10) * 3)));
}
pub fn read(&mut self, pin: u8) -> Result<Level> {
if !self.initialized {
return Err(Error::NotInitialized);
}
if pin >= GPIO_MAX_PINS {
return Err(Error::InvalidPin(pin));
}
let reg_addr: usize = GPIO_OFFSET_GPLEV + (pin / 32) as usize;
let reg_value = self.gpio_mem.read(reg_addr);
if (reg_value & (1 << (pin % 32))) > 0 {
Ok(Level::High)
} else {
Ok(Level::Low)
}
}
pub fn write(&self, pin: u8, level: Level) {
if !self.initialized || (pin >= GPIO_MAX_PINS) {
return;
}
let reg_addr: usize = match level {
Level::Low => GPIO_OFFSET_GPCLR + (pin / 32) as usize,
Level::High => GPIO_OFFSET_GPSET + (pin / 32) as usize,
};
self.gpio_mem.write(reg_addr, 1 << (pin % 32));
}
pub fn set_pullupdown(&mut self, pin: u8, pud: PullUpDown) {
if !self.initialized || (pin >= GPIO_MAX_PINS) {
return;
}
let reg_addr: usize = GPIO_OFFSET_GPPUDCLK + (pin / 32) as usize;
let reg_value = self.gpio_mem.read(GPIO_OFFSET_GPPUD);
self.gpio_mem.write(GPIO_OFFSET_GPPUD,
(reg_value & !0b11) | ((pud as u32) & 0b11));
sleep(Duration::new(0, 20000));
self.gpio_mem.write(reg_addr, 1 << (pin % 32));
sleep(Duration::new(0, 20000));
let reg_value = self.gpio_mem.read(GPIO_OFFSET_GPPUD);
self.gpio_mem.write(GPIO_OFFSET_GPPUD, (reg_value & !0b11));
self.gpio_mem.write(reg_addr, 0 << (pin % 32));
}
}
impl Drop for GPIO {
fn drop(&mut self) {
self.cleanup();
}
}