diff --git a/Cargo.toml b/Cargo.toml index 66621bd..b9512ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,9 @@ async-tokio = ["gpio-cdev/async-tokio"] default = [ "gpio_cdev", "gpio_sysfs" ] [dependencies] -embedded-hal = "=1.0.0-alpha.5" +embedded-hal = "=1.0.0-alpha.6" gpio-cdev = { version = "0.5.1", optional = true } sysfs_gpio = { version = "0.6.1", optional = true } - i2cdev = "0.5.1" nb = "1" serial-core = "0.4.0" diff --git a/src/delay.rs b/src/delay.rs index 878d062..c05920e 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -2,34 +2,16 @@ //! //! [`embedded-hal`]: https://docs.rs/embedded-hal -use cast::{u32, u64}; +use cast::u64; use core::convert::Infallible; -use embedded_hal::delay::blocking::{DelayMs, DelayUs}; +use embedded_hal::delay::blocking::DelayUs; use std::thread; use std::time::Duration; /// Empty struct that provides delay functionality on top of `thread::sleep` pub struct Delay; -impl DelayUs for Delay { - type Error = Infallible; - - fn delay_us(&mut self, n: u8) -> Result<(), Self::Error> { - thread::sleep(Duration::new(0, u32(n) * 1000)); - Ok(()) - } -} - -impl DelayUs for Delay { - type Error = Infallible; - - fn delay_us(&mut self, n: u16) -> Result<(), Self::Error> { - thread::sleep(Duration::new(0, u32(n) * 1000)); - Ok(()) - } -} - -impl DelayUs for Delay { +impl DelayUs for Delay { type Error = Infallible; fn delay_us(&mut self, n: u32) -> Result<(), Self::Error> { @@ -40,51 +22,3 @@ impl DelayUs for Delay { Ok(()) } } - -impl DelayUs for Delay { - type Error = Infallible; - - fn delay_us(&mut self, n: u64) -> Result<(), Self::Error> { - let secs = n / 1_000_000; - let nsecs = ((n % 1_000_000) * 1_000) as u32; - - thread::sleep(Duration::new(secs, nsecs)); - Ok(()) - } -} - -impl DelayMs for Delay { - type Error = Infallible; - - fn delay_ms(&mut self, n: u8) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(u64(n))); - Ok(()) - } -} - -impl DelayMs for Delay { - type Error = Infallible; - - fn delay_ms(&mut self, n: u16) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(u64(n))); - Ok(()) - } -} - -impl DelayMs for Delay { - type Error = Infallible; - - fn delay_ms(&mut self, n: u32) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(u64(n))); - Ok(()) - } -} - -impl DelayMs for Delay { - type Error = Infallible; - - fn delay_ms(&mut self, n: u64) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(n)); - Ok(()) - } -} diff --git a/src/i2c.rs b/src/i2c.rs index 65d71ce..c175681 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -62,25 +62,25 @@ mod embedded_hal_impl { use i2cdev::linux::LinuxI2CMessage; impl Read for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; + type Error = I2CError; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.set_address(address)?; - self.inner.read(buffer) + self.inner.read(buffer).map_err(|err| I2CError { err }) } } impl Write for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; + type Error = I2CError; fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { self.set_address(address)?; - self.inner.write(bytes) + self.inner.write(bytes).map_err(|err| I2CError { err }) } } impl WriteRead for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; + type Error = I2CError; fn write_read( &mut self, @@ -90,12 +90,15 @@ mod embedded_hal_impl { ) -> Result<(), Self::Error> { self.set_address(address)?; let mut messages = [LinuxI2CMessage::write(bytes), LinuxI2CMessage::read(buffer)]; - self.inner.transfer(&mut messages).map(drop) + self.inner + .transfer(&mut messages) + .map(drop) + .map_err(|err| I2CError { err }) } } impl Transactional for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; + type Error = I2CError; fn exec( &mut self, @@ -113,7 +116,33 @@ mod embedded_hal_impl { .collect(); self.set_address(address)?; - self.inner.transfer(&mut messages).map(drop) + self.inner + .transfer(&mut messages) + .map(drop) + .map_err(|err| I2CError { err }) + } + } +} + +/// Error type wrapping [LinuxI2CError](i2cdev::linux::LinuxI2CError) to implement [embedded_hal::i2c::ErrorKind] +#[derive(Debug)] +pub struct I2CError { + err: i2cdev::linux::LinuxI2CError, +} + +impl From for I2CError { + fn from(err: i2cdev::linux::LinuxI2CError) -> Self { + Self { err } + } +} + +impl embedded_hal::i2c::Error for I2CError { + fn kind(&self) -> embedded_hal::i2c::ErrorKind { + use embedded_hal::i2c::ErrorKind::*; + match &self.err { + // i2cdev::linux::LinuxI2CError::Nix(_) => todo!(), + // i2cdev::linux::LinuxI2CError::Io(_) => todo!(), + _ => Other, } } } diff --git a/src/lib.rs b/src/lib.rs index ffb3e87..d315355 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ mod spi; mod timer; pub use crate::delay::Delay; -pub use crate::i2c::I2cdev; -pub use crate::serial::Serial; -pub use crate::spi::Spidev; +pub use crate::i2c::{I2CError, I2cdev}; +pub use crate::serial::{Serial, SerialError}; +pub use crate::spi::{SPIError, Spidev}; pub use crate::timer::SysTimer; diff --git a/src/serial.rs b/src/serial.rs index 46bb61d..ac8b47b 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -20,17 +20,17 @@ impl Serial { } /// Helper to convert std::io::Error to the nb::Error -fn translate_io_errors(err: std::io::Error) -> nb::Error { +fn translate_io_errors(err: std::io::Error) -> nb::Error { match err.kind() { IoErrorKind::WouldBlock | IoErrorKind::TimedOut | IoErrorKind::Interrupted => { nb::Error::WouldBlock } - err => nb::Error::Other(err), + err => nb::Error::Other(SerialError { err }), } } impl embedded_hal::serial::nb::Read for Serial { - type Error = IoErrorKind; + type Error = SerialError; fn read(&mut self) -> nb::Result { let mut buffer = [0; 1]; @@ -44,7 +44,7 @@ impl embedded_hal::serial::nb::Read for Serial { } impl embedded_hal::serial::nb::Write for Serial { - type Error = IoErrorKind; + type Error = SerialError; fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { self.0.write(&[word]).map_err(translate_io_errors)?; @@ -56,6 +56,62 @@ impl embedded_hal::serial::nb::Write for Serial { } } +/// Error type wrapping [io::ErrorKind](IoErrorKind) to implement [embedded_hal::serial::ErrorKind] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct SerialError { + err: IoErrorKind, +} + +impl embedded_hal::serial::Error for SerialError { + fn kind(&self) -> embedded_hal::serial::ErrorKind { + use embedded_hal::serial::ErrorKind::*; + match &self.err { + // IoErrorKind::NotFound => todo!(), + // IoErrorKind::PermissionDenied => todo!(), + // IoErrorKind::ConnectionRefused => todo!(), + // IoErrorKind::ConnectionReset => todo!(), + // IoErrorKind::HostUnreachable => todo!(), + // IoErrorKind::NetworkUnreachable => todo!(), + // IoErrorKind::ConnectionAborted => todo!(), + // IoErrorKind::NotConnected => todo!(), + // IoErrorKind::AddrInUse => todo!(), + // IoErrorKind::AddrNotAvailable => todo!(), + // IoErrorKind::NetworkDown => todo!(), + // IoErrorKind::BrokenPipe => todo!(), + // IoErrorKind::AlreadyExists => todo!(), + // IoErrorKind::WouldBlock => todo!(), + // IoErrorKind::NotADirectory => todo!(), + // IoErrorKind::IsADirectory => todo!(), + // IoErrorKind::DirectoryNotEmpty => todo!(), + // IoErrorKind::ReadOnlyFilesystem => todo!(), + // IoErrorKind::FilesystemLoop => todo!(), + // IoErrorKind::StaleNetworkFileHandle => todo!(), + // IoErrorKind::InvalidInput => todo!(), + // IoErrorKind::InvalidData => todo!(), + // IoErrorKind::TimedOut => todo!(), + // IoErrorKind::WriteZero => todo!(), + // IoErrorKind::StorageFull => todo!(), + // IoErrorKind::NotSeekable => todo!(), + // IoErrorKind::FilesystemQuotaExceeded => todo!(), + // IoErrorKind::FileTooLarge => todo!(), + // IoErrorKind::ResourceBusy => todo!(), + // IoErrorKind::ExecutableFileBusy => todo!(), + // IoErrorKind::Deadlock => todo!(), + // IoErrorKind::CrossesDevices => todo!(), + // IoErrorKind::TooManyLinks => todo!(), + // IoErrorKind::FilenameTooLong => todo!(), + // IoErrorKind::ArgumentListTooLong => todo!(), + // IoErrorKind::Interrupted => todo!(), + // IoErrorKind::Unsupported => todo!(), + // IoErrorKind::UnexpectedEof => todo!(), + // IoErrorKind::OutOfMemory => todo!(), + // IoErrorKind::Other => todo!(), + // IoErrorKind::Uncategorized => todo!(), + _ => Other, + } + } +} + #[cfg(test)] mod test { use std::path::Path; diff --git a/src/spi.rs b/src/spi.rs index f5f1faa..8492c0c 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -16,11 +16,11 @@ impl Spidev { /// See [`spidev::Spidev::open`][0] for details. /// /// [0]: https://docs.rs/spidev/0.5.0/spidev/struct.Spidev.html#method.open - pub fn open

(path: P) -> io::Result + pub fn open

(path: P) -> Result where P: AsRef, { - spidev::Spidev::open(path).map(Spidev) + spidev::Spidev::open(path).map(Spidev).map_err(|e| e.into()) } } @@ -40,31 +40,44 @@ impl ops::DerefMut for Spidev { mod embedded_hal_impl { use super::*; - use embedded_hal::spi::blocking::{Operation as SpiOperation, Transactional, Transfer, Write}; + use embedded_hal::spi::blocking::{ + Operation as SpiOperation, Transactional, Transfer, TransferInplace, Write, + }; use spidev::SpidevTransfer; use std::io::Write as _; impl Transfer for Spidev { - type Error = io::Error; + type Error = SPIError; - fn transfer<'b>(&mut self, buffer: &'b mut [u8]) -> io::Result<()> { + fn transfer<'b>(&mut self, read: &'b mut [u8], write: &[u8]) -> Result<(), Self::Error> { + self.0 + .transfer(&mut SpidevTransfer::read_write(&write, read)) + .map_err(|err| SPIError { err }) + } + } + + impl TransferInplace for Spidev { + type Error = SPIError; + + fn transfer_inplace<'b>(&mut self, buffer: &'b mut [u8]) -> Result<(), Self::Error> { let tx = buffer.to_owned(); self.0 .transfer(&mut SpidevTransfer::read_write(&tx, buffer)) + .map_err(|err| SPIError { err }) } } impl Write for Spidev { - type Error = io::Error; + type Error = SPIError; - fn write(&mut self, buffer: &[u8]) -> io::Result<()> { - self.0.write_all(buffer) + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.0.write_all(buffer).map_err(|err| SPIError { err }) } } /// Transactional implementation batches SPI operations into a single transaction impl Transactional for Spidev { - type Error = io::Error; + type Error = SPIError; fn exec<'a>(&mut self, operations: &mut [SpiOperation<'a, u8>]) -> Result<(), Self::Error> { // Map types from generic to linux objects @@ -72,8 +85,10 @@ mod embedded_hal_impl { .iter_mut() .map(|a| { match a { + SpiOperation::Read(w) => SpidevTransfer::read(w), SpiOperation::Write(w) => SpidevTransfer::write(w), - SpiOperation::Transfer(r) => { + SpiOperation::Transfer(r, w) => SpidevTransfer::read_write(w, r), + SpiOperation::TransferInplace(r) => { // Clone read to write pointer // SPIdev is okay with having w == r but this is tricky to achieve in safe rust let w = unsafe { @@ -88,7 +103,71 @@ mod embedded_hal_impl { .collect(); // Execute transfer - self.0.transfer_multiple(&mut messages) + self.0 + .transfer_multiple(&mut messages) + .map_err(|err| SPIError { err }) + } + } +} + +/// Error type wrapping [io::Error](io::Error) to implement [embedded_hal::spi::ErrorKind] +#[derive(Debug)] +pub struct SPIError { + err: io::Error, +} + +impl From for SPIError { + fn from(err: io::Error) -> Self { + Self { err } + } +} + +impl embedded_hal::spi::Error for SPIError { + fn kind(&self) -> embedded_hal::spi::ErrorKind { + use embedded_hal::spi::ErrorKind::*; + match self.err.kind() { + // io::ErrorKind::NotFound => todo!(), + // io::ErrorKind::PermissionDenied => todo!(), + // io::ErrorKind::ConnectionRefused => todo!(), + // io::ErrorKind::ConnectionReset => todo!(), + // io::ErrorKind::HostUnreachable => todo!(), + // io::ErrorKind::NetworkUnreachable => todo!(), + // io::ErrorKind::ConnectionAborted => todo!(), + // io::ErrorKind::NotConnected => todo!(), + // io::ErrorKind::AddrInUse => todo!(), + // io::ErrorKind::AddrNotAvailable => todo!(), + // io::ErrorKind::NetworkDown => todo!(), + // io::ErrorKind::BrokenPipe => todo!(), + // io::ErrorKind::AlreadyExists => todo!(), + // io::ErrorKind::WouldBlock => todo!(), + // io::ErrorKind::NotADirectory => todo!(), + // io::ErrorKind::IsADirectory => todo!(), + // io::ErrorKind::DirectoryNotEmpty => todo!(), + // io::ErrorKind::ReadOnlyFilesystem => todo!(), + // io::ErrorKind::FilesystemLoop => todo!(), + // io::ErrorKind::StaleNetworkFileHandle => todo!(), + // io::ErrorKind::InvalidInput => todo!(), + // io::ErrorKind::InvalidData => todo!(), + // io::ErrorKind::TimedOut => todo!(), + // io::ErrorKind::WriteZero => todo!(), + // io::ErrorKind::StorageFull => todo!(), + // io::ErrorKind::NotSeekable => todo!(), + // io::ErrorKind::FilesystemQuotaExceeded => todo!(), + // io::ErrorKind::FileTooLarge => todo!(), + // io::ErrorKind::ResourceBusy => todo!(), + // io::ErrorKind::ExecutableFileBusy => todo!(), + // io::ErrorKind::Deadlock => todo!(), + // io::ErrorKind::CrossesDevices => todo!(), + // io::ErrorKind::TooManyLinks => todo!(), + // io::ErrorKind::FilenameTooLong => todo!(), + // io::ErrorKind::ArgumentListTooLong => todo!(), + // io::ErrorKind::Interrupted => todo!(), + // io::ErrorKind::Unsupported => todo!(), + // io::ErrorKind::UnexpectedEof => todo!(), + // io::ErrorKind::OutOfMemory => todo!(), + // io::ErrorKind::Other => todo!(), + // io::ErrorKind::Uncategorized => todo!(), + _ => Other, } } }