Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add serial::Read/Write implementation #9

Merged
merged 7 commits into from Jun 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
ryankurte marked this conversation as resolved.
Show resolved Hide resolved

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]);
}
}