|
| 1 | +//! Implementation of [`Serial`](https://docs.rs/embedded-hal/0.2.1/embedded_hal/serial/index.html) |
| 2 | +
|
| 3 | +use std::io::{ErrorKind as IoErrorKind, Read, Write}; |
| 4 | +use std::path::Path; |
| 5 | + |
| 6 | +use nb; |
| 7 | + |
| 8 | +use hal; |
| 9 | +use serial_core; |
| 10 | +use serial_unix::TTYPort; |
| 11 | + |
| 12 | +/// Newtype around [`serial_unix::TTYPort`] that implements |
| 13 | +/// the `embedded-hal` traits. |
| 14 | +pub struct Serial(pub TTYPort); |
| 15 | + |
| 16 | +impl Serial { |
| 17 | + /// Wrapper for `serial_unix::TTYPort::open` |
| 18 | + pub fn open(path: &Path) -> Result<Serial, serial_core::Error> { |
| 19 | + Ok(Serial(TTYPort::open(path)?)) |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +/// Helper to convert std::io::Error to the nb::Error |
| 24 | +fn translate_io_errors(err: std::io::Error) -> nb::Error<IoErrorKind> { |
| 25 | + match err.kind() { |
| 26 | + IoErrorKind::WouldBlock | IoErrorKind::TimedOut | IoErrorKind::Interrupted => { |
| 27 | + nb::Error::WouldBlock |
| 28 | + } |
| 29 | + err => nb::Error::Other(err), |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +impl hal::serial::Read<u8> for Serial { |
| 34 | + type Error = IoErrorKind; |
| 35 | + |
| 36 | + fn read(&mut self) -> nb::Result<u8, Self::Error> { |
| 37 | + let mut buffer = [0; 1]; |
| 38 | + let bytes_read = self.0.read(&mut buffer).map_err(translate_io_errors)?; |
| 39 | + if bytes_read == 1 { |
| 40 | + Ok(buffer[0]) |
| 41 | + } else { |
| 42 | + Err(nb::Error::WouldBlock) |
| 43 | + } |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +impl hal::serial::Write<u8> for Serial { |
| 48 | + type Error = IoErrorKind; |
| 49 | + |
| 50 | + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { |
| 51 | + self.0.write(&[word]).map_err(translate_io_errors)?; |
| 52 | + Ok(()) |
| 53 | + } |
| 54 | + |
| 55 | + fn flush(&mut self) -> nb::Result<(), Self::Error> { |
| 56 | + self.0.flush().map_err(translate_io_errors) |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +#[cfg(test)] |
| 61 | +mod test { |
| 62 | + use std::path::Path; |
| 63 | + |
| 64 | + use hal::serial::{Read, Write}; |
| 65 | + use std::io::{Read as IoRead, Write as IoWrite}; |
| 66 | + |
| 67 | + use super::*; |
| 68 | + |
| 69 | + fn create_pty_and_serial() -> (std::fs::File, Serial) { |
| 70 | + let (master, _slave, name) = |
| 71 | + openpty::openpty(None, None, None).expect("Creating pty failed"); |
| 72 | + let serial = Serial::open(Path::new(&name)).expect("Creating TTYPort failed"); |
| 73 | + (master, serial) |
| 74 | + } |
| 75 | + |
| 76 | + #[test] |
| 77 | + fn test_empty_read() { |
| 78 | + let (mut _master, mut serial) = create_pty_and_serial(); |
| 79 | + assert_eq!(Err(nb::Error::WouldBlock), serial.read()); |
| 80 | + } |
| 81 | + |
| 82 | + #[test] |
| 83 | + fn test_read() { |
| 84 | + let (mut master, mut serial) = create_pty_and_serial(); |
| 85 | + master.write(&[1]).expect("Write failed"); |
| 86 | + assert_eq!(Ok(1), serial.read()); |
| 87 | + } |
| 88 | + |
| 89 | + #[test] |
| 90 | + fn test_write() { |
| 91 | + let (mut master, mut serial) = create_pty_and_serial(); |
| 92 | + serial.write(2).expect("Write failed"); |
| 93 | + let mut buf = [0; 2]; |
| 94 | + assert_eq!(1, master.read(&mut buf).unwrap()); |
| 95 | + assert_eq!(buf, [2, 0]); |
| 96 | + } |
| 97 | +} |
0 commit comments