Skip to content

Commit

Permalink
Merge #9
Browse files Browse the repository at this point in the history
9: Add serial::Read/Write implementation r=ryankurte a=rnestler

So this is just a quick PoC to implement #8.
@japaric Do you think it would be OK to implement it in that way? If yes I can finish it up.

Co-authored-by: Raphael Nestler <raphael.nestler@gmail.com>
Co-authored-by: Raphael Nestler <raphael.nestler@sensirion.com>
Co-authored-by: Danilo Bargen <mail@dbrgn.ch>
  • Loading branch information
4 people committed Jun 16, 2019
2 parents 92a092d + b4239f1 commit 235d9db
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Expand Up @@ -13,6 +13,12 @@ embedded-hal = { version = "0.2.0", features = ["unproven"] }
i2cdev = "0.4.1"
spidev = "0.3.0"
sysfs_gpio = "0.5.1"
serial-unix = "0.4.0"
serial-core = "0.4.0"
nb = "0.1.1"

[dev-dependencies]
openpty = "0.1.0"

[dependencies.cast]
# we don't need the `Error` implementation
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Expand Up @@ -17,6 +17,9 @@ extern crate embedded_hal as hal;
pub extern crate i2cdev;
pub extern crate spidev;
pub extern crate sysfs_gpio;
pub extern crate serial_unix;
pub extern crate serial_core;
pub extern crate nb;

use std::io::{self, Write};
use std::path::{Path, PathBuf};
Expand All @@ -27,6 +30,10 @@ use cast::{u32, u64};
use i2cdev::core::I2CDevice;
use spidev::SpidevTransfer;

mod serial;

pub use serial::Serial;

/// Empty struct that provides delay functionality on top of `thread::sleep`
pub struct Delay;

Expand Down
97 changes: 97 additions & 0 deletions src/serial.rs
@@ -0,0 +1,97 @@
//! Implementation of [`Serial`](https://docs.rs/embedded-hal/0.2.1/embedded_hal/serial/index.html)

use std::io::{ErrorKind as IoErrorKind, Read, Write};
use std::path::Path;

use nb;

use hal;
use serial_core;
use serial_unix::TTYPort;

/// Newtype around [`serial_unix::TTYPort`] that implements
/// the `embedded-hal` traits.
pub struct Serial(pub TTYPort);

impl Serial {
/// Wrapper for `serial_unix::TTYPort::open`
pub fn open(path: &Path) -> Result<Serial, serial_core::Error> {
Ok(Serial(TTYPort::open(path)?))
}
}

/// Helper to convert std::io::Error to the nb::Error
fn translate_io_errors(err: std::io::Error) -> nb::Error<IoErrorKind> {
match err.kind() {
IoErrorKind::WouldBlock | IoErrorKind::TimedOut | IoErrorKind::Interrupted => {
nb::Error::WouldBlock
}
err => nb::Error::Other(err),
}
}

impl hal::serial::Read<u8> for Serial {
type Error = IoErrorKind;

fn read(&mut self) -> nb::Result<u8, Self::Error> {
let mut buffer = [0; 1];
let bytes_read = self.0.read(&mut buffer).map_err(translate_io_errors)?;
if bytes_read == 1 {
Ok(buffer[0])
} else {
Err(nb::Error::WouldBlock)
}
}
}

impl hal::serial::Write<u8> for Serial {
type Error = IoErrorKind;

fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
self.0.write(&[word]).map_err(translate_io_errors)?;
Ok(())
}

fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.0.flush().map_err(translate_io_errors)
}
}

#[cfg(test)]
mod test {
use std::path::Path;

use hal::serial::{Read, Write};
use std::io::{Read as IoRead, Write as IoWrite};

use super::*;

fn create_pty_and_serial() -> (std::fs::File, Serial) {
let (master, _slave, name) =
openpty::openpty(None, None, None).expect("Creating pty failed");
let serial = Serial::open(Path::new(&name)).expect("Creating TTYPort failed");
(master, serial)
}

#[test]
fn test_empty_read() {
let (mut _master, mut serial) = create_pty_and_serial();
assert_eq!(Err(nb::Error::WouldBlock), serial.read());
}

#[test]
fn test_read() {
let (mut master, mut serial) = create_pty_and_serial();
master.write(&[1]).expect("Write failed");
assert_eq!(Ok(1), serial.read());
}

#[test]
fn test_write() {
let (mut master, mut serial) = create_pty_and_serial();
serial.write(2).expect("Write failed");
let mut buf = [0; 2];
assert_eq!(1, master.read(&mut buf).unwrap());
assert_eq!(buf, [2, 0]);
}
}

0 comments on commit 235d9db

Please sign in to comment.