diff --git a/.github/bors.toml b/.github/bors.toml index ca42be0..64eb89c 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -1,4 +1,8 @@ block_labels = ["needs-decision"] delete_merged_branches = true required_approvals = 1 -status = ["continuous-integration/travis-ci/push"] +status = [ + "CI (stable, x86_64-unknown-linux-gnu)", + "CI (stable, armv7-unknown-linux-gnueabihf)", + "CI (1.46.0, x86_64-unknown-linux-gnu)", +] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4d5469e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request: + +name: Continuous integration + +jobs: + ci-linux: + name: CI + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # All generated code should be running on stable now + rust: [stable] + + # The default target we're compiling on and for + TARGET: [x86_64-unknown-linux-gnu, armv7-unknown-linux-gnueabihf] + + include: + # Test MSRV + - rust: 1.46.0 + TARGET: x86_64-unknown-linux-gnu + + # Test nightly but don't fail + - rust: nightly + experimental: true + TARGET: x86_64-unknown-linux-gnu + + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: ${{ matrix.TARGET }} + override: true + + - name: Install armv7 libraries + if: ${{ matrix.TARGET == 'armv7-unknown-linux-gnueabihf' }} + run: sudo apt-get install -y libc6-armhf-cross libc6-dev-armhf-cross gcc-arm-linux-gnueabihf + + - uses: actions-rs/cargo@v1 + with: + command: build + args: --target=${{ matrix.TARGET }} + + - uses: actions-rs/cargo@v1 + if: ${{ matrix.TARGET == 'x86_64-unknown-linux-gnu' }} + with: + command: test + args: --target=${{ matrix.TARGET }} diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 0000000..72d9a10 --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,20 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request: + +name: Clippy check +jobs: + clippy_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.55.0 + override: true + components: clippy + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml new file mode 100644 index 0000000..9a55c00 --- /dev/null +++ b/.github/workflows/rustfmt.yml @@ -0,0 +1,23 @@ +on: + push: + branches: [ staging, trying, master ] + pull_request: + +name: Code formatting check + +jobs: + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 062bb3a..0000000 --- a/.travis.yml +++ /dev/null @@ -1,78 +0,0 @@ -language: rust - - - -matrix: - include: - - env: TARGET=armv7-unknown-linux-gnueabihf - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - addons: - apt: - packages: - - libc6-armhf-cross - - libc6-dev-armhf-cross - - gcc-arm-linux-gnueabihf - - - env: TARGET=x86_64-unknown-linux-gnu - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=armv7-unknown-linux-gnueabihf - rust: stable - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - addons: - apt: - packages: - - libc6-armhf-cross - - libc6-dev-armhf-cross - - gcc-arm-linux-gnueabihf - - - - env: TARGET=x86_64-unknown-linux-gnu - rust: stable - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - # MSRV - - env: TARGET=armv7-unknown-linux-gnueabihf - rust: 1.36.0 - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - addons: - apt: - packages: - - libc6-armhf-cross - - libc6-dev-armhf-cross - - gcc-arm-linux-gnueabihf - - # MSRV - - env: TARGET=x86_64-unknown-linux-gnu - rust: 1.36.0 - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - -before_install: - - set -e - - rustup self update - -install: - - bash ci/install.sh - -script: - - bash ci/script.sh - -after_script: set +e - -cache: cargo -before_cache: - # Travis can't cache files that are not readable by "others" - - chmod -R a+r $HOME/.cargo - -branches: - only: - - master - - trying - - staging - -notifications: - email: - on_success: never diff --git a/CHANGELOG.md b/CHANGELOG.md index 10d342e..d4e978e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] + +## [v0.3.1] - 2021-09-27 +### Added + +- Added implementation of transactional SPI and I2C traits. +- `CountDown` implementation for `SysTimer`. +- `Default` implementation for `SysTimer`. + ### Changed -- Set default features to build both sysfs and cdev pin types -- Removed `Pin` export, use `CdevPin` or `SysfsPin` +- Set default features to build both sysfs and cdev pin types. +- Removed `Pin` export, use `CdevPin` or `SysfsPin`. +- Updated `embedded-hal` to version `0.2.6`. +- Updated `nb` to version `0.1.3`. +- Updated `gpio-cdev` to version `0.5`. +- Updated `i2cdev` to version `0.5`. +- Updated `spidev` to version `0.5`. +- Updated `sysfs-gpio` to version `0.6`. +- Updated `cast` to version `0.3`. + +### Fixed + +- Modified `OutputPin` behavior for active-low pins to match `InputPin` behavior. ## [v0.3.0] - 2019-11-25 @@ -41,7 +60,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added -- implementation of the unproven `embedded_hal::::digital::InputPin` trait. +- implementation of the unproven `embedded_hal::::digital::InputPin` trait. ## [v0.2.0] - 2018-05-14 @@ -62,8 +81,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/japaric/linux-embedded-hal/compare/v0.2.1...HEAD -[v0.2.2]: https://github.com/japaric/linux-embedded-hal/compare/v0.2.1...v0.2.2 +[Unreleased]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.3.1...HEAD +[v0.3.1]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.3.0...v0.3.1 +[v0.3.0]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.2.2...v0.3.0 +[v0.2.2]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.2.1...v0.2.2 [v0.2.1]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.2.0...v0.2.1 [v0.2.0]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.1.1...v0.2.0 -[v0.1.1]: https://github.com/japaric/linux-embedded-hal/compare/v0.1.0...v0.1.1 +[v0.1.1]: https://github.com/rust-embedded/linux-embedded-hal/compare/v0.1.0...v0.1.1 diff --git a/Cargo.toml b/Cargo.toml index 2494ff9..83752b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,16 @@ [package] -authors = ["Jorge Aparicio "] +authors = [ + "Jorge Aparicio ", + "The Embedded Linux Team ", +] categories = ["embedded", "hardware-support"] description = "Implementation of the `embedded-hal` traits for Linux devices" keywords = ["Linux", "hal"] license = "MIT OR Apache-2.0" name = "linux-embedded-hal" -repository = "https://github.com/japaric/linux-embedded-hal" -version = "0.3.0" +repository = "https://github.com/rust-embedded/linux-embedded-hal" +version = "0.3.1" +readme = "README.md" [features] gpio_sysfs = ["sysfs_gpio"] @@ -15,15 +19,15 @@ gpio_cdev = ["gpio-cdev"] default = [ "gpio_cdev", "gpio_sysfs" ] [dependencies] -embedded-hal = { version = "0.2.3", features = ["unproven"] } -gpio-cdev = { version = "0.3", optional = true } -sysfs_gpio = { version = "0.5", optional = true } +embedded-hal = { version = "0.2.6", features = ["unproven"] } +gpio-cdev = { version = "0.5", optional = true } +sysfs_gpio = { version = "0.6", optional = true } -i2cdev = "0.4.3" -nb = "0.1.1" +i2cdev = "0.5" +nb = "0.1.3" serial-core = "0.4.0" serial-unix = "0.4.0" -spidev = "0.4" +spidev = "0.5" void = "1" [dev-dependencies] @@ -32,4 +36,4 @@ openpty = "0.1.0" [dependencies.cast] # we don't need the `Error` implementation default-features = false -version = "0.2.2" +version = "0.3" diff --git a/LICENSE-MIT b/LICENSE-MIT index 432fbea..de3297b 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,5 @@ Copyright (c) 2018 Jorge Aparicio +Copyright (c) 2021 The Rust Embedded Linux Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/README.md b/README.md index c84ad07..e703caf 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ linux-embedded-hal = { version = "0.3", features = ["gpio_cdev"] } # Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.36.0 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.46.0 and up. It *might* compile with older versions but that may change in any new patch release. ## License diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index 3c41921..0000000 --- a/ci/install.sh +++ /dev/null @@ -1,9 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TARGET != x86_64-unknown-linux-gnu ]; then - rustup target add $TARGET - fi -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index 9e10e88..0000000 --- a/ci/script.sh +++ /dev/null @@ -1,7 +0,0 @@ -set -euxo pipefail - -main() { - cargo check --target $TARGET -} - -main diff --git a/examples/transactional-i2c.rs b/examples/transactional-i2c.rs new file mode 100644 index 0000000..5b4d9ce --- /dev/null +++ b/examples/transactional-i2c.rs @@ -0,0 +1,35 @@ +extern crate embedded_hal; +extern crate linux_embedded_hal; +use embedded_hal::blocking::i2c::{Operation as I2cOperation, Transactional}; +use linux_embedded_hal::I2cdev; + +const ADDR: u8 = 0x12; + +struct Driver { + i2c: I2C, +} + +impl Driver +where + I2C: Transactional, +{ + pub fn new(i2c: I2C) -> Self { + Driver { i2c } + } + + fn read_something(&mut self) -> Result { + let mut read_buffer = [0]; + let mut ops = [ + I2cOperation::Write(&[0xAB]), + I2cOperation::Read(&mut read_buffer), + ]; + self.i2c.exec(ADDR, &mut ops).and(Ok(read_buffer[0])) + } +} + +fn main() { + let dev = I2cdev::new("/dev/i2c-1").unwrap(); + let mut driver = Driver::new(dev); + let value = driver.read_something().unwrap(); + println!("Read value: {}", value); +} diff --git a/src/cdev_pin.rs b/src/cdev_pin.rs index 22a4cd1..9d9345d 100644 --- a/src/cdev_pin.rs +++ b/src/cdev_pin.rs @@ -2,13 +2,13 @@ /// Newtype around [`gpio_cdev::LineHandle`] that implements the `embedded-hal` traits /// -/// [`gpio_cdev::LineHandle`]: https://docs.rs/gpio-cdev/0.2.0/gpio_cdev/struct.LineHandle.html +/// [`gpio_cdev::LineHandle`]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.LineHandle.html pub struct CdevPin(pub gpio_cdev::LineHandle, bool); impl CdevPin { /// See [`gpio_cdev::Line::request`][0] for details. /// - /// [0]: https://docs.rs/gpio-cdev/0.2.0/gpio_cdev/struct.Line.html#method.request + /// [0]: https://docs.rs/gpio-cdev/0.5.0/gpio_cdev/struct.Line.html#method.request pub fn new(handle: gpio_cdev::LineHandle) -> Result { let info = handle.line().info()?; Ok(CdevPin(handle, info.is_active_low())) @@ -19,11 +19,19 @@ impl hal::digital::v2::OutputPin for CdevPin { type Error = gpio_cdev::errors::Error; fn set_low(&mut self) -> Result<(), Self::Error> { - self.0.set_value(0) + if self.1 { + self.0.set_value(1) + } else { + self.0.set_value(0) + } } fn set_high(&mut self) -> Result<(), Self::Error> { - self.0.set_value(1) + if self.1 { + self.0.set_value(0) + } else { + self.0.set_value(1) + } } } diff --git a/src/lib.rs b/src/lib.rs index 8551276..2ae3292 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,14 +13,12 @@ #![deny(missing_docs)] extern crate cast; -extern crate core; extern crate embedded_hal as hal; pub extern crate i2cdev; -pub extern crate spidev; -pub extern crate serial_unix; -pub extern crate serial_core; pub extern crate nb; - +pub extern crate serial_core; +pub extern crate serial_unix; +pub extern crate spidev; #[cfg(feature = "gpio_sysfs")] pub extern crate sysfs_gpio; @@ -28,13 +26,13 @@ pub extern crate sysfs_gpio; #[cfg(feature = "gpio_cdev")] pub extern crate gpio_cdev; - use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::time::Duration; use std::{ops, thread}; use cast::{u32, u64}; +use hal::blocking::i2c::Operation as I2cOperation; use i2cdev::core::{I2CDevice, I2CMessage, I2CTransfer}; use i2cdev::linux::LinuxI2CMessage; use spidev::SpidevTransfer; @@ -60,7 +58,6 @@ pub use cdev_pin::CdevPin; /// Sysfs pin re-export pub use sysfs_pin::SysfsPin; - /// Empty struct that provides delay functionality on top of `thread::sleep` pub struct Delay; @@ -118,10 +115,9 @@ impl hal::blocking::delay::DelayMs for Delay { } } - /// Newtype around [`i2cdev::linux::LinuxI2CDevice`] that implements the `embedded-hal` traits /// -/// [`i2cdev::linux::LinuxI2CDevice`]: https://docs.rs/i2cdev/0.3.1/i2cdev/linux/struct.LinuxI2CDevice.html +/// [`i2cdev::linux::LinuxI2CDevice`]: https://docs.rs/i2cdev/0.5.0/i2cdev/linux/struct.LinuxI2CDevice.html pub struct I2cdev { inner: i2cdev::linux::LinuxI2CDevice, path: PathBuf, @@ -131,7 +127,7 @@ pub struct I2cdev { impl I2cdev { /// See [`i2cdev::linux::LinuxI2CDevice::new`][0] for details. /// - /// [0]: https://docs.rs/i2cdev/0.3.1/i2cdev/linux/struct.LinuxI2CDevice.html#method.new + /// [0]: https://docs.rs/i2cdev/0.5.0/i2cdev/linux/struct.LinuxI2CDevice.html#method.new pub fn new

(path: P) -> Result where P: AsRef, @@ -186,6 +182,25 @@ impl hal::blocking::i2c::WriteRead for I2cdev { } } +impl hal::blocking::i2c::Transactional for I2cdev { + type Error = i2cdev::linux::LinuxI2CError; + + fn exec(&mut self, address: u8, operations: &mut [I2cOperation]) -> Result<(), Self::Error> { + // Map operations from generic to linux objects + let mut messages: Vec<_> = operations + .as_mut() + .iter_mut() + .map(|a| match a { + I2cOperation::Write(w) => LinuxI2CMessage::write(w), + I2cOperation::Read(r) => LinuxI2CMessage::read(r), + }) + .collect(); + + self.set_address(address)?; + self.inner.transfer(&mut messages).map(drop) + } +} + impl ops::Deref for I2cdev { type Target = i2cdev::linux::LinuxI2CDevice; @@ -202,13 +217,13 @@ impl ops::DerefMut for I2cdev { /// Newtype around [`spidev::Spidev`] that implements the `embedded-hal` traits /// -/// [`spidev::Spidev`]: https://docs.rs/spidev/0.4.0/spidev/struct.Spidev.html +/// [`spidev::Spidev`]: https://docs.rs/spidev/0.5.0/spidev/struct.Spidev.html pub struct Spidev(pub spidev::Spidev); impl Spidev { /// See [`spidev::Spidev::open`][0] for details. /// - /// [0]: https://docs.rs/spidev/0.4.0/spidev/struct.Spidev.html#method.open + /// [0]: https://docs.rs/spidev/0.5.0/spidev/struct.Spidev.html#method.open pub fn open

(path: P) -> io::Result where P: AsRef, @@ -236,6 +251,39 @@ impl hal::blocking::spi::Write for Spidev { } } +pub use hal::blocking::spi::Operation as SpiOperation; + +impl hal::blocking::spi::Transactional for Spidev { + type Error = io::Error; + + fn exec<'a>(&mut self, operations: &mut [SpiOperation<'a, u8>]) -> Result<(), Self::Error> { + // Map types from generic to linux objects + let mut messages: Vec<_> = operations + .iter_mut() + .map(|a| { + match a { + SpiOperation::Write(w) => SpidevTransfer::write(w), + SpiOperation::Transfer(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 { + let p = r.as_ptr(); + std::slice::from_raw_parts(p, r.len()) + }; + + SpidevTransfer::read_write(w, r) + } + } + }) + .collect(); + + // Execute transfer + self.0.transfer_multiple(&mut messages)?; + + Ok(()) + } +} + impl ops::Deref for Spidev { type Target = spidev::Spidev; diff --git a/src/serial.rs b/src/serial.rs index e6fe5a8..0f9be50 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,4 +1,4 @@ -//! Implementation of [`Serial`](https://docs.rs/embedded-hal/0.2.1/embedded_hal/serial/index.html) +//! Implementation of [`Serial`](https://docs.rs/embedded-hal/0.2.6/embedded_hal/serial/index.html) use std::io::{ErrorKind as IoErrorKind, Read, Write}; use std::path::Path; diff --git a/src/sysfs_pin.rs b/src/sysfs_pin.rs index 8e0597d..aa03218 100644 --- a/src/sysfs_pin.rs +++ b/src/sysfs_pin.rs @@ -4,20 +4,20 @@ use std::path::Path; /// Newtype around [`sysfs_gpio::Pin`] that implements the `embedded-hal` traits /// -/// [`sysfs_gpio::Pin`]: https://docs.rs/sysfs_gpio/0.5.1/sysfs_gpio/struct.Pin.html +/// [`sysfs_gpio::Pin`]: https://docs.rs/sysfs_gpio/0.6.0/sysfs_gpio/struct.Pin.html pub struct SysfsPin(pub sysfs_gpio::Pin); impl SysfsPin { /// See [`sysfs_gpio::Pin::new`][0] for details. /// - /// [0]: https://docs.rs/sysfs_gpio/0.5.1/sysfs_gpio/struct.Pin.html#method.new + /// [0]: https://docs.rs/sysfs_gpio/0.6.0/sysfs_gpio/struct.Pin.html#method.new pub fn new(pin_num: u64) -> Self { SysfsPin(sysfs_gpio::Pin::new(pin_num)) } /// See [`sysfs_gpio::Pin::from_path`][0] for details. /// - /// [0]: https://docs.rs/sysfs_gpio/0.5.1/sysfs_gpio/struct.Pin.html#method.from_path + /// [0]: https://docs.rs/sysfs_gpio/0.6.0/sysfs_gpio/struct.Pin.html#method.from_path pub fn from_path

(path: P) -> sysfs_gpio::Result where P: AsRef, @@ -30,11 +30,19 @@ impl hal::digital::v2::OutputPin for SysfsPin { type Error = sysfs_gpio::Error; fn set_low(&mut self) -> Result<(), Self::Error> { - self.0.set_value(0) + if self.0.get_active_low()? { + self.0.set_value(1) + } else { + self.0.set_value(0) + } } fn set_high(&mut self) -> Result<(), Self::Error> { - self.0.set_value(1) + if self.0.get_active_low()? { + self.0.set_value(0) + } else { + self.0.set_value(1) + } } } diff --git a/src/timer.rs b/src/timer.rs index 69d8538..eb5719f 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -26,6 +26,12 @@ impl SysTimer { } } +impl Default for SysTimer { + fn default() -> SysTimer { + SysTimer::new() + } +} + impl CountDown for SysTimer { type Time = Duration; @@ -77,12 +83,12 @@ mod tests { nb::block!(timer.wait()).unwrap(); let after1 = Instant::now(); let duration_ms_1 = (after1 - before).as_millis(); - assert!(duration_ms_1 >= 100); + assert!(duration_ms_1 >= 98); assert!(duration_ms_1 < 500); nb::block!(timer.wait()).unwrap(); let after2 = Instant::now(); let duration_ms_2 = (after2 - after1).as_millis(); - assert!(duration_ms_2 >= 100); + assert!(duration_ms_2 >= 98); assert!(duration_ms_2 < 500); } }