#[macro_use] extern crate lazy_static;
extern crate atomic_option;
extern crate unix_socket;
extern crate bit_set;
pub mod protocol;
use atomic_option::AtomicOption;
use protocol::{Command, reply, PortSocket};
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::sync::atomic::Ordering;
use bit_set::BitSet;
use std::sync::{Arc, Mutex, MutexGuard};
const PORT_A_UDS_PATH: &'static str = "/var/run/tessel/port_a";
const PORT_B_UDS_PATH: &'static str = "/var/run/tessel/port_b";
const MCU_MAX_SPEED: u32 = 48e6 as u32;
const MCU_MAX_SCL_RISE_TIME_NS: f64 = 1.5e-8 as f64;
const MCU_MAGIC_DIV_FACTOR_FOR_I2C_BAUD: u8 = 2;
const MCU_MAGIC_SUBTRACT_FACTOR_FOR_I2C_BAUD: u8 = 5;
pub struct Tessel {
pub led: Vec<LED>,
}
lazy_static! {
static ref TESSEL_PORTS: AtomicOption<(Port, Port)> = AtomicOption::new(Box::new((
Port::new(PORT_A_UDS_PATH),
Port::new(PORT_B_UDS_PATH),
)));
}
impl Tessel {
pub fn new() -> Tessel {
let red_led = LED::new("red", "error");
let amber_led = LED::new("amber", "wlan");
let green_led = LED::new("green", "user1");
let blue_led = LED::new("blue", "user2");
Tessel {
led: vec![red_led, amber_led, green_led, blue_led],
}
}
pub fn ports() -> Option<(Port, Port)> {
TESSEL_PORTS.take(Ordering::Relaxed).map(|x| *x)
}
}
pub struct Port {
socket: Arc<Mutex<PortSocket>>,
}
impl Port {
pub fn new(path: &str) -> Port {
Port {
socket: Arc::new(Mutex::new(PortSocket::new(path))),
}
}
pub fn pins(&mut self) -> (Pin, Pin, Pin) {
(
Pin::new(5, self.socket.clone()),
Pin::new(6, self.socket.clone()),
Pin::new(7, self.socket.clone()),
)
}
pub fn i2c<'b>(self) -> (I2cPort<'b>, Gpio<'b>) {
let mut available = BitSet::new();
for i in 2..8 {
available.insert(i);
}
(I2cPort::new(self.socket.clone()), Gpio::new(self.socket.clone(), available))
}
}
#[allow(dead_code)]
pub struct Gpio<'a> {
socket: Arc<Mutex<PortSocket>>,
available: BitSet,
_phantom: PhantomData<&'a Port>,
}
impl<'a> Gpio<'a> {
pub fn new<'b>(socket: Arc<Mutex<PortSocket>>, available: BitSet) -> Gpio<'b> {
Gpio {
socket: socket,
available: available,
_phantom: PhantomData,
}
}
pub fn pin_select<H: PinSelect<'a>>(self, select: H) -> H::Output {
select.select(self.socket.clone())
}
}
pub trait PinSelect<'a> {
type Output;
fn validate(&self, &BitSet<usize>) -> bool;
fn select(&self, socket: Arc<Mutex<PortSocket>>) -> Self::Output;
}
impl<'a> PinSelect<'a> for usize {
type Output = Pin<'a>;
fn validate(&self, set: &BitSet<usize>) -> bool {
set.contains(*self)
}
fn select<'b>(&self, socket: Arc<Mutex<PortSocket>>) -> Self::Output {
Pin::new(*self, socket)
}
}
impl<'a> PinSelect<'a> for (usize, usize) {
type Output = (Pin<'a>, Pin<'a>);
fn validate(&self, set: &BitSet<usize>) -> bool {
set.contains(self.0) || set.contains(self.1)
}
fn select<'b>(&self, socket: Arc<Mutex<PortSocket>>) -> Self::Output {
(Pin::new(self.0, socket.clone()), Pin::new(self.1, socket))
}
}
impl<'a> PinSelect<'a> for (usize, usize, usize) {
type Output = (Pin<'a>, Pin<'a>, Pin<'a>);
fn validate(&self, set: &BitSet<usize>) -> bool {
set.contains(self.0) || set.contains(self.1) || set.contains(self.2)
}
fn select<'b>(&self, socket: Arc<Mutex<PortSocket>>) -> Self::Output {
(Pin::new(self.0, socket.clone()), Pin::new(self.1, socket.clone()), Pin::new(self.2, socket))
}
}
pub struct Pin<'a> {
index: usize,
socket: Arc<Mutex<PortSocket>>,
_phantom: PhantomData<&'a Port>,
}
impl<'a> Pin<'a> {
fn new<'b>(index: usize, socket: Arc<Mutex<PortSocket>>) -> Pin<'b> {
Pin {
index: index,
socket: socket,
_phantom: PhantomData,
}
}
pub fn output(&mut self, value: bool) -> io::Result<()> {
let mut sock = self.socket.lock().unwrap();
if value {
sock.write_command(Command::GpioHigh(self.index as u8))
} else {
sock.write_command(Command::GpioLow(self.index as u8))
}
}
pub fn high(&mut self) -> io::Result<()> {
self.output(true)
}
pub fn low(&mut self) -> io::Result<()> {
self.output(false)
}
}
pub struct I2cPort<'a> {
socket: Arc<Mutex<PortSocket>>,
_phantom: PhantomData<&'a Port>,
}
impl<'p> I2cPort<'p> {
fn new<'a>(socket: Arc<Mutex<PortSocket>>) -> I2cPort<'a> {
let mut i2c = I2cPort {
socket: socket,
_phantom: PhantomData,
};
i2c.enable(I2cPort::compute_baud(100_000));
i2c
}
fn compute_baud(frequency: u32) -> u8 {
let mut intermediate: f64 = MCU_MAX_SPEED as f64 / frequency as f64;
intermediate = intermediate - MCU_MAX_SPEED as f64 * MCU_MAX_SCL_RISE_TIME_NS;
intermediate = intermediate / MCU_MAGIC_DIV_FACTOR_FOR_I2C_BAUD as f64 -
MCU_MAGIC_SUBTRACT_FACTOR_FOR_I2C_BAUD as f64;
let low = intermediate.min(u8::max_value() as f64);
if (low as i64) < u8::min_value() as i64 {
return u8::min_value();
} else {
return low as u8;
}
}
fn enable(&mut self, baud: u8) {
let mut sock = self.socket.lock().unwrap();
sock.write_command(Command::EnableI2c { baud: baud }).unwrap();
}
fn tx(sock: &mut MutexGuard<PortSocket>, address: u8, write_buf: &[u8]) {
sock.write_command(Command::Start(address<<1)).unwrap();
sock.write_command(Command::Tx(write_buf)).unwrap();
}
fn rx(sock: &mut MutexGuard<PortSocket>, address: u8, read_buf: &mut [u8]) {
sock.write_command(Command::Start(address << 1 | 1)).unwrap();
sock.write_command(Command::Rx(read_buf.len() as u8)).unwrap();
}
fn stop(sock: &mut MutexGuard<PortSocket>) {
sock.write_command(Command::Stop).unwrap();
}
pub fn set_frequency(&mut self, frequency: u32) {
self.enable(I2cPort::compute_baud(frequency));
}
pub fn send(&mut self, address: u8, write_buf: &[u8]) {
let mut sock = self.socket.lock().unwrap();
I2cPort::tx(&mut sock, address, write_buf);
I2cPort::stop(&mut sock);
}
pub fn read(&mut self, address: u8, read_buf: &mut [u8]) -> io::Result<()> {
let mut sock = self.socket.lock().unwrap();
I2cPort::rx(&mut sock, address, read_buf);
I2cPort::stop(&mut sock);
let mut read_byte = [0];
try!(sock.read_exact(&mut read_byte));
assert_eq!(read_byte[0], reply::DATA.0);
return sock.read_exact(read_buf);
}
pub fn transfer(&mut self, address: u8, write_buf: &[u8], read_buf: &mut [u8]) -> io::Result<()> {
let mut sock = self.socket.lock().unwrap();
I2cPort::tx(&mut sock, address, write_buf);
I2cPort::rx(&mut sock, address, read_buf);
I2cPort::stop(&mut sock);
let mut read_byte = [0];
try!(sock.read_exact(&mut read_byte));
assert_eq!(read_byte[0], reply::DATA.0);
return sock.read_exact(read_buf);
}
}
pub struct LED {
file: File,
value: bool,
}
impl LED {
pub fn new(color: &'static str, kind: &'static str) -> LED {
let path = format!("/sys/devices/leds/leds/tessel:{}:{}/brightness",
color,
kind);
LED::new_with_file(File::create(path).unwrap())
}
fn new_with_file(file: File) -> LED {
let mut led = LED {
value: false,
file: file,
};
led.off().unwrap();
led
}
pub fn on(&mut self) -> Result<(), io::Error> {
self.high()
}
pub fn off(&mut self) -> Result<(), io::Error> {
self.low()
}
pub fn high(&mut self) -> Result<(), io::Error> {
self.write(true)
}
pub fn low(&mut self) -> Result<(), io::Error> {
self.write(false)
}
pub fn toggle(&mut self) -> Result<(), io::Error> {
let new_value = !self.value;
self.write(new_value)
}
pub fn read(&self) -> bool {
self.value
}
fn write(&mut self, new_value: bool) -> Result<(), io::Error> {
self.value = new_value;
let string_value = match new_value {
true => b'1',
false => b'0',
};
self.file.write_all(&[string_value])
}
}
#[cfg(test)]
mod tests {
extern crate tempfile;
use super::*;
use std::io::{Read, Seek, SeekFrom};
#[test]
fn led_writes_to_file() {
let mut tmpfile = tempfile::tempfile().unwrap();
let mut led = LED::new_with_file(tmpfile.try_clone().unwrap());
let mut buf = String::new();
tmpfile.seek(SeekFrom::Start(0)).unwrap();
tmpfile.read_to_string(&mut buf).unwrap();
assert_eq!("0", buf);
led.on().unwrap();
tmpfile.seek(SeekFrom::Start(0)).unwrap();
tmpfile.read_to_string(&mut buf).unwrap();
assert_eq!("001", buf);
}
}