From d07d39e359ed2157cbc0bbd88ef950b60c58e804 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 May 2023 21:26:15 +0200 Subject: [PATCH 001/201] spi: add Operation::DelayUs(u32). --- embedded-hal-async/src/spi.rs | 33 ++++++++++++++---- embedded-hal-bus/src/spi/critical_section.rs | 36 ++++++++++++++++---- embedded-hal-bus/src/spi/exclusive.rs | 36 ++++++++++++++++---- embedded-hal-bus/src/spi/mod.rs | 9 +++++ embedded-hal-bus/src/spi/mutex.rs | 36 ++++++++++++++++---- embedded-hal-bus/src/spi/refcell.rs | 36 ++++++++++++++++---- embedded-hal/CHANGELOG.md | 3 ++ embedded-hal/src/spi.rs | 2 ++ 8 files changed, 160 insertions(+), 31 deletions(-) diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 0c99d79bf..2c05f56e4 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -8,6 +8,8 @@ pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; +use crate::delay::DelayUs; + /// SPI device trait /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected @@ -195,19 +197,20 @@ where /// /// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], /// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { +pub struct ExclusiveDevice { bus: BUS, cs: CS, + delay: D, } -impl ExclusiveDevice { +impl ExclusiveDevice { /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: BUS, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl ErrorType for ExclusiveDevice +impl ErrorType for ExclusiveDevice where BUS: ErrorType, CS: OutputPin, @@ -215,10 +218,11 @@ where type Error = ExclusiveDeviceError; } -impl blocking::SpiDevice for ExclusiveDevice +impl blocking::SpiDevice for ExclusiveDevice where BUS: blocking::SpiBus, CS: OutputPin, + D: embedded_hal::delay::DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; @@ -230,6 +234,13 @@ where Operation::Write(buf) => self.bus.write(buf), Operation::Transfer(read, write) => self.bus.transfer(read, write), Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + Operation::DelayUs(us) => match self.bus.flush() { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us); + Ok(()) + } + }, }; if let Err(e) = res { break 'ops Err(e); @@ -250,10 +261,11 @@ where } } -impl SpiDevice for ExclusiveDevice +impl SpiDevice for ExclusiveDevice where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { async fn transaction( &mut self, @@ -268,6 +280,13 @@ where Operation::Write(buf) => self.bus.write(buf).await, Operation::Transfer(read, write) => self.bus.transfer(read, write).await, Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + Operation::DelayUs(us) => match self.bus.flush().await { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us).await; + Ok(()) + } + }, }; if let Err(e) = res { break 'ops Err(e); diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index c3e8cf699..2a4c09788 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -1,5 +1,6 @@ use core::cell::RefCell; use critical_section::Mutex; +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -15,19 +16,36 @@ use super::DeviceError; /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using /// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections. -pub struct CriticalSectionDevice<'a, BUS, CS> { +pub struct CriticalSectionDevice<'a, BUS, CS, D> { bus: &'a Mutex>, cs: CS, + delay: D, } -impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS> { +impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { /// Create a new ExclusiveDevice - pub fn new(bus: &'a Mutex>, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl<'a, BUS, CS> ErrorType for CriticalSectionDevice<'a, BUS, CS> +impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { + /// Create a new CriticalSectionDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for CriticalSectionDevice<'a, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -35,10 +53,11 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for CriticalSectionDevice<'a, BUS, CS> +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for CriticalSectionDevice<'a, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { critical_section::with(|cs| { @@ -51,6 +70,11 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 9d4d002c9..cbec194dc 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -1,5 +1,6 @@ //! SPI bus sharing mechanisms. +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -9,19 +10,36 @@ use super::DeviceError; /// /// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), /// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { +pub struct ExclusiveDevice { bus: BUS, cs: CS, + delay: D, } -impl ExclusiveDevice { +impl ExclusiveDevice { /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: BUS, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl ErrorType for ExclusiveDevice +impl ExclusiveDevice { + /// Create a new ExclusiveDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: BUS, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl ErrorType for ExclusiveDevice where BUS: ErrorType, CS: OutputPin, @@ -29,10 +47,11 @@ where type Error = DeviceError; } -impl SpiDevice for ExclusiveDevice +impl SpiDevice for ExclusiveDevice where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { self.cs.set_low().map_err(DeviceError::Cs)?; @@ -42,6 +61,11 @@ where Operation::Write(buf) => self.bus.write(buf), Operation::Transfer(read, write) => self.bus.transfer(read, write), Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + self.bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 6ae803865..10f74e8d6 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -35,3 +35,12 @@ where } } } + +/// Dummy `DelayUs` implementation that panics on use. +pub struct NoDelay; + +impl embedded_hal::delay::DelayUs for NoDelay { + fn delay_us(&mut self, _us: u32) { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + } +} diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 59f8dfe95..bdf329217 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -1,3 +1,4 @@ +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use std::sync::Mutex; @@ -12,19 +13,36 @@ use super::DeviceError; /// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is /// it is only available in `std` targets. -pub struct MutexDevice<'a, BUS, CS> { +pub struct MutexDevice<'a, BUS, CS, D> { bus: &'a Mutex, cs: CS, + delay: D, } -impl<'a, BUS, CS> MutexDevice<'a, BUS, CS> { +impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { /// Create a new ExclusiveDevice - pub fn new(bus: &'a Mutex, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl<'a, BUS, CS> ErrorType for MutexDevice<'a, BUS, CS> +impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { + /// Create a new MutexDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for MutexDevice<'a, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -32,10 +50,11 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for MutexDevice<'a, BUS, CS> +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for MutexDevice<'a, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); @@ -47,6 +66,11 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 9fd5d3e03..a08e8a262 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -12,19 +13,36 @@ use super::DeviceError; /// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, /// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several /// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. -pub struct RefCellDevice<'a, BUS, CS> { +pub struct RefCellDevice<'a, BUS, CS, D> { bus: &'a RefCell, cs: CS, + delay: D, } -impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS> { +impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { /// Create a new ExclusiveDevice - pub fn new(bus: &'a RefCell, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl<'a, BUS, CS> ErrorType for RefCellDevice<'a, BUS, CS> +impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { + /// Create a new RefCellDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for RefCellDevice<'a, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -32,10 +50,11 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for RefCellDevice<'a, BUS, CS> +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for RefCellDevice<'a, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); @@ -47,6 +66,11 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 3c25f2dd6..afd99a7a7 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- spi: added `Operation::DelayUs(u32)`. + ### Removed - spi: removed read-only and write-only traits. diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index ea77bf982..3bcdda795 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -313,6 +313,8 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::transfer_in_place`]. TransferInPlace(&'a mut [Word]), + /// Delay for at least the specified number of microseconds + DelayUs(u32), } /// SPI device trait From 50cceacf4667998ee0617603bf00e5cb08300f2a Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Tue, 20 Jun 2023 10:20:00 +0200 Subject: [PATCH 002/201] Switch to GHMQ --- .github/bors.toml | 16 ---------------- .github/workflows/clippy.yml | 8 +++++--- .github/workflows/rustfmt.yml | 8 +++++--- .github/workflows/test.yml | 10 ++++++---- 4 files changed, 16 insertions(+), 26 deletions(-) delete mode 100644 .github/bors.toml diff --git a/.github/bors.toml b/.github/bors.toml deleted file mode 100644 index 0f213ce07..000000000 --- a/.github/bors.toml +++ /dev/null @@ -1,16 +0,0 @@ -block_labels = ["needs-decision"] -delete_merged_branches = true -required_approvals = 1 -status = [ - "test (stable, x86_64-unknown-linux-gnu)", - "test (stable, thumbv6m-none-eabi)", - "test (stable, thumbv7m-none-eabi)", - "test (1.59.0, x86_64-unknown-linux-gnu)", - "test (1.59.0, thumbv6m-none-eabi)", - "test (1.59.0, thumbv7m-none-eabi)", - "test (nightly, x86_64-unknown-linux-gnu)", - "test (nightly, thumbv6m-none-eabi)", - "test (nightly, thumbv7m-none-eabi)", - "clippy", - "fmt", -] diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 9fa0c3bed..20cd030f6 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,7 +1,9 @@ on: - push: - branches: [ staging, trying, master ] - pull_request: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue name: Clippy check jobs: diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 29049c24d..3d9036e69 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -1,7 +1,9 @@ on: - push: - branches: [ staging, trying, master ] - pull_request: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue name: Code formatting check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ff0013f53..1677ea895 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,9 @@ on: - push: - branches: [ staging, trying, master ] - pull_request: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue name: Continuous integration @@ -32,7 +34,7 @@ jobs: toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} - - run: sed -i '/nightly-only/d' Cargo.toml + - run: sed -i '/nightly-only/d' Cargo.toml if: matrix.rust != 'nightly' - run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }} From 851dae5ec7f6eec2c2a03df242302d2db8eb50c0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 17:46:22 +0200 Subject: [PATCH 003/201] Release e-h 1.0-alpha.11, e-h-async 0.2-alpha.2, e-h-bus 0.1-alpha.3, e-h-nb v1.0-alpha.3 --- embedded-hal-async/CHANGELOG.md | 12 +++++++++++- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-bus/CHANGELOG.md | 9 ++++++++- embedded-hal-bus/Cargo.toml | 4 ++-- embedded-hal-nb/CHANGELOG.md | 11 +++++++++-- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 9 ++++++++- embedded-hal/Cargo.toml | 2 +- 8 files changed, 43 insertions(+), 12 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 5da34a3ca..c1b4de3c3 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.2.0-alpha.2] - 2023-07-04 + +### Added +- spi: added `Operation::DelayUs(u32)`. + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.11`. +- spi: removed redundant lifetime annotations. Note that recent nightlies care about them and require impls to match, so you might have to adjust them. + ### Removed - spi: removed read-only and write-only traits. @@ -52,7 +61,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...HEAD +[v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 [v0.2.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...embedded-hal-async-v0.2.0-alpha.1 [v0.2.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.3...embedded-hal-async-v0.2.0-alpha.0 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.2...embedded-hal-async-v0.1.0-alpha.3 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index a230a0f68..dbae01c7d 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,8 +11,8 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" rust-version = "1.65.0" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 45abc9b4d..c97121910 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.0-alpha.3] - 2023-07-04 + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.11`. + + ## [v0.1.0-alpha.2] - 2023-04-04 ### Changed @@ -25,7 +31,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...HEAD +[v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 [v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...embedded-hal-bus-v0.1.0-alpha.2 [v0.1.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.0...embedded-hal-bus-v0.1.0-alpha.1 [v0.1.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-bus-v0.1.0-alpha.0 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 2ad3815da..5e6f40995 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -11,11 +11,11 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-alpha.2" +version = "0.1.0-alpha.3" [features] std = [] [dependencies] -embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } critical-section = { version = "1.0" } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 0bc3a63f9..11ebab3b0 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ... +## [v1.0.0-alpha.3] - 2023-07-04 + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.11`. + + ## [v1.0.0-alpha.2] - 2023-04-04 ### Changed @@ -23,7 +29,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...HEAD -[v1.0.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...embedded-hal-nb-v1.0.0-alpha.2which +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...HEAD +[v1.0.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...embedded-hal-nb-v1.0.0-alpha.3 +[v1.0.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...embedded-hal-nb-v1.0.0-alpha.2 [v1.0.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.0...embedded-hal-nb-v1.0.0-alpha.1 [v1.0.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-nb-v1.0.0-alpha.0 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index eefaa19a2..0bf3d7346 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" edition = "2021" categories = ["embedded", "hardware-support", "no-std"] @@ -12,7 +12,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index afd99a7a7..0803cb8f7 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] + +## [v1.0.0-alpha.11] - 2023-07-04 + +*** This is (also) an alpha release with breaking changes (sorry) *** + ### Added - spi: added `Operation::DelayUs(u32)`. @@ -286,7 +291,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...HEAD +[v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 +[v1.0.0-alpha.10]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...v1.0.0-alpha.10 [v1.0.0-alpha.9]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.8...v1.0.0-alpha.9 [v1.0.0-alpha.8]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.7...v1.0.0-alpha.8 [v1.0.0-alpha.7]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.6...v1.0.0-alpha.7 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 5e8307061..c70b3106a 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -13,4 +13,4 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-alpha.10" +version = "1.0.0-alpha.11" From 6e27094671a116b3e1301a6fcb5c7bb0b9c8dae8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:25:50 +0200 Subject: [PATCH 004/201] Add embedded-io. --- Cargo.toml | 1 + embedded-io/CHANGELOG.md | 34 +++++ embedded-io/Cargo.toml | 12 ++ embedded-io/LICENSE-APACHE | 201 ++++++++++++++++++++++++++ embedded-io/LICENSE-MIT | 25 ++++ embedded-io/README.md | 39 +++++ embedded-io/src/lib.rs | 281 +++++++++++++++++++++++++++++++++++++ 7 files changed, 593 insertions(+) create mode 100644 embedded-io/CHANGELOG.md create mode 100644 embedded-io/Cargo.toml create mode 100644 embedded-io/LICENSE-APACHE create mode 100644 embedded-io/LICENSE-MIT create mode 100644 embedded-io/README.md create mode 100644 embedded-io/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4322dde4e..2a8ca15a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ members = [ "embedded-hal-nb", "embedded-hal-bus", "embedded-can", + "embedded-io", ] diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md new file mode 100644 index 000000000..43db9e22f --- /dev/null +++ b/embedded-io/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- Moved `embedded_io::blocking` to the crate root. +- Split async traits to the `embedded-io-async` crate. +- Split async trait adapters to separate crates. + +## 0.4.0 - 2022-11-25 + +- Switch all traits to use [`async_fn_in_trait`](https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html) (AFIT). Requires `nightly-2022-11-22` or newer. + +## 0.3.1 - 2022-10-26 + +- Fix compilation on recent nightlies (#5) + +## 0.3.0 - 2022-05-19 + +- `FromFutures` adapter now requires `futures` Cargo feature. (breaking change) +- Add `FromTokio` adapter. +- Add blanket impls for `&mut T`, `Box`. +- Add impl `Read`, `BufRead` for `&[u8]` +- Add impl `Write` for `&mut [u8]` +- Add impl `Write` for `Vec` +- impl `std::error::Error` for `ReadExactError`, `WriteFmtError`. + +## 0.2.0 - 2022-05-07 + +- First release \ No newline at end of file diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml new file mode 100644 index 000000000..71fe31203 --- /dev/null +++ b/embedded-io/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "embedded-io" +version = "0.5.0" +edition = "2021" +description = "Embedded IO traits" +repository = "https://github.com/rust-embedded/embedded-hal" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", +] diff --git a/embedded-io/LICENSE-APACHE b/embedded-io/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/embedded-io/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/embedded-io/LICENSE-MIT b/embedded-io/LICENSE-MIT new file mode 100644 index 000000000..e00608fbd --- /dev/null +++ b/embedded-io/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The embedded-io authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/embedded-io/README.md b/embedded-io/README.md new file mode 100644 index 000000000..33461b3cf --- /dev/null +++ b/embedded-io/README.md @@ -0,0 +1,39 @@ +[![crates.io](https://img.shields.io/crates/d/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) + +# `embedded-io` + +This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). + +IO traits for embedded systems. + +Rust's `std::io` traits are not available in `no_std` targets, mainly because `std::io::Error` +requires allocation. This crate contains replacement equivalent traits, usable in `no_std` +targets. + +The only difference with `std::io` is `Error` is an associated type. This allows each implementor +to return its own error type, while avoiding `dyn` or `Box`. This is how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +compile with older versions but that may change in any new patch release. + +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs new file mode 100644 index 000000000..4d90b5ea5 --- /dev/null +++ b/embedded-io/src/lib.rs @@ -0,0 +1,281 @@ +#![no_std] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] + +use core::fmt; + +/// Enumeration of possible methods to seek within an I/O object. +/// +/// Semantics are the same as [`std::io::SeekFrom`], check its documentation for details. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum SeekFrom { + /// Sets the offset to the provided number of bytes. + Start(u64), + /// Sets the offset to the size of this object plus the specified number of bytes. + End(i64), + /// Sets the offset to the current position plus the specified number of bytes. + Current(i64), +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +/// Possible kinds of errors. +pub enum ErrorKind { + /// Unspecified error kind. + Other, +} + +/// Error trait. +/// +/// This trait allows generic code to do limited inspecting of errors, +/// to react differently to different kinds. +pub trait Error: core::fmt::Debug { + /// Get the kind of this error. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +/// Base trait for all IO traits. +/// +/// All IO operations of all traits return the error defined in this trait. +/// +/// Having a shared trait instead of having every trait define its own +/// `Error` associated type enforces all impls on the same type use the same error. +/// This is very convenient when writing generic code, it means you have to +/// handle a single error type `T::Error`, instead of `::Error` and `::Error` +/// which might be different types. +pub trait Io { + /// Error type of all the IO operations on this type. + type Error: Error; +} + +impl crate::Io for &mut T { + type Error = T::Error; +} + +/// Error returned by [`Read::read_exact`] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ReadExactError { + /// An EOF error was encountered before reading the exact amount of requested bytes. + UnexpectedEof, + /// Error returned by the inner Read. + Other(E), +} + +impl From for ReadExactError { + fn from(err: E) -> Self { + Self::Other(err) + } +} + +impl fmt::Display for ReadExactError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Error returned by [`Write::write_fmt`] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum WriteFmtError { + /// [`Write::write`] wrote zero bytes + WriteZero, + /// An error was encountered while formatting. + FmtError, + /// Error returned by the inner Write. + Other(E), +} + +impl From for WriteFmtError { + fn from(err: E) -> Self { + Self::Other(err) + } +} + +impl fmt::Display for WriteFmtError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Error returned by [`Write::write_all`] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum WriteAllError { + /// [`Write::write`] wrote zero bytes + WriteZero, + /// Error returned by the inner Write. + Other(E), +} + +impl From for WriteAllError { + fn from(err: E) -> Self { + Self::Other(err) + } +} + +impl fmt::Display for WriteAllError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Blocking reader. +/// +/// Semantics are the same as [`std::io::Read`], check its documentation for details. +pub trait Read: crate::Io { + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read the exact number of bytes required to fill `buf`. + fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { + while !buf.is_empty() { + match self.read(buf) { + Ok(0) => break, + Ok(n) => buf = &mut buf[n..], + Err(e) => return Err(ReadExactError::Other(e)), + } + } + if !buf.is_empty() { + Err(ReadExactError::UnexpectedEof) + } else { + Ok(()) + } + } +} + +/// Blocking buffered reader. +/// +/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +pub trait BufRead: crate::Io { + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; + + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + fn consume(&mut self, amt: usize); +} + +/// Blocking writer. +/// +/// Semantics are the same as [`std::io::Write`], check its documentation for details. +pub trait Write: crate::Io { + /// Write a buffer into this writer, returning how many bytes were written. + fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + fn flush(&mut self) -> Result<(), Self::Error>; + + /// Write an entire buffer into this writer. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => return Err(WriteAllError::WriteZero), + Ok(n) => buf = &buf[n..], + Err(e) => return Err(WriteAllError::Other(e)), + } + } + Ok(()) + } + + /// Write a formatted string into this writer, returning any error encountered. + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError> { + // Create a shim which translates a Write to a fmt::Write and saves + // off I/O errors. instead of discarding them + struct Adapter<'a, T: Write + ?Sized + 'a> { + inner: &'a mut T, + error: Result<(), WriteAllError>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { + inner: self, + error: Ok(()), + }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => match output.error { + // check if the error came from the underlying `Write` or not + Err(e) => match e { + WriteAllError::WriteZero => Err(WriteFmtError::WriteZero), + WriteAllError::Other(e) => Err(WriteFmtError::Other(e)), + }, + Ok(()) => Err(WriteFmtError::FmtError), + }, + } + } +} + +/// Blocking seek within streams. +/// +/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +pub trait Seek: crate::Io { + /// Seek to an offset, in bytes, in a stream. + fn seek(&mut self, pos: crate::SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + fn rewind(&mut self) -> Result<(), Self::Error> { + self.seek(crate::SeekFrom::Start(0))?; + Ok(()) + } + + /// Returns the current seek position from the start of the stream. + fn stream_position(&mut self) -> Result { + self.seek(crate::SeekFrom::Current(0)) + } +} + +impl Read for &mut T { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf) + } +} + +impl BufRead for &mut T { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self) + } + + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +impl Write for &mut T { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self) + } +} + +impl Seek for &mut T { + #[inline] + fn seek(&mut self, pos: crate::SeekFrom) -> Result { + T::seek(self, pos) + } +} From 6eb30c50d1bf49dde55fe25d46d695c06ba50b5f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:26:21 +0200 Subject: [PATCH 005/201] io: add impls for slices. --- embedded-io/src/impls/mod.rs | 2 ++ embedded-io/src/impls/slice_mut.rs | 31 ++++++++++++++++++++++ embedded-io/src/impls/slice_ref.rs | 41 ++++++++++++++++++++++++++++++ embedded-io/src/lib.rs | 2 ++ 4 files changed, 76 insertions(+) create mode 100644 embedded-io/src/impls/mod.rs create mode 100644 embedded-io/src/impls/slice_mut.rs create mode 100644 embedded-io/src/impls/slice_ref.rs diff --git a/embedded-io/src/impls/mod.rs b/embedded-io/src/impls/mod.rs new file mode 100644 index 000000000..7f4a6f802 --- /dev/null +++ b/embedded-io/src/impls/mod.rs @@ -0,0 +1,2 @@ +mod slice_mut; +mod slice_ref; diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs new file mode 100644 index 000000000..cefaa296c --- /dev/null +++ b/embedded-io/src/impls/slice_mut.rs @@ -0,0 +1,31 @@ +use crate::{Io, Write}; +use core::mem; + +impl Io for &mut [u8] { + type Error = core::convert::Infallible; +} + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +/// +/// If the number of bytes to be written exceeds the size of the slice, write operations will +/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of +/// kind `ErrorKind::WriteZero`. +impl Write for &mut [u8] { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&buf[..amt]); + *self = b; + Ok(amt) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/embedded-io/src/impls/slice_ref.rs b/embedded-io/src/impls/slice_ref.rs new file mode 100644 index 000000000..73c98ab55 --- /dev/null +++ b/embedded-io/src/impls/slice_ref.rs @@ -0,0 +1,41 @@ +use crate::{BufRead, Io, Read}; + +impl Io for &[u8] { + type Error = core::convert::Infallible; +} + +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. +impl Read for &[u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } +} + +impl BufRead for &[u8] { + #[inline] + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 4d90b5ea5..6bafbbe53 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -4,6 +4,8 @@ use core::fmt; +mod impls; + /// Enumeration of possible methods to seek within an I/O object. /// /// Semantics are the same as [`std::io::SeekFrom`], check its documentation for details. From eb6e078b2688d59c7a5d2553a61bd1f70650f2af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:31:01 +0200 Subject: [PATCH 006/201] io: add std/alloc features, add Box/Vec impls. --- embedded-io/Cargo.toml | 8 ++++++ embedded-io/src/impls/boxx.rs | 47 +++++++++++++++++++++++++++++++++++ embedded-io/src/impls/mod.rs | 5 ++++ embedded-io/src/impls/vec.rs | 21 ++++++++++++++++ embedded-io/src/lib.rs | 15 ++++++++++- 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 embedded-io/src/impls/boxx.rs create mode 100644 embedded-io/src/impls/vec.rs diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index 71fe31203..d1f82254f 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -10,3 +10,11 @@ categories = [ "embedded", "no-std", ] + +[features] +std = ["alloc"] +alloc = [] + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs new file mode 100644 index 000000000..27bc1931c --- /dev/null +++ b/embedded-io/src/impls/boxx.rs @@ -0,0 +1,47 @@ +use crate::{BufRead, Io, Read, Seek, Write}; +use alloc::boxed::Box; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Io for Box { + type Error = T::Error; +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Read for Box { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl BufRead for Box { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self) + } + + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Seek for Box { + #[inline] + fn seek(&mut self, pos: crate::SeekFrom) -> Result { + T::seek(self, pos) + } +} diff --git a/embedded-io/src/impls/mod.rs b/embedded-io/src/impls/mod.rs index 7f4a6f802..e79b9b8bf 100644 --- a/embedded-io/src/impls/mod.rs +++ b/embedded-io/src/impls/mod.rs @@ -1,2 +1,7 @@ mod slice_mut; mod slice_ref; + +#[cfg(feature = "alloc")] +mod boxx; +#[cfg(feature = "alloc")] +mod vec; diff --git a/embedded-io/src/impls/vec.rs b/embedded-io/src/impls/vec.rs new file mode 100644 index 000000000..8aa1b62d6 --- /dev/null +++ b/embedded-io/src/impls/vec.rs @@ -0,0 +1,21 @@ +use crate::{Io, Write}; +use alloc::vec::Vec; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Io for Vec { + type Error = core::convert::Infallible; +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 6bafbbe53..61f112832 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -1,9 +1,13 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] use core::fmt; +#[cfg(feature = "alloc")] +extern crate alloc; + mod impls; /// Enumeration of possible methods to seek within an I/O object. @@ -87,6 +91,9 @@ impl fmt::Display for ReadExactError { } } +#[cfg(feature = "std")] +impl std::error::Error for ReadExactError {} + /// Error returned by [`Write::write_fmt`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum WriteFmtError { @@ -110,6 +117,9 @@ impl fmt::Display for WriteFmtError { } } +#[cfg(feature = "std")] +impl std::error::Error for WriteFmtError {} + /// Error returned by [`Write::write_all`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum WriteAllError { @@ -131,6 +141,9 @@ impl fmt::Display for WriteAllError { } } +#[cfg(feature = "std")] +impl std::error::Error for WriteAllError {} + /// Blocking reader. /// /// Semantics are the same as [`std::io::Read`], check its documentation for details. From 1b5a858b1231185b60a6835c91581860c9ce6194 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:32:39 +0200 Subject: [PATCH 007/201] io: add adapters to/from std::io. --- embedded-io/src/adapters.rs | 161 ++++++++++++++++++++++++++++++++++++ embedded-io/src/lib.rs | 3 + 2 files changed, 164 insertions(+) create mode 100644 embedded-io/src/adapters.rs diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs new file mode 100644 index 000000000..942d52455 --- /dev/null +++ b/embedded-io/src/adapters.rs @@ -0,0 +1,161 @@ +//! Adapters to/from `std::io` traits. +//! +//! To interoperate with `std::io`, wrap a type in one of these +//! adapters. +//! +//! There's no separate adapters for Read/ReadBuf/Write traits. Instead, a single +//! adapter implements the right traits based on what the inner type implements. +//! This allows adapting a `Read+Write`, for example. + +use crate::SeekFrom; + +/// Adapter from `std::io` traits. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[derive(Clone)] +pub struct FromStd { + inner: T, +} + +impl FromStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl crate::Io for FromStd { + type Error = std::io::Error; +} + +impl crate::Read for FromStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf) + } +} + +impl crate::BufRead for FromStd { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.inner.consume(amt) + } +} + +impl crate::Write for FromStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf) + } + fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.flush() + } +} + +impl crate::Seek for FromStd { + fn seek(&mut self, pos: crate::SeekFrom) -> Result { + self.inner.seek(pos.into()) + } +} + +/// Adapter to `std::io` traits. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub struct ToStd { + inner: T, +} + +impl ToStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl ToStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl std::io::Read for ToStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf).map_err(to_io_error) + } +} + +impl std::io::Write for ToStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf).map_err(to_io_error) + } + fn flush(&mut self) -> Result<(), std::io::Error> { + self.inner.flush().map_err(to_io_error) + } +} + +impl std::io::Seek for ToStd { + fn seek(&mut self, pos: std::io::SeekFrom) -> Result { + self.inner.seek(pos.into()).map_err(to_io_error) + } +} + +fn to_io_error(err: T) -> std::io::Error { + let kind = std::io::ErrorKind::Other; + std::io::Error::new(kind, format!("{:?}", err)) +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl crate::Error for std::io::Error { + fn kind(&self) -> crate::ErrorKind { + crate::ErrorKind::Other + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for std::io::SeekFrom { + fn from(pos: SeekFrom) -> Self { + match pos { + SeekFrom::Start(n) => std::io::SeekFrom::Start(n), + SeekFrom::End(n) => std::io::SeekFrom::End(n), + SeekFrom::Current(n) => std::io::SeekFrom::Current(n), + } + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for SeekFrom { + fn from(pos: std::io::SeekFrom) -> SeekFrom { + match pos { + std::io::SeekFrom::Start(n) => SeekFrom::Start(n), + std::io::SeekFrom::End(n) => SeekFrom::End(n), + std::io::SeekFrom::Current(n) => SeekFrom::Current(n), + } + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 61f112832..07bf60517 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -8,6 +8,9 @@ use core::fmt; #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "std")] +pub mod adapters; + mod impls; /// Enumeration of possible methods to seek within an I/O object. From d6f641995fc1597920b7d86bf58d0332b7ebbaeb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:39:53 +0200 Subject: [PATCH 008/201] Add embedded-io-async. --- Cargo.toml | 1 + embedded-io-async/Cargo.toml | 15 +++++ embedded-io-async/README.md | 35 ++++++++++ embedded-io-async/src/lib.rs | 123 +++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 embedded-io-async/Cargo.toml create mode 100644 embedded-io-async/README.md create mode 100644 embedded-io-async/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 2a8ca15a9..ab7723059 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ members = [ "embedded-hal-bus", "embedded-can", "embedded-io", + "embedded-io-async", # nightly-only ] diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml new file mode 100644 index 000000000..e900278f9 --- /dev/null +++ b/embedded-io-async/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "embedded-io-async" +version = "0.5.0" +edition = "2021" +description = "Async embedded IO traits" +repository = "https://github.com/rust-embedded/embedded-hal" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", +] + +[dependencies] +embedded-io = { version = "0.5", path = "../embedded-io" } diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md new file mode 100644 index 000000000..557a0866a --- /dev/null +++ b/embedded-io-async/README.md @@ -0,0 +1,35 @@ +[![crates.io](https://img.shields.io/crates/d/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) +[![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) +[![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) + +# `embedded-io-async` + +Async IO traits for embedded systems. + +This crate contains asynchronous versions of the [`embedded-io`](https://crates.io/crates/embedded-io) traits and shares its scope and design goals. + +This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). + +## Minimum Supported Rust Version (MSRV) + +This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for +`async fn` in traits (AFIT), which is not stable yet. + +Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features +at any time. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs new file mode 100644 index 000000000..8bf4fd26f --- /dev/null +++ b/embedded-io-async/src/lib.rs @@ -0,0 +1,123 @@ +#![no_std] +#![feature(async_fn_in_trait, impl_trait_projections)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] + +pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; + +/// Async reader. +/// +/// Semantics are the same as [`std::io::Read`], check its documentation for details. +pub trait Read: Io { + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + async fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read the exact number of bytes required to fill `buf`. + async fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { + while !buf.is_empty() { + match self.read(buf).await { + Ok(0) => break, + Ok(n) => buf = &mut buf[n..], + Err(e) => return Err(ReadExactError::Other(e)), + } + } + if !buf.is_empty() { + Err(ReadExactError::UnexpectedEof) + } else { + Ok(()) + } + } +} + +/// Async buffered reader. +/// +/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +pub trait BufRead: Io { + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; + + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + fn consume(&mut self, amt: usize); +} + +/// Async writer. +/// +/// Semantics are the same as [`std::io::Write`], check its documentation for details. +pub trait Write: Io { + /// Write a buffer into this writer, returning how many bytes were written. + async fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + /// Write an entire buffer into this writer. + async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteAllError> { + let mut buf = buf; + while !buf.is_empty() { + match self.write(buf).await { + Ok(0) => return Err(WriteAllError::WriteZero), + Ok(n) => buf = &buf[n..], + Err(e) => return Err(WriteAllError::Other(e)), + } + } + Ok(()) + } +} + +/// Async seek within streams. +/// +/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +pub trait Seek: Io { + /// Seek to an offset, in bytes, in a stream. + async fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + async fn rewind(&mut self) -> Result<(), Self::Error> { + self.seek(SeekFrom::Start(0)).await?; + Ok(()) + } + + /// Returns the current seek position from the start of the stream. + async fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)).await + } +} + +impl Read for &mut T { + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf).await + } +} + +impl BufRead for &mut T { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self).await + } + + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +impl Write for &mut T { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf).await + } + + #[inline] + async fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self).await + } +} + +impl Seek for &mut T { + #[inline] + async fn seek(&mut self, pos: SeekFrom) -> Result { + T::seek(self, pos).await + } +} From a105876f16045ae37b67e19f67c672112164c516 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:58:10 +0200 Subject: [PATCH 009/201] io-async: add impls for slices. --- embedded-io-async/src/impls/mod.rs | 2 ++ embedded-io-async/src/impls/slice_mut.rs | 22 ++++++++++++++ embedded-io-async/src/impls/slice_ref.rs | 37 ++++++++++++++++++++++++ embedded-io-async/src/lib.rs | 2 ++ 4 files changed, 63 insertions(+) create mode 100644 embedded-io-async/src/impls/mod.rs create mode 100644 embedded-io-async/src/impls/slice_mut.rs create mode 100644 embedded-io-async/src/impls/slice_ref.rs diff --git a/embedded-io-async/src/impls/mod.rs b/embedded-io-async/src/impls/mod.rs new file mode 100644 index 000000000..7f4a6f802 --- /dev/null +++ b/embedded-io-async/src/impls/mod.rs @@ -0,0 +1,2 @@ +mod slice_mut; +mod slice_ref; diff --git a/embedded-io-async/src/impls/slice_mut.rs b/embedded-io-async/src/impls/slice_mut.rs new file mode 100644 index 000000000..4a195e3f4 --- /dev/null +++ b/embedded-io-async/src/impls/slice_mut.rs @@ -0,0 +1,22 @@ +use crate::Write; +use core::mem; + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +/// +/// If the number of bytes to be written exceeds the size of the slice, write operations will +/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of +/// kind `ErrorKind::WriteZero`. +impl Write for &mut [u8] { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&buf[..amt]); + *self = b; + Ok(amt) + } +} diff --git a/embedded-io-async/src/impls/slice_ref.rs b/embedded-io-async/src/impls/slice_ref.rs new file mode 100644 index 000000000..a6a4ba807 --- /dev/null +++ b/embedded-io-async/src/impls/slice_ref.rs @@ -0,0 +1,37 @@ +use crate::{BufRead, Read}; + +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. +impl Read for &[u8] { + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } +} + +impl BufRead for &[u8] { + #[inline] + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 8bf4fd26f..0e120c40c 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -4,6 +4,8 @@ #![warn(missing_docs)] #![doc = include_str!("../README.md")] +mod impls; + pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; /// Async reader. From 2c818b8b09aee0014a2b88e4997bd3a01e5f5a07 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 23:00:22 +0200 Subject: [PATCH 010/201] io-async: add std/alloc features, add Box/Vec impls. --- embedded-io-async/Cargo.toml | 8 ++++++ embedded-io-async/src/impls/boxx.rs | 44 +++++++++++++++++++++++++++++ embedded-io-async/src/impls/mod.rs | 5 ++++ embedded-io-async/src/impls/vec.rs | 12 ++++++++ embedded-io-async/src/lib.rs | 5 +++- 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 embedded-io-async/src/impls/boxx.rs create mode 100644 embedded-io-async/src/impls/vec.rs diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index e900278f9..e8f19af39 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -11,5 +11,13 @@ categories = [ "no-std", ] +[features] +std = ["alloc", "embedded-io/std"] +alloc = ["embedded-io/alloc"] + [dependencies] embedded-io = { version = "0.5", path = "../embedded-io" } + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io-async/src/impls/boxx.rs b/embedded-io-async/src/impls/boxx.rs new file mode 100644 index 000000000..677bf328d --- /dev/null +++ b/embedded-io-async/src/impls/boxx.rs @@ -0,0 +1,44 @@ +use crate::{BufRead, Read, Seek, SeekFrom, Write}; +use alloc::boxed::Box; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Read for Box { + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf).await + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl BufRead for Box { + #[inline] + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self).await + } + + #[inline] + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Box { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf).await + } + + #[inline] + async fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self).await + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Seek for Box { + #[inline] + async fn seek(&mut self, pos: SeekFrom) -> Result { + T::seek(self, pos).await + } +} diff --git a/embedded-io-async/src/impls/mod.rs b/embedded-io-async/src/impls/mod.rs index 7f4a6f802..e79b9b8bf 100644 --- a/embedded-io-async/src/impls/mod.rs +++ b/embedded-io-async/src/impls/mod.rs @@ -1,2 +1,7 @@ mod slice_mut; mod slice_ref; + +#[cfg(feature = "alloc")] +mod boxx; +#[cfg(feature = "alloc")] +mod vec; diff --git a/embedded-io-async/src/impls/vec.rs b/embedded-io-async/src/impls/vec.rs new file mode 100644 index 000000000..c24405e39 --- /dev/null +++ b/embedded-io-async/src/impls/vec.rs @@ -0,0 +1,12 @@ +use alloc::vec::Vec; + +use crate::Write; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Vec { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } +} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 0e120c40c..8ef951450 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -1,9 +1,12 @@ -#![no_std] #![feature(async_fn_in_trait, impl_trait_projections)] +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] +#[cfg(feature = "alloc")] +extern crate alloc; + mod impls; pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; From 931dd37ad66ad6588dcbbec9481e5f9323719e0a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 23:05:19 +0200 Subject: [PATCH 011/201] Remove serial traits (blocking and async). --- embedded-hal-async/src/lib.rs | 1 - embedded-hal-async/src/serial.rs | 27 --------- embedded-hal-nb/src/serial.rs | 76 ++++++++++++++++++++++++- embedded-hal/src/lib.rs | 1 - embedded-hal/src/serial.rs | 98 -------------------------------- 5 files changed, 73 insertions(+), 130 deletions(-) delete mode 100644 embedded-hal-async/src/serial.rs delete mode 100644 embedded-hal/src/serial.rs diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 83410cdb9..37a81c0ff 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -7,5 +7,4 @@ pub mod delay; pub mod digital; pub mod i2c; -pub mod serial; pub mod spi; diff --git a/embedded-hal-async/src/serial.rs b/embedded-hal-async/src/serial.rs deleted file mode 100644 index 60ee1bc05..000000000 --- a/embedded-hal-async/src/serial.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Serial interface - -pub use embedded_hal::serial::{Error, ErrorKind, ErrorType}; - -/// Write half of a serial interface -pub trait Write: ErrorType { - /// Writes a slice, blocking until everything has been written. - /// - /// An implementation can choose to buffer the write, returning `Ok(())` - /// after the complete slice has been written to a buffer, but before all - /// words have been sent via the serial interface. To make sure that - /// everything has been sent, call [`flush`](Write::flush) after this function returns. - async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; - - /// Ensures that none of the previously written data is still buffered - async fn flush(&mut self) -> Result<(), Self::Error>; -} - -impl, Word: 'static + Copy> Write for &mut T { - async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { - T::write(self, words).await - } - - async fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self).await - } -} diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index e01803ab1..c069c1441 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -1,6 +1,77 @@ //! Serial interface -pub use embedded_hal::serial::{Error, ErrorKind, ErrorType}; +/// Serial error +pub trait Error: core::fmt::Debug { + /// Convert error to a generic serial error kind + /// + /// By using this method, serial errors freely defined by HAL implementations + /// can be converted to a set of generic serial errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// Serial error kind +/// +/// This represents a common set of serial operation errors. HAL implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common serial errors, generic code can still react to them. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[non_exhaustive] +pub enum ErrorKind { + /// The peripheral receive buffer was overrun. + Overrun, + /// Received data does not conform to the peripheral configuration. + /// Can be caused by a misconfigured device on either end of the serial line. + FrameFormat, + /// Parity check failed. + Parity, + /// Serial line is too noisy to read valid data. + Noise, + /// A different error occurred. The original error may contain more information. + Other, +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), + Self::Parity => write!(f, "Parity check failed"), + Self::Noise => write!(f, "Serial line is too noisy to read valid data"), + Self::FrameFormat => write!( + f, + "Received data does not conform to the peripheral configuration" + ), + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +/// Serial error type trait +/// +/// This just defines the error type, to be used by the other traits. +pub trait ErrorType { + /// Error type + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} /// Read half of a serial interface /// @@ -40,8 +111,7 @@ impl, Word: Copy> Write for &mut T { /// /// TODO write example of usage -impl core::fmt::Write - for dyn Write + '_ +impl core::fmt::Write for dyn Write + '_ where Word: Copy + From, { diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index def14dbc0..ac09042b3 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -81,7 +81,6 @@ pub mod delay; pub mod digital; pub mod i2c; pub mod pwm; -pub mod serial; pub mod spi; mod private { diff --git a/embedded-hal/src/serial.rs b/embedded-hal/src/serial.rs deleted file mode 100644 index ab9be722b..000000000 --- a/embedded-hal/src/serial.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Serial traits - -/// Serial error -pub trait Error: core::fmt::Debug { - /// Convert error to a generic serial error kind - /// - /// By using this method, serial errors freely defined by HAL implementations - /// can be converted to a set of generic serial errors upon which generic - /// code can act. - fn kind(&self) -> ErrorKind; -} - -impl Error for core::convert::Infallible { - fn kind(&self) -> ErrorKind { - match *self {} - } -} - -/// Serial error kind -/// -/// This represents a common set of serial operation errors. HAL implementations are -/// free to define more specific or additional error types. However, by providing -/// a mapping to these common serial errors, generic code can still react to them. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[non_exhaustive] -pub enum ErrorKind { - /// The peripheral receive buffer was overrun. - Overrun, - /// Received data does not conform to the peripheral configuration. - /// Can be caused by a misconfigured device on either end of the serial line. - FrameFormat, - /// Parity check failed. - Parity, - /// Serial line is too noisy to read valid data. - Noise, - /// A different error occurred. The original error may contain more information. - Other, -} - -impl Error for ErrorKind { - fn kind(&self) -> ErrorKind { - *self - } -} - -impl core::fmt::Display for ErrorKind { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), - Self::Parity => write!(f, "Parity check failed"), - Self::Noise => write!(f, "Serial line is too noisy to read valid data"), - Self::FrameFormat => write!( - f, - "Received data does not conform to the peripheral configuration" - ), - Self::Other => write!( - f, - "A different error occurred. The original error may contain more information" - ), - } - } -} - -/// Serial error type trait -/// -/// This just defines the error type, to be used by the other traits. -pub trait ErrorType { - /// Error type - type Error: Error; -} - -impl ErrorType for &mut T { - type Error = T::Error; -} - -/// Write half of a serial interface (blocking variant) -pub trait Write: ErrorType { - /// Writes a slice, blocking until everything has been written - /// - /// An implementation can choose to buffer the write, returning `Ok(())` - /// after the complete slice has been written to a buffer, but before all - /// words have been sent via the serial interface. To make sure that - /// everything has been sent, call [`flush`](Write::flush) after this function returns. - fn write(&mut self, buffer: &[Word]) -> Result<(), Self::Error>; - - /// Block until the serial interface has sent all buffered words - fn flush(&mut self) -> Result<(), Self::Error>; -} - -impl, Word: Copy> Write for &mut T { - fn write(&mut self, buffer: &[Word]) -> Result<(), Self::Error> { - T::write(self, buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self) - } -} From 31b62d3a0775b2d4b5ea45e9500259f02af26daa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Jul 2023 00:09:19 +0200 Subject: [PATCH 012/201] io: add ReadReady, WriteReady. --- embedded-io-async/src/lib.rs | 4 +++- embedded-io/CHANGELOG.md | 1 + embedded-io/src/impls/boxx.rs | 18 ++++++++++++++- embedded-io/src/lib.rs | 41 +++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 8ef951450..f204eada5 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -9,7 +9,9 @@ extern crate alloc; mod impls; -pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; +pub use embedded_io::{ + Error, ErrorKind, Io, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, +}; /// Async reader. /// diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 43db9e22f..6af13a646 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the i/o handle is ready to read/write, so they allow using the traits in a non-blocking way. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs index 27bc1931c..727d1f21c 100644 --- a/embedded-io/src/impls/boxx.rs +++ b/embedded-io/src/impls/boxx.rs @@ -1,4 +1,4 @@ -use crate::{BufRead, Io, Read, Seek, Write}; +use crate::{BufRead, Io, Read, ReadReady, Seek, Write, WriteReady}; use alloc::boxed::Box; #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] @@ -45,3 +45,19 @@ impl Seek for Box { T::seek(self, pos) } } + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl ReadReady for Box { + #[inline] + fn read_ready(&mut self) -> Result { + T::read_ready(self) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl WriteReady for Box { + #[inline] + fn write_ready(&mut self) -> Result { + T::write_ready(self) + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 07bf60517..7f7aa2eb9 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -262,6 +262,33 @@ pub trait Seek: crate::Io { } } +/// Get whether a reader is ready. +/// +/// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read +/// only when it is ready. +pub trait ReadReady: crate::Io { + /// Get whether the reader is ready for immediately reading. + /// + /// This usually means that there is either some bytes have been received and are buffered and ready to be read, + /// or that the reader is at EOF. + /// + /// If this returns `true`, it's guaranteed that the next call to [`Read::read`] or [`BufRead::fill_buf`] will not block. + fn read_ready(&mut self) -> Result; +} + +/// Get whether a writer is ready. +/// +/// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write +/// only when it is ready. +pub trait WriteReady: crate::Io { + /// Get whether the writer is ready for immediately writeing. + /// + /// This usually means that there is free space in the internal transmit buffer. + /// + /// If this returns `true`, it's guaranteed that the next call to [`Write::write`] will not block. + fn write_ready(&mut self) -> Result; +} + impl Read for &mut T { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result { @@ -297,3 +324,17 @@ impl Seek for &mut T { T::seek(self, pos) } } + +impl ReadReady for &mut T { + #[inline] + fn read_ready(&mut self) -> Result { + T::read_ready(self) + } +} + +impl WriteReady for &mut T { + #[inline] + fn write_ready(&mut self) -> Result { + T::write_ready(self) + } +} From 5df51e56dae11fc4fbe1bb9fc6ed08ceb1958e2c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 23:02:02 +0200 Subject: [PATCH 013/201] ci: test io with std and alloc features, update nightly. --- .github/workflows/clippy.yml | 4 ++-- .github/workflows/test.yml | 4 +++- embedded-hal-async/src/lib.rs | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 20cd030f6..bdec38346 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -15,6 +15,6 @@ jobs: with: # embedded-hal-async needs nightly. # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) - toolchain: nightly-2022-11-22 + toolchain: nightly-2023-07-03 components: clippy - - run: cargo clippy --features=embedded-hal-bus/std -- --deny=warnings \ No newline at end of file + - run: cargo clippy --features=std -- --deny=warnings \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1677ea895..e50f0c6a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,9 @@ jobs: - thumbv7m-none-eabi include: - target: x86_64-unknown-linux-gnu - features: embedded-hal-bus/std + features: std + - target: x86_64-unknown-linux-gnu + features: alloc steps: - uses: actions/checkout@v3 diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 37a81c0ff..5fe9168e7 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -1,7 +1,6 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] -#![allow(incomplete_features)] #![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; From e36386845ce7fc6969e0ea5b3ae5042113e92df6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 12:48:27 +0200 Subject: [PATCH 014/201] Apply suggestions from code review Co-authored-by: Diego Barrios Romero --- embedded-io/CHANGELOG.md | 2 +- embedded-io/README.md | 2 +- embedded-io/src/adapters.rs | 14 +++++++------- embedded-io/src/lib.rs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 6af13a646..f7043a7de 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the i/o handle is ready to read/write, so they allow using the traits in a non-blocking way. +- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. diff --git a/embedded-io/README.md b/embedded-io/README.md index 33461b3cf..2563bdbf7 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -6,7 +6,7 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -IO traits for embedded systems. +Input/Output traits for embedded systems. Rust's `std::io` traits are not available in `no_std` targets, mainly because `std::io::Error` requires allocation. This crate contains replacement equivalent traits, usable in `no_std` diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs index 942d52455..741e243d0 100644 --- a/embedded-io/src/adapters.rs +++ b/embedded-io/src/adapters.rs @@ -3,9 +3,9 @@ //! To interoperate with `std::io`, wrap a type in one of these //! adapters. //! -//! There's no separate adapters for Read/ReadBuf/Write traits. Instead, a single +//! There are no separate adapters for `Read`/`ReadBuf`/`Write` traits. Instead, a single //! adapter implements the right traits based on what the inner type implements. -//! This allows adapting a `Read+Write`, for example. +//! This allows using these adapters when using `Read+Write`, for example. use crate::SeekFrom; @@ -107,26 +107,26 @@ impl ToStd { impl std::io::Read for ToStd { fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.read(buf).map_err(to_io_error) + self.inner.read(buf).map_err(to_std_error) } } impl std::io::Write for ToStd { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf).map_err(to_io_error) + self.inner.write(buf).map_err(to_std_error) } fn flush(&mut self) -> Result<(), std::io::Error> { - self.inner.flush().map_err(to_io_error) + self.inner.flush().map_err(to_std_error) } } impl std::io::Seek for ToStd { fn seek(&mut self, pos: std::io::SeekFrom) -> Result { - self.inner.seek(pos.into()).map_err(to_io_error) + self.inner.seek(pos.into()).map_err(to_std_error) } } -fn to_io_error(err: T) -> std::io::Error { +fn to_std_error(err: T) -> std::io::Error { let kind = std::io::ErrorKind::Other; std::io::Error::new(kind, format!("{:?}", err)) } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 7f7aa2eb9..40c98d0b2 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -281,7 +281,7 @@ pub trait ReadReady: crate::Io { /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. pub trait WriteReady: crate::Io { - /// Get whether the writer is ready for immediately writeing. + /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. /// From 7aaa585c634fb04ef5c12a8e190c1b728ef34db8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 13:26:23 +0200 Subject: [PATCH 015/201] Rename trait `Io` to `ErrorType`. --- embedded-io-async/src/lib.rs | 10 +++++----- embedded-io/CHANGELOG.md | 1 + embedded-io/src/adapters.rs | 2 +- embedded-io/src/impls/boxx.rs | 4 ++-- embedded-io/src/impls/slice_mut.rs | 4 ++-- embedded-io/src/impls/slice_ref.rs | 4 ++-- embedded-io/src/impls/vec.rs | 4 ++-- embedded-io/src/lib.rs | 18 +++++++++--------- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index f204eada5..d5a934ae2 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -10,13 +10,13 @@ extern crate alloc; mod impls; pub use embedded_io::{ - Error, ErrorKind, Io, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, + Error, ErrorKind, ErrorType, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, }; /// Async reader. /// /// Semantics are the same as [`std::io::Read`], check its documentation for details. -pub trait Read: Io { +pub trait Read: ErrorType { /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. async fn read(&mut self, buf: &mut [u8]) -> Result; @@ -40,7 +40,7 @@ pub trait Read: Io { /// Async buffered reader. /// /// Semantics are the same as [`std::io::BufRead`], check its documentation for details. -pub trait BufRead: Io { +pub trait BufRead: ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. async fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; @@ -51,7 +51,7 @@ pub trait BufRead: Io { /// Async writer. /// /// Semantics are the same as [`std::io::Write`], check its documentation for details. -pub trait Write: Io { +pub trait Write: ErrorType { /// Write a buffer into this writer, returning how many bytes were written. async fn write(&mut self, buf: &[u8]) -> Result; @@ -77,7 +77,7 @@ pub trait Write: Io { /// Async seek within streams. /// /// Semantics are the same as [`std::io::Seek`], check its documentation for details. -pub trait Seek: Io { +pub trait Seek: ErrorType { /// Seek to an offset, in bytes, in a stream. async fn seek(&mut self, pos: SeekFrom) -> Result; diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index f7043a7de..a91aabe37 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. +- Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs index 741e243d0..729f8dc0d 100644 --- a/embedded-io/src/adapters.rs +++ b/embedded-io/src/adapters.rs @@ -40,7 +40,7 @@ impl FromStd { } } -impl crate::Io for FromStd { +impl crate::ErrorType for FromStd { type Error = std::io::Error; } diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs index 727d1f21c..6e61ebf8d 100644 --- a/embedded-io/src/impls/boxx.rs +++ b/embedded-io/src/impls/boxx.rs @@ -1,8 +1,8 @@ -use crate::{BufRead, Io, Read, ReadReady, Seek, Write, WriteReady}; +use crate::{BufRead, ErrorType, Read, ReadReady, Seek, Write, WriteReady}; use alloc::boxed::Box; #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] -impl Io for Box { +impl ErrorType for Box { type Error = T::Error; } diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index cefaa296c..b89b72182 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -1,7 +1,7 @@ -use crate::{Io, Write}; +use crate::{ErrorType, Write}; use core::mem; -impl Io for &mut [u8] { +impl ErrorType for &mut [u8] { type Error = core::convert::Infallible; } diff --git a/embedded-io/src/impls/slice_ref.rs b/embedded-io/src/impls/slice_ref.rs index 73c98ab55..6332d70dd 100644 --- a/embedded-io/src/impls/slice_ref.rs +++ b/embedded-io/src/impls/slice_ref.rs @@ -1,6 +1,6 @@ -use crate::{BufRead, Io, Read}; +use crate::{BufRead, ErrorType, Read}; -impl Io for &[u8] { +impl ErrorType for &[u8] { type Error = core::convert::Infallible; } diff --git a/embedded-io/src/impls/vec.rs b/embedded-io/src/impls/vec.rs index 8aa1b62d6..3b279c564 100644 --- a/embedded-io/src/impls/vec.rs +++ b/embedded-io/src/impls/vec.rs @@ -1,8 +1,8 @@ -use crate::{Io, Write}; +use crate::{ErrorType, Write}; use alloc::vec::Vec; #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] -impl Io for Vec { +impl ErrorType for Vec { type Error = core::convert::Infallible; } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 40c98d0b2..b71c43f5a 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -55,7 +55,7 @@ impl Error for ErrorKind { } } -/// Base trait for all IO traits. +/// Base trait for all IO traits, defining the error type. /// /// All IO operations of all traits return the error defined in this trait. /// @@ -64,12 +64,12 @@ impl Error for ErrorKind { /// This is very convenient when writing generic code, it means you have to /// handle a single error type `T::Error`, instead of `::Error` and `::Error` /// which might be different types. -pub trait Io { +pub trait ErrorType { /// Error type of all the IO operations on this type. type Error: Error; } -impl crate::Io for &mut T { +impl crate::ErrorType for &mut T { type Error = T::Error; } @@ -150,7 +150,7 @@ impl std::error::Error for WriteAllError {} /// Blocking reader. /// /// Semantics are the same as [`std::io::Read`], check its documentation for details. -pub trait Read: crate::Io { +pub trait Read: crate::ErrorType { /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. fn read(&mut self, buf: &mut [u8]) -> Result; @@ -174,7 +174,7 @@ pub trait Read: crate::Io { /// Blocking buffered reader. /// /// Semantics are the same as [`std::io::BufRead`], check its documentation for details. -pub trait BufRead: crate::Io { +pub trait BufRead: crate::ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; @@ -185,7 +185,7 @@ pub trait BufRead: crate::Io { /// Blocking writer. /// /// Semantics are the same as [`std::io::Write`], check its documentation for details. -pub trait Write: crate::Io { +pub trait Write: crate::ErrorType { /// Write a buffer into this writer, returning how many bytes were written. fn write(&mut self, buf: &[u8]) -> Result; @@ -246,7 +246,7 @@ pub trait Write: crate::Io { /// Blocking seek within streams. /// /// Semantics are the same as [`std::io::Seek`], check its documentation for details. -pub trait Seek: crate::Io { +pub trait Seek: crate::ErrorType { /// Seek to an offset, in bytes, in a stream. fn seek(&mut self, pos: crate::SeekFrom) -> Result; @@ -266,7 +266,7 @@ pub trait Seek: crate::Io { /// /// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read /// only when it is ready. -pub trait ReadReady: crate::Io { +pub trait ReadReady: crate::ErrorType { /// Get whether the reader is ready for immediately reading. /// /// This usually means that there is either some bytes have been received and are buffered and ready to be read, @@ -280,7 +280,7 @@ pub trait ReadReady: crate::Io { /// /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. -pub trait WriteReady: crate::Io { +pub trait WriteReady: crate::ErrorType { /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. From e37cb225b59f2348ae3941ff4fd87616d4ab90f9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 15:53:44 +0200 Subject: [PATCH 016/201] io: add error kinds from std. --- embedded-io/src/adapters.rs | 42 +++++++++++++++++++++++++-- embedded-io/src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs index 729f8dc0d..d64c92e4b 100644 --- a/embedded-io/src/adapters.rs +++ b/embedded-io/src/adapters.rs @@ -126,15 +126,51 @@ impl std::io::Seek for ToStd { } } -fn to_std_error(err: T) -> std::io::Error { - let kind = std::io::ErrorKind::Other; +fn to_std_error(err: T) -> std::io::Error { + let kind = match err.kind() { + crate::ErrorKind::NotFound => std::io::ErrorKind::NotFound, + crate::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, + crate::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, + crate::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, + crate::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, + crate::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, + crate::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, + crate::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, + crate::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, + crate::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, + crate::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, + crate::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, + crate::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, + crate::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, + crate::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, + crate::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, + _ => std::io::ErrorKind::Other, + }; std::io::Error::new(kind, format!("{:?}", err)) } #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl crate::Error for std::io::Error { fn kind(&self) -> crate::ErrorKind { - crate::ErrorKind::Other + match self.kind() { + std::io::ErrorKind::NotFound => crate::ErrorKind::NotFound, + std::io::ErrorKind::PermissionDenied => crate::ErrorKind::PermissionDenied, + std::io::ErrorKind::ConnectionRefused => crate::ErrorKind::ConnectionRefused, + std::io::ErrorKind::ConnectionReset => crate::ErrorKind::ConnectionReset, + std::io::ErrorKind::ConnectionAborted => crate::ErrorKind::ConnectionAborted, + std::io::ErrorKind::NotConnected => crate::ErrorKind::NotConnected, + std::io::ErrorKind::AddrInUse => crate::ErrorKind::AddrInUse, + std::io::ErrorKind::AddrNotAvailable => crate::ErrorKind::AddrNotAvailable, + std::io::ErrorKind::BrokenPipe => crate::ErrorKind::BrokenPipe, + std::io::ErrorKind::AlreadyExists => crate::ErrorKind::AlreadyExists, + std::io::ErrorKind::InvalidInput => crate::ErrorKind::InvalidInput, + std::io::ErrorKind::InvalidData => crate::ErrorKind::InvalidData, + std::io::ErrorKind::TimedOut => crate::ErrorKind::TimedOut, + std::io::ErrorKind::Interrupted => crate::ErrorKind::Interrupted, + std::io::ErrorKind::Unsupported => crate::ErrorKind::Unsupported, + std::io::ErrorKind::OutOfMemory => crate::ErrorKind::OutOfMemory, + _ => crate::ErrorKind::Other, + } } } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index b71c43f5a..1a8487889 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -29,9 +29,67 @@ pub enum SeekFrom { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] /// Possible kinds of errors. +/// +/// This list is intended to grow over time and it is not recommended to +/// exhaustively match against it. In application code, use `match` for the `ErrorKind` +/// values you are expecting; use `_` to match "all other errors". +/// +/// This is the `embedded-io` equivalent of [`std::io::ErrorKind`], except with the following changes: +/// +/// - `WouldBlock` is removed, since `embedded-io` traits are always blocking. See the [crate-level documentation](crate) for details. +/// - `WriteZero` is removed, since it is a separate variant in [`WriteAllError`] and [`WriteFmtError`]. pub enum ErrorKind { /// Unspecified error kind. Other, + + /// An entity was not found, often a file. + NotFound, + /// The operation lacked the necessary privileges to complete. + PermissionDenied, + /// The connection was refused by the remote server. + ConnectionRefused, + /// The connection was reset by the remote server. + ConnectionReset, + /// The connection was aborted (terminated) by the remote server. + ConnectionAborted, + /// The network operation failed because it was not connected yet. + NotConnected, + /// A socket address could not be bound because the address is already in + /// use elsewhere. + AddrInUse, + /// A nonexistent interface was requested or the requested address was not + /// local. + AddrNotAvailable, + /// The operation failed because a pipe was closed. + BrokenPipe, + /// An entity already exists, often a file. + AlreadyExists, + /// A parameter was incorrect. + InvalidInput, + /// Data not valid for the operation were encountered. + /// + /// Unlike [`InvalidInput`], this typically means that the operation + /// parameters were valid, however the error was caused by malformed + /// input data. + /// + /// For example, a function that reads a file into a string will error with + /// `InvalidData` if the file's contents are not valid UTF-8. + /// + /// [`InvalidInput`]: ErrorKind::InvalidInput + InvalidData, + /// The I/O operation's timeout expired, causing it to be canceled. + TimedOut, + /// This operation was interrupted. + /// + /// Interrupted operations can typically be retried. + Interrupted, + /// This operation is unsupported on this platform. + /// + /// This means that the operation can never succeed. + Unsupported, + /// An operation could not be completed, because it failed + /// to allocate enough memory. + OutOfMemory, } /// Error trait. From b55faf99066a9486b7fc525e3944faee988be9c8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 15:00:53 +0200 Subject: [PATCH 017/201] io: expand docs. --- embedded-io-async/src/lib.rs | 74 +++++++++++++++++++++++++++++++++--- embedded-io/README.md | 8 +++- embedded-io/src/lib.rs | 69 +++++++++++++++++++++++++++++---- 3 files changed, 137 insertions(+), 14 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index d5a934ae2..c94e349a4 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -15,12 +15,41 @@ pub use embedded_io::{ /// Async reader. /// -/// Semantics are the same as [`std::io::Read`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::Read`]. pub trait Read: ErrorType { - /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + /// Read some bytes from this source into the specified buffer, returning how many bytes were read. + /// + /// If no bytes are currently available to read, this function waits until at least one byte is available. + /// + /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the + /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately + /// available. + /// + /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. + /// + /// If `buf.len() == 0`, `read` returns without waiting, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. + /// + /// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e. + /// guarantee that if you cancel (drop) a `read()` future that hasn't completed yet, the stream's + /// state hasn't changed (no bytes have been read). + /// + /// This is not a requirement to allow implementations that read into the user's buffer straight from + /// the hardware with e.g. DMA. + /// + /// Implementations should document whether they're actually side-effect-free on cancel or not. async fn read(&mut self, buf: &mut [u8]) -> Result; /// Read the exact number of bytes required to fill `buf`. + /// + /// This function calls `read()` in a loop until exactly `buf.len()` bytes have + /// been read, waiting if needed. + /// + /// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned + /// future that hasn't completed yet, some bytes might have already been read, which will get lost. async fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { while !buf.is_empty() { match self.read(buf).await { @@ -39,9 +68,15 @@ pub trait Read: ErrorType { /// Async buffered reader. /// -/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::BufRead`]. pub trait BufRead: ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + /// + /// If no bytes are currently available to read, this function waits until at least one byte is available. + /// + /// If the reader is at end-of-file (EOF), an empty slice is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. async fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. @@ -50,9 +85,32 @@ pub trait BufRead: ErrorType { /// Async writer. /// -/// Semantics are the same as [`std::io::Write`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::Write`]. pub trait Write: ErrorType { /// Write a buffer into this writer, returning how many bytes were written. + /// + /// If the writer is not currently ready to accept more bytes (for example, its buffer is full), + /// this function waits until it is ready to accept least one byte. + /// + /// If it's ready to accept bytes, a non-zero amount of bytes is written from the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available buffer space is filled, i.e. it is possible for the + /// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be + /// ready to accept more bytes immediately. + /// + /// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not + /// able to accept more bytes and likely never will are better indicated with errors. + /// + /// If `buf.len() == 0`, `write` returns without waiting, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate an error. + /// + /// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e. + /// guarantee that if you cancel (drop) a `write()` future that hasn't completed yet, the stream's + /// state hasn't changed (no bytes have been written). + /// + /// This is not a requirement to allow implementations that write from the user's buffer straight to + /// the hardware with e.g. DMA. + /// + /// Implementations should document whether they're actually side-effect-free on cancel or not. async fn write(&mut self, buf: &[u8]) -> Result; /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. @@ -61,6 +119,12 @@ pub trait Write: ErrorType { } /// Write an entire buffer into this writer. + /// + /// This function calls `write()` in a loop until exactly `buf.len()` bytes have + /// been written, waiting if needed. + /// + /// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned + /// future that hasn't completed yet, some bytes might have already been written. async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteAllError> { let mut buf = buf; while !buf.is_empty() { @@ -76,7 +140,7 @@ pub trait Write: ErrorType { /// Async seek within streams. /// -/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::Seek`]. pub trait Seek: ErrorType { /// Seek to an offset, in bytes, in a stream. async fn seek(&mut self, pos: SeekFrom) -> Result; diff --git a/embedded-io/README.md b/embedded-io/README.md index 2563bdbf7..d579b09f6 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -12,8 +12,12 @@ Rust's `std::io` traits are not available in `no_std` targets, mainly because `s requires allocation. This crate contains replacement equivalent traits, usable in `no_std` targets. -The only difference with `std::io` is `Error` is an associated type. This allows each implementor -to return its own error type, while avoiding `dyn` or `Box`. This is how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). +## Differences with `std::io` + +- `Error` is an associated type. This allows each implementor to return its own error type, +while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). +- In `std::io`, the `Read`/`Write` traits might be blocking or non-blocking (i.e. returning `WouldBlock` errors) depending on the file descriptor's mode, which is only known at run-time. This allows passing a non-blocking stream to code that expects a blocking +stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index b71c43f5a..c514e055e 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -15,7 +15,7 @@ mod impls; /// Enumeration of possible methods to seek within an I/O object. /// -/// Semantics are the same as [`std::io::SeekFrom`], check its documentation for details. +/// This is the `embedded-io` equivalent of [`std::io::SeekFrom`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. @@ -149,12 +149,33 @@ impl std::error::Error for WriteAllError {} /// Blocking reader. /// -/// Semantics are the same as [`std::io::Read`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::Read`]. pub trait Read: crate::ErrorType { - /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + /// Read some bytes from this source into the specified buffer, returning how many bytes were read. + /// + /// If no bytes are currently available to read, this function blocks until at least one byte is available. + /// + /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the + /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately + /// available. + /// + /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. + /// + /// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. fn read(&mut self, buf: &mut [u8]) -> Result; /// Read the exact number of bytes required to fill `buf`. + /// + /// This function calls `read()` in a loop until exactly `buf.len()` bytes have + /// been read, blocking if needed. + /// + /// If you are using [`ReadReady`] to avoid blocking, you should not use this function. + /// `ReadReady::read_ready()` returning true only guarantees the first call to `read()` will + /// not block, so this function may still block in subsequent calls. fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { while !buf.is_empty() { match self.read(buf) { @@ -173,9 +194,15 @@ pub trait Read: crate::ErrorType { /// Blocking buffered reader. /// -/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::BufRead`]. pub trait BufRead: crate::ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + /// + /// If no bytes are currently available to read, this function blocks until at least one byte is available. + /// + /// If the reader is at end-of-file (EOF), an empty slice is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. @@ -184,15 +211,36 @@ pub trait BufRead: crate::ErrorType { /// Blocking writer. /// -/// Semantics are the same as [`std::io::Write`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::Write`]. pub trait Write: crate::ErrorType { /// Write a buffer into this writer, returning how many bytes were written. + /// + /// If the writer is not currently ready to accept more bytes (for example, its buffer is full), + /// this function blocks until it is ready to accept least one byte. + /// + /// If it's ready to accept bytes, a non-zero amount of bytes is written from the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available buffer space is filled, i.e. it is possible for the + /// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be + /// ready to accept more bytes immediately. + /// + /// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not + /// able to accept more bytes and likely never will are better indicated with errors. + /// + /// If `buf.len() == 0`, `write` returns without blocking, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate an error. fn write(&mut self, buf: &[u8]) -> Result; - /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + /// Flush this output stream, blocking until all intermediately buffered contents reach their destination. fn flush(&mut self) -> Result<(), Self::Error>; /// Write an entire buffer into this writer. + /// + /// This function calls `write()` in a loop until exactly `buf.len()` bytes have + /// been written, blocking if needed. + /// + /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. + /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will + /// not block, so this function may still block in subsequent calls. fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError> { while !buf.is_empty() { match self.write(buf) { @@ -205,6 +253,13 @@ pub trait Write: crate::ErrorType { } /// Write a formatted string into this writer, returning any error encountered. + /// + /// This function calls `write()` in a loop until the entire formatted string has + /// been written, blocking if needed. + /// + /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. + /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will + /// not block, so this function may still block in subsequent calls. fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError> { // Create a shim which translates a Write to a fmt::Write and saves // off I/O errors. instead of discarding them @@ -245,7 +300,7 @@ pub trait Write: crate::ErrorType { /// Blocking seek within streams. /// -/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::Seek`]. pub trait Seek: crate::ErrorType { /// Seek to an offset, in bytes, in a stream. fn seek(&mut self, pos: crate::SeekFrom) -> Result; From 9ac61a73d78f071ff50198cdb8d22a992030f908 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 15:17:34 +0200 Subject: [PATCH 018/201] Add `+ ?Sized` to all blanket impls. --- Cargo.toml | 1 + embedded-hal-async/src/delay.rs | 2 +- embedded-hal-async/src/digital.rs | 2 +- embedded-hal-async/src/i2c.rs | 2 +- embedded-hal-async/src/spi.rs | 4 ++-- embedded-hal-nb/src/serial.rs | 6 +++--- embedded-hal-nb/src/spi.rs | 2 +- embedded-hal/src/delay.rs | 2 +- embedded-hal/src/digital.rs | 12 ++++++------ embedded-hal/src/i2c.rs | 4 ++-- embedded-hal/src/pwm.rs | 4 ++-- embedded-hal/src/spi.rs | 6 +++--- 12 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab7723059..ca4e0c584 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" # CI removes lines containing 'nightly-only' when not building with nightly. members = [ diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index 4b0223930..acf18f2af 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -13,7 +13,7 @@ pub trait DelayUs { impl DelayUs for &mut T where - T: DelayUs, + T: DelayUs + ?Sized, { async fn delay_us(&mut self, us: u32) { T::delay_us(self, us).await diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 600876975..8f09eb78d 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -48,7 +48,7 @@ pub trait Wait: embedded_hal::digital::ErrorType { async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error>; } -impl Wait for &mut T { +impl Wait for &mut T { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { T::wait_for_high(self).await } diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 67712dd46..0837da8dd 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -122,7 +122,7 @@ pub trait I2c: ErrorType { ) -> Result<(), Self::Error>; } -impl> I2c for &mut T { +impl + ?Sized> I2c for &mut T { async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read).await } diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 2c05f56e4..834a37fd2 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -76,7 +76,7 @@ pub trait SpiDevice: ErrorType { } } -impl> SpiDevice for &mut T { +impl + ?Sized> SpiDevice for &mut T { async fn transaction( &mut self, operations: &mut [Operation<'_, Word>], @@ -149,7 +149,7 @@ pub trait SpiBus: ErrorType { async fn flush(&mut self) -> Result<(), Self::Error>; } -impl, Word: 'static + Copy> SpiBus for &mut T { +impl + ?Sized, Word: 'static + Copy> SpiBus for &mut T { async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words).await } diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index c069c1441..624aadee3 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -69,7 +69,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -82,7 +82,7 @@ pub trait Read: ErrorType { fn read(&mut self) -> nb::Result; } -impl, Word: Copy> Read for &mut T { +impl + ?Sized, Word: Copy> Read for &mut T { fn read(&mut self) -> nb::Result { T::read(self) } @@ -97,7 +97,7 @@ pub trait Write: ErrorType { fn flush(&mut self) -> nb::Result<(), Self::Error>; } -impl, Word: Copy> Write for &mut T { +impl + ?Sized, Word: Copy> Write for &mut T { fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { T::write(self, word) } diff --git a/embedded-hal-nb/src/spi.rs b/embedded-hal-nb/src/spi.rs index a9e7c94f0..ecdb88ffb 100644 --- a/embedded-hal-nb/src/spi.rs +++ b/embedded-hal-nb/src/spi.rs @@ -31,7 +31,7 @@ pub trait FullDuplex: ErrorType { fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; } -impl, Word: Copy> FullDuplex for &mut T { +impl + ?Sized, Word: Copy> FullDuplex for &mut T { fn read(&mut self) -> nb::Result { T::read(self) } diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 7486f412f..da4cd7e2d 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -18,7 +18,7 @@ pub trait DelayUs { impl DelayUs for &mut T where - T: DelayUs, + T: DelayUs + ?Sized, { fn delay_us(&mut self, us: u32) { T::delay_us(self, us) diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index c476cb769..677bb64d1 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -55,11 +55,11 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &T { +impl ErrorType for &T { type Error = T::Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -139,7 +139,7 @@ pub trait OutputPin: ErrorType { } } -impl OutputPin for &mut T { +impl OutputPin for &mut T { fn set_low(&mut self) -> Result<(), Self::Error> { T::set_low(self) } @@ -166,7 +166,7 @@ pub trait StatefulOutputPin: OutputPin { fn is_set_low(&self) -> Result; } -impl StatefulOutputPin for &mut T { +impl StatefulOutputPin for &mut T { fn is_set_high(&self) -> Result { T::is_set_high(self) } @@ -182,7 +182,7 @@ pub trait ToggleableOutputPin: ErrorType { fn toggle(&mut self) -> Result<(), Self::Error>; } -impl ToggleableOutputPin for &mut T { +impl ToggleableOutputPin for &mut T { fn toggle(&mut self) -> Result<(), Self::Error> { T::toggle(self) } @@ -197,7 +197,7 @@ pub trait InputPin: ErrorType { fn is_low(&self) -> Result; } -impl InputPin for &T { +impl InputPin for &T { fn is_high(&self) -> Result { T::is_high(self) } diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index fdaf9e15c..735408758 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -249,7 +249,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -372,7 +372,7 @@ pub trait I2c: ErrorType { ) -> Result<(), Self::Error>; } -impl> I2c for &mut T { +impl + ?Sized> I2c for &mut T { fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read) } diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 1ba9c4df0..4c0f6ac60 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -53,7 +53,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -101,7 +101,7 @@ pub trait SetDutyCycle: ErrorType { } } -impl SetDutyCycle for &mut T { +impl SetDutyCycle for &mut T { fn get_max_duty_cycle(&self) -> u16 { T::get_max_duty_cycle(self) } diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 3bcdda795..54bf8bcea 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -288,7 +288,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -378,7 +378,7 @@ pub trait SpiDevice: ErrorType { } } -impl> SpiDevice for &mut T { +impl + ?Sized> SpiDevice for &mut T { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { T::transaction(self, operations) } @@ -448,7 +448,7 @@ pub trait SpiBus: ErrorType { fn flush(&mut self) -> Result<(), Self::Error>; } -impl, Word: Copy + 'static> SpiBus for &mut T { +impl + ?Sized, Word: Copy + 'static> SpiBus for &mut T { fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words) } From 93a50114311ad6cd4bade5d1dd41134fbf14f704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 12 Jul 2023 22:00:03 +0200 Subject: [PATCH 019/201] Add bus access methods to ExclusiveDevice --- embedded-hal-async/CHANGELOG.md | 3 +++ embedded-hal-async/src/spi.rs | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index c1b4de3c3..5ea703cdd 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- spi: added `ExclusiveDevice::{bus, bus_mut}`. + ## [v0.2.0-alpha.2] - 2023-07-04 ### Added diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 834a37fd2..1e852e34b 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -208,6 +208,16 @@ impl ExclusiveDevice { pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } + + /// Returns a reference to the underlying bus object. + pub fn bus(&self) -> &BUS { + &self.bus + } + + /// Returns a mutable reference to the underlying bus object. + pub fn bus_mut(&mut self) -> &mut BUS { + &mut self.bus + } } impl ErrorType for ExclusiveDevice From 3f4c8d95758c1eef5733f082aca2d6c2a25e59c7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 18:41:05 +0200 Subject: [PATCH 020/201] io: remove useless `crate::`. --- embedded-io/src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 81851ae50..2c50deca9 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -127,7 +127,7 @@ pub trait ErrorType { type Error: Error; } -impl crate::ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -208,7 +208,7 @@ impl std::error::Error for WriteAllError {} /// Blocking reader. /// /// This trait is the `embedded-io` equivalent of [`std::io::Read`]. -pub trait Read: crate::ErrorType { +pub trait Read: ErrorType { /// Read some bytes from this source into the specified buffer, returning how many bytes were read. /// /// If no bytes are currently available to read, this function blocks until at least one byte is available. @@ -253,7 +253,7 @@ pub trait Read: crate::ErrorType { /// Blocking buffered reader. /// /// This trait is the `embedded-io` equivalent of [`std::io::BufRead`]. -pub trait BufRead: crate::ErrorType { +pub trait BufRead: ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. /// /// If no bytes are currently available to read, this function blocks until at least one byte is available. @@ -270,7 +270,7 @@ pub trait BufRead: crate::ErrorType { /// Blocking writer. /// /// This trait is the `embedded-io` equivalent of [`std::io::Write`]. -pub trait Write: crate::ErrorType { +pub trait Write: ErrorType { /// Write a buffer into this writer, returning how many bytes were written. /// /// If the writer is not currently ready to accept more bytes (for example, its buffer is full), @@ -359,19 +359,19 @@ pub trait Write: crate::ErrorType { /// Blocking seek within streams. /// /// This trait is the `embedded-io` equivalent of [`std::io::Seek`]. -pub trait Seek: crate::ErrorType { +pub trait Seek: ErrorType { /// Seek to an offset, in bytes, in a stream. - fn seek(&mut self, pos: crate::SeekFrom) -> Result; + fn seek(&mut self, pos: SeekFrom) -> Result; /// Rewind to the beginning of a stream. fn rewind(&mut self) -> Result<(), Self::Error> { - self.seek(crate::SeekFrom::Start(0))?; + self.seek(SeekFrom::Start(0))?; Ok(()) } /// Returns the current seek position from the start of the stream. fn stream_position(&mut self) -> Result { - self.seek(crate::SeekFrom::Current(0)) + self.seek(SeekFrom::Current(0)) } } @@ -379,7 +379,7 @@ pub trait Seek: crate::ErrorType { /// /// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read /// only when it is ready. -pub trait ReadReady: crate::ErrorType { +pub trait ReadReady: ErrorType { /// Get whether the reader is ready for immediately reading. /// /// This usually means that there is either some bytes have been received and are buffered and ready to be read, @@ -393,7 +393,7 @@ pub trait ReadReady: crate::ErrorType { /// /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. -pub trait WriteReady: crate::ErrorType { +pub trait WriteReady: ErrorType { /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. @@ -433,7 +433,7 @@ impl Write for &mut T { impl Seek for &mut T { #[inline] - fn seek(&mut self, pos: crate::SeekFrom) -> Result { + fn seek(&mut self, pos: SeekFrom) -> Result { T::seek(self, pos) } } From 3a4814f33a64e8ac416faa0f7440041079558b89 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 19:02:39 +0200 Subject: [PATCH 021/201] io: Add `From` impls to convert between `ErrorKind` and `std::io::ErrorKind`. --- embedded-io/CHANGELOG.md | 4 +++- embedded-io/src/lib.rs | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index a91aabe37..45a1445e3 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. +- Add `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. +- Add variants to `ErrorKind` mirroring `std::io::ErrorKind`. +- Add `From` impls to convert between `ErrorKind` and `std::io::ErrorKind`. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 2c50deca9..ceb176aeb 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -92,6 +92,58 @@ pub enum ErrorKind { OutOfMemory, } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for std::io::ErrorKind { + fn from(value: ErrorKind) -> Self { + match value { + ErrorKind::NotFound => std::io::ErrorKind::NotFound, + ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, + ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, + ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, + ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, + ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, + ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, + ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, + ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, + ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, + ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, + ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, + ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, + ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, + ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, + ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, + _ => std::io::ErrorKind::Other, + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for ErrorKind { + fn from(value: std::io::ErrorKind) -> Self { + match value { + std::io::ErrorKind::NotFound => ErrorKind::NotFound, + std::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied, + std::io::ErrorKind::ConnectionRefused => ErrorKind::ConnectionRefused, + std::io::ErrorKind::ConnectionReset => ErrorKind::ConnectionReset, + std::io::ErrorKind::ConnectionAborted => ErrorKind::ConnectionAborted, + std::io::ErrorKind::NotConnected => ErrorKind::NotConnected, + std::io::ErrorKind::AddrInUse => ErrorKind::AddrInUse, + std::io::ErrorKind::AddrNotAvailable => ErrorKind::AddrNotAvailable, + std::io::ErrorKind::BrokenPipe => ErrorKind::BrokenPipe, + std::io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists, + std::io::ErrorKind::InvalidInput => ErrorKind::InvalidInput, + std::io::ErrorKind::InvalidData => ErrorKind::InvalidData, + std::io::ErrorKind::TimedOut => ErrorKind::TimedOut, + std::io::ErrorKind::Interrupted => ErrorKind::Interrupted, + std::io::ErrorKind::Unsupported => ErrorKind::Unsupported, + std::io::ErrorKind::OutOfMemory => ErrorKind::OutOfMemory, + _ => ErrorKind::Other, + } + } +} + /// Error trait. /// /// This trait allows generic code to do limited inspecting of errors, From f04beb5e02915a2aa51d55cb2349f0031b9f23a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 19:12:04 +0200 Subject: [PATCH 022/201] io: remove adapters from main crate. --- embedded-io/CHANGELOG.md | 2 +- embedded-io/src/adapters.rs | 197 ------------------------------------ embedded-io/src/lib.rs | 35 ++++++- 3 files changed, 33 insertions(+), 201 deletions(-) delete mode 100644 embedded-io/src/adapters.rs diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 45a1445e3..d5038933e 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `From` impls to convert between `ErrorKind` and `std::io::ErrorKind`. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. -- Split async trait adapters to separate crates. +- Split trait adapters to the `embedded-io-adapters` crate. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs deleted file mode 100644 index d64c92e4b..000000000 --- a/embedded-io/src/adapters.rs +++ /dev/null @@ -1,197 +0,0 @@ -//! Adapters to/from `std::io` traits. -//! -//! To interoperate with `std::io`, wrap a type in one of these -//! adapters. -//! -//! There are no separate adapters for `Read`/`ReadBuf`/`Write` traits. Instead, a single -//! adapter implements the right traits based on what the inner type implements. -//! This allows using these adapters when using `Read+Write`, for example. - -use crate::SeekFrom; - -/// Adapter from `std::io` traits. -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -#[derive(Clone)] -pub struct FromStd { - inner: T, -} - -impl FromStd { - /// Create a new adapter. - pub fn new(inner: T) -> Self { - Self { inner } - } - - /// Consume the adapter, returning the inner object. - pub fn into_inner(self) -> T { - self.inner - } -} - -impl FromStd { - /// Borrow the inner object. - pub fn inner(&self) -> &T { - &self.inner - } - - /// Mutably borrow the inner object. - pub fn inner_mut(&mut self) -> &mut T { - &mut self.inner - } -} - -impl crate::ErrorType for FromStd { - type Error = std::io::Error; -} - -impl crate::Read for FromStd { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.read(buf) - } -} - -impl crate::BufRead for FromStd { - fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner.fill_buf() - } - - fn consume(&mut self, amt: usize) { - self.inner.consume(amt) - } -} - -impl crate::Write for FromStd { - fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf) - } - fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.flush() - } -} - -impl crate::Seek for FromStd { - fn seek(&mut self, pos: crate::SeekFrom) -> Result { - self.inner.seek(pos.into()) - } -} - -/// Adapter to `std::io` traits. -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -pub struct ToStd { - inner: T, -} - -impl ToStd { - /// Create a new adapter. - pub fn new(inner: T) -> Self { - Self { inner } - } - - /// Consume the adapter, returning the inner object. - pub fn into_inner(self) -> T { - self.inner - } -} - -impl ToStd { - /// Borrow the inner object. - pub fn inner(&self) -> &T { - &self.inner - } - - /// Mutably borrow the inner object. - pub fn inner_mut(&mut self) -> &mut T { - &mut self.inner - } -} - -impl std::io::Read for ToStd { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.read(buf).map_err(to_std_error) - } -} - -impl std::io::Write for ToStd { - fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf).map_err(to_std_error) - } - fn flush(&mut self) -> Result<(), std::io::Error> { - self.inner.flush().map_err(to_std_error) - } -} - -impl std::io::Seek for ToStd { - fn seek(&mut self, pos: std::io::SeekFrom) -> Result { - self.inner.seek(pos.into()).map_err(to_std_error) - } -} - -fn to_std_error(err: T) -> std::io::Error { - let kind = match err.kind() { - crate::ErrorKind::NotFound => std::io::ErrorKind::NotFound, - crate::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, - crate::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, - crate::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, - crate::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, - crate::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, - crate::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, - crate::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, - crate::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, - crate::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, - crate::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, - crate::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, - crate::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, - crate::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, - crate::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, - crate::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, - _ => std::io::ErrorKind::Other, - }; - std::io::Error::new(kind, format!("{:?}", err)) -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl crate::Error for std::io::Error { - fn kind(&self) -> crate::ErrorKind { - match self.kind() { - std::io::ErrorKind::NotFound => crate::ErrorKind::NotFound, - std::io::ErrorKind::PermissionDenied => crate::ErrorKind::PermissionDenied, - std::io::ErrorKind::ConnectionRefused => crate::ErrorKind::ConnectionRefused, - std::io::ErrorKind::ConnectionReset => crate::ErrorKind::ConnectionReset, - std::io::ErrorKind::ConnectionAborted => crate::ErrorKind::ConnectionAborted, - std::io::ErrorKind::NotConnected => crate::ErrorKind::NotConnected, - std::io::ErrorKind::AddrInUse => crate::ErrorKind::AddrInUse, - std::io::ErrorKind::AddrNotAvailable => crate::ErrorKind::AddrNotAvailable, - std::io::ErrorKind::BrokenPipe => crate::ErrorKind::BrokenPipe, - std::io::ErrorKind::AlreadyExists => crate::ErrorKind::AlreadyExists, - std::io::ErrorKind::InvalidInput => crate::ErrorKind::InvalidInput, - std::io::ErrorKind::InvalidData => crate::ErrorKind::InvalidData, - std::io::ErrorKind::TimedOut => crate::ErrorKind::TimedOut, - std::io::ErrorKind::Interrupted => crate::ErrorKind::Interrupted, - std::io::ErrorKind::Unsupported => crate::ErrorKind::Unsupported, - std::io::ErrorKind::OutOfMemory => crate::ErrorKind::OutOfMemory, - _ => crate::ErrorKind::Other, - } - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From for std::io::SeekFrom { - fn from(pos: SeekFrom) -> Self { - match pos { - SeekFrom::Start(n) => std::io::SeekFrom::Start(n), - SeekFrom::End(n) => std::io::SeekFrom::End(n), - SeekFrom::Current(n) => std::io::SeekFrom::Current(n), - } - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From for SeekFrom { - fn from(pos: std::io::SeekFrom) -> SeekFrom { - match pos { - std::io::SeekFrom::Start(n) => SeekFrom::Start(n), - std::io::SeekFrom::End(n) => SeekFrom::End(n), - std::io::SeekFrom::Current(n) => SeekFrom::Current(n), - } - } -} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index ceb176aeb..454ac8642 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -8,9 +8,6 @@ use core::fmt; #[cfg(feature = "alloc")] extern crate alloc; -#[cfg(feature = "std")] -pub mod adapters; - mod impls; /// Enumeration of possible methods to seek within an I/O object. @@ -26,6 +23,30 @@ pub enum SeekFrom { Current(i64), } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for std::io::SeekFrom { + fn from(pos: SeekFrom) -> Self { + match pos { + SeekFrom::Start(n) => std::io::SeekFrom::Start(n), + SeekFrom::End(n) => std::io::SeekFrom::End(n), + SeekFrom::Current(n) => std::io::SeekFrom::Current(n), + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for SeekFrom { + fn from(pos: std::io::SeekFrom) -> SeekFrom { + match pos { + std::io::SeekFrom::Start(n) => SeekFrom::Start(n), + std::io::SeekFrom::End(n) => SeekFrom::End(n), + std::io::SeekFrom::Current(n) => SeekFrom::Current(n), + } + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] /// Possible kinds of errors. @@ -165,6 +186,14 @@ impl Error for ErrorKind { } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl crate::Error for std::io::Error { + fn kind(&self) -> crate::ErrorKind { + self.kind().into() + } +} + /// Base trait for all IO traits, defining the error type. /// /// All IO operations of all traits return the error defined in this trait. From 8db8b0bb211e9002d8d55cb7a5e3b14f50268327 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 19:25:57 +0200 Subject: [PATCH 023/201] io: add separate embedded-io-adapters crate, add futures/tokio support. If we want to release `embedded-io` 1.0 and the adapters live there, we can't make any breaking changes to them after release. Putting them in a separate crate allows doing breaking changes without breaking everyone using the IO traits. --- .github/workflows/clippy.yml | 2 +- .github/workflows/test.yml | 4 +- Cargo.toml | 1 + embedded-io-adapters/Cargo.toml | 28 ++++ embedded-io-adapters/LICENSE-APACHE | 201 +++++++++++++++++++++++++ embedded-io-adapters/LICENSE-MIT | 25 +++ embedded-io-adapters/README.md | 56 +++++++ embedded-io-adapters/src/futures_03.rs | 66 ++++++++ embedded-io-adapters/src/lib.rs | 20 +++ embedded-io-adapters/src/std.rs | 139 +++++++++++++++++ embedded-io-adapters/src/tokio_1.rs | 81 ++++++++++ 11 files changed, 621 insertions(+), 2 deletions(-) create mode 100644 embedded-io-adapters/Cargo.toml create mode 100644 embedded-io-adapters/LICENSE-APACHE create mode 100644 embedded-io-adapters/LICENSE-MIT create mode 100644 embedded-io-adapters/README.md create mode 100644 embedded-io-adapters/src/futures_03.rs create mode 100644 embedded-io-adapters/src/lib.rs create mode 100644 embedded-io-adapters/src/std.rs create mode 100644 embedded-io-adapters/src/tokio_1.rs diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index bdec38346..1479e4a56 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -17,4 +17,4 @@ jobs: # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) toolchain: nightly-2023-07-03 components: clippy - - run: cargo clippy --features=std -- --deny=warnings \ No newline at end of file + - run: cargo clippy --all-features -- --deny=warnings \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e50f0c6a4..44a82a39b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,9 @@ jobs: features: std - target: x86_64-unknown-linux-gnu features: alloc - + - target: x86_64-unknown-linux-gnu + features: std,tokio-1,futures-03 + rust: nightly steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master diff --git a/Cargo.toml b/Cargo.toml index ca4e0c584..ccfa1e0f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ members = [ "embedded-can", "embedded-io", "embedded-io-async", # nightly-only + "embedded-io-adapters", ] diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml new file mode 100644 index 000000000..05905ad60 --- /dev/null +++ b/embedded-io-adapters/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "embedded-io-adapters" +version = "0.1.0" +edition = "2021" +description = "Adapters between the `embedded-io` traits and other I/O traits" +repository = "https://github.com/rust-embedded/embedded-hal" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", +] + +[features] +std = ["embedded-io/std"] +tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std"] +futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"] + +[dependencies] +embedded-io = { version = "0.5", path = "../embedded-io" } +embedded-io-async = { version = "0.5", path = "../embedded-io-async", optional = true } + +futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } +tokio = { version = "1", default-features = false, optional = true } + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io-adapters/LICENSE-APACHE b/embedded-io-adapters/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/embedded-io-adapters/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/embedded-io-adapters/LICENSE-MIT b/embedded-io-adapters/LICENSE-MIT new file mode 100644 index 000000000..e00608fbd --- /dev/null +++ b/embedded-io-adapters/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The embedded-io authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md new file mode 100644 index 000000000..e4ad0278e --- /dev/null +++ b/embedded-io-adapters/README.md @@ -0,0 +1,56 @@ +[![crates.io](https://img.shields.io/crates/d/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) + +# `embedded-io-adapters` + +This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). + +Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits. + +The adapters are structs that wrap an I/O stream and implement another family of I/O traits +based on the wrapped streams. This allows "converting" from an `embedded_io::Read` +to a `std::io::Read` or vice versa, for example. + +There are no separate adapters for `Read`/`ReadBuf`/`Write` traits. Instead, a single +adapter implements the right traits based on what the inner type implements. +This allows using these adapters when using combinations of traits, like `Read+Write`. + +## Supported traits + +For `embedded-io`: + +- [`std::io`](https://doc.rust-lang.org/stable/std/io/index.html) traits. Needs the `std` feature. + +For `embedded-io-async`: + +- [`futures` 0.3](https://crates.io/crates/futures) traits. Needs the `futures-03` feature. +- [`tokio` 1.x](https://crates.io/crates/tokio) traits. Needs the `tokio-1` feature. + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +compile with older versions but that may change in any new patch release. + +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. + +Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust nightly newer than +`nightly-2022-11-22`, due to requiring support for `async fn` in traits (AFIT), +which is not stable yet. Keep in mind Rust nightlies can make backwards-incompatible +changes to unstable features at any time. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs new file mode 100644 index 000000000..b672215ed --- /dev/null +++ b/embedded-io-adapters/src/futures_03.rs @@ -0,0 +1,66 @@ +//! Adapters to/from `futures::io` traits. + +use core::future::poll_fn; +use core::pin::Pin; + +/// Adapter from `futures::io` traits. +#[derive(Clone)] +pub struct FromFutures { + inner: T, +} + +impl FromFutures { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromFutures { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl embedded_io::ErrorType for FromFutures { + type Error = std::io::Error; +} + +impl embedded_io_async::Read for FromFutures { + async fn read(&mut self, buf: &mut [u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_read(cx, buf)).await + } +} + +impl embedded_io_async::Write for FromFutures { + async fn write(&mut self, buf: &[u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await + } +} + +impl embedded_io_async::Seek for FromFutures { + async fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { + poll_fn(move |cx| Pin::new(&mut self.inner).poll_seek(cx, pos.into())).await + } +} + +// TODO: ToFutures. +// It's a bit tricky because futures::io is "stateless", while we're "stateful" (we +// return futures that borrow Self and get polled for the duration of the operation.) +// It can probably done by storing the futures in Self, with unsafe Pin hacks because +// we're a self-referential struct diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs new file mode 100644 index 000000000..d11316405 --- /dev/null +++ b/embedded-io-adapters/src/lib.rs @@ -0,0 +1,20 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr( + any(feature = "tokio-1", feature = "futures-03"), + feature(async_fn_in_trait, impl_trait_projections) +)] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub mod std; + +#[cfg(feature = "futures-03")] +#[cfg_attr(docsrs, doc(cfg(feature = "futures-03")))] +pub mod futures_03; + +#[cfg(feature = "tokio-1")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio-1")))] +pub mod tokio_1; diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs new file mode 100644 index 000000000..7449c805b --- /dev/null +++ b/embedded-io-adapters/src/std.rs @@ -0,0 +1,139 @@ +//! Adapters to/from `std::io` traits. + +/// Adapter from `std::io` traits. +#[derive(Clone)] +pub struct FromStd { + inner: T, +} + +impl FromStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl embedded_io::ErrorType for FromStd { + type Error = std::io::Error; +} + +impl embedded_io::Read for FromStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf) + } +} + +impl embedded_io::BufRead for FromStd { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.inner.consume(amt) + } +} + +impl embedded_io::Write for FromStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf) + } + fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.flush() + } +} + +impl embedded_io::Seek for FromStd { + fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { + self.inner.seek(pos.into()) + } +} + +/// Adapter to `std::io` traits. +pub struct ToStd { + inner: T, +} + +impl ToStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl ToStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl std::io::Read for ToStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf).map_err(to_std_error) + } +} + +impl std::io::Write for ToStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf).map_err(to_std_error) + } + fn flush(&mut self) -> Result<(), std::io::Error> { + self.inner.flush().map_err(to_std_error) + } +} + +impl std::io::Seek for ToStd { + fn seek(&mut self, pos: std::io::SeekFrom) -> Result { + self.inner.seek(pos.into()).map_err(to_std_error) + } +} + +fn to_std_error(err: T) -> std::io::Error { + let kind = match err.kind() { + embedded_io::ErrorKind::NotFound => std::io::ErrorKind::NotFound, + embedded_io::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, + embedded_io::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, + embedded_io::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, + embedded_io::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, + embedded_io::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, + embedded_io::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, + embedded_io::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, + embedded_io::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, + embedded_io::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, + embedded_io::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, + embedded_io::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, + embedded_io::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, + embedded_io::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, + embedded_io::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, + embedded_io::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, + _ => std::io::ErrorKind::Other, + }; + std::io::Error::new(kind, format!("{:?}", err)) +} diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs new file mode 100644 index 000000000..04e1cf1aa --- /dev/null +++ b/embedded-io-adapters/src/tokio_1.rs @@ -0,0 +1,81 @@ +//! Adapters to/from `tokio::io` traits. + +use core::future::poll_fn; +use core::pin::Pin; +use core::task::Poll; + +/// Adapter from `tokio::io` traits. +#[derive(Clone)] +pub struct FromTokio { + inner: T, +} + +impl FromTokio { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromTokio { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl embedded_io::ErrorType for FromTokio { + type Error = std::io::Error; +} + +impl embedded_io_async::Read for FromTokio { + async fn read(&mut self, buf: &mut [u8]) -> Result { + poll_fn(|cx| { + let mut buf = tokio::io::ReadBuf::new(buf); + match Pin::new(&mut self.inner).poll_read(cx, &mut buf) { + Poll::Ready(r) => match r { + Ok(()) => Poll::Ready(Ok(buf.filled().len())), + Err(e) => Poll::Ready(Err(e)), + }, + Poll::Pending => Poll::Pending, + } + }) + .await + } +} + +impl embedded_io_async::Write for FromTokio { + async fn write(&mut self, buf: &[u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await + } +} + +impl embedded_io_async::Seek for FromTokio { + async fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { + // Note: `start_seek` can return an error if there is another seek in progress. + // Therefor it is recommended to call `poll_complete` before any call to `start_seek`. + poll_fn(|cx| Pin::new(&mut self.inner).poll_complete(cx)).await?; + Pin::new(&mut self.inner).start_seek(pos.into())?; + poll_fn(|cx| Pin::new(&mut self.inner).poll_complete(cx)).await + } +} + +// TODO: ToTokio. +// It's a bit tricky because tokio::io is "stateless", while we're "stateful" (we +// return futures that borrow Self and get polled for the duration of the operation.) +// It can probably done by storing the futures in Self, with unsafe Pin hacks because +// we're a self-referential struct From 7d975f025d1eefaf0a4a0b4f69053f3b9ee889f4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Jul 2023 21:01:43 +0200 Subject: [PATCH 024/201] Bump MSRV to 1.60 Needed for namespaced features (`dep:` in Cargo.toml) --- .github/workflows/test.yml | 2 +- README.md | 2 +- embedded-can/README.md | 4 ++-- embedded-hal-bus/README.md | 4 ++-- embedded-hal-nb/README.md | 4 ++-- embedded-hal/CHANGELOG.md | 2 +- embedded-hal/README.md | 4 ++-- embedded-io-adapters/README.md | 2 +- embedded-io/README.md | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 44a82a39b..9314fc3d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: matrix: rust: - stable - - 1.59.0 # MSRV + - 1.60.0 # MSRV - nightly target: - x86_64-unknown-linux-gnu diff --git a/README.md b/README.md index 6067b23b8..3b8692783 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ on crates.io. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-can/README.md b/embedded-can/README.md index 3380c43f3..d0b7d05d9 100644 --- a/embedded-can/README.md +++ b/embedded-can/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-can.svg)](https://crates.io/crates/embedded-can) [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-can` @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 81bf52c2c..394ffb88b 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-hal-bus` @@ -34,7 +34,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal-nb/README.md b/embedded-hal-nb/README.md index f04b79475..aa72e7b1e 100644 --- a/embedded-hal-nb/README.md +++ b/embedded-hal-nb/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-hal-nb` @@ -17,7 +17,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 0803cb8f7..ccb13c888 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -65,7 +65,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). *** This is (also) an alpha release with breaking changes (sorry) *** ### Changed -- The Minimum Supported Rust Version (MSRV) is now 1.59.0 +- The Minimum Supported Rust Version (MSRV) is now 1.60.0 - `spi`: unify all traits into `SpiReadBus`, `SpiWriteBus` and `SpiBus` (read-write). - `spi`: Add `SpiDevice` trait to represent a single device in a (possibly shared) bus, with managed chip-select (CS) pin. - `spi`: Clarify that implementations are allowed to return before operations are finished, add `flush` to wait until finished. diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 902e64883..98871ebb9 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-hal` @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md index e4ad0278e..0cdb8c558 100644 --- a/embedded-io-adapters/README.md +++ b/embedded-io-adapters/README.md @@ -29,7 +29,7 @@ For `embedded-io-async`: ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-io/README.md b/embedded-io/README.md index d579b09f6..3318daa2b 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -21,7 +21,7 @@ stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read` ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. From 981caa649b280fe2df1a23b2c611486dcdd16032 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 13:05:54 +0200 Subject: [PATCH 025/201] Fix workspace issues when building for stable. --- .github/workflows/test.yml | 2 +- Cargo.stable.toml | 20 ++++++++++++++++++++ Cargo.toml | 5 ++--- 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 Cargo.stable.toml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9314fc3d9..a2fa0d9d7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,7 +38,7 @@ jobs: toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} - - run: sed -i '/nightly-only/d' Cargo.toml + - run: mv Cargo.stable.toml Cargo.toml if: matrix.rust != 'nightly' - run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }} diff --git a/Cargo.stable.toml b/Cargo.stable.toml new file mode 100644 index 000000000..78f6fd3cd --- /dev/null +++ b/Cargo.stable.toml @@ -0,0 +1,20 @@ +# CI moves this file to `Cargo.toml` when building for stable. + +[workspace] +resolver = "2" + +members = [ + "embedded-hal", + "embedded-hal-nb", + "embedded-hal-bus", + "embedded-can", + "embedded-io", + "embedded-io-adapters", +] + +# Cargo implicitly adds path dependencies to the workspace. +# Even if they're optional and not enabled. This prevents that. +exclude = [ + "embedded-hal-async", + "embedded-io-async", +] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index ccfa1e0f6..eb2832174 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,13 @@ [workspace] resolver = "2" -# CI removes lines containing 'nightly-only' when not building with nightly. members = [ "embedded-hal", - "embedded-hal-async", # nightly-only + "embedded-hal-async", "embedded-hal-nb", "embedded-hal-bus", "embedded-can", "embedded-io", - "embedded-io-async", # nightly-only + "embedded-io-async", "embedded-io-adapters", ] From 332d0cea4d8c22f03f97543e00c0acc7d6220268 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Fri, 14 Jul 2023 12:09:51 +0100 Subject: [PATCH 026/201] Missing `ReadExact` & WriteAll error impls for std - Add `ReadExactError` for `std::io::Error` - Add `WriteAllError` for `std::io::Error` --- embedded-io-adapters/src/std.rs | 24 +++--------------------- embedded-io/CHANGELOG.md | 1 + embedded-io/src/lib.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index 7449c805b..63978583d 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -115,25 +115,7 @@ impl std::io::Seek for ToStd { } } -fn to_std_error(err: T) -> std::io::Error { - let kind = match err.kind() { - embedded_io::ErrorKind::NotFound => std::io::ErrorKind::NotFound, - embedded_io::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, - embedded_io::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, - embedded_io::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, - embedded_io::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, - embedded_io::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, - embedded_io::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, - embedded_io::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, - embedded_io::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, - embedded_io::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, - embedded_io::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, - embedded_io::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, - embedded_io::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, - embedded_io::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, - embedded_io::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, - embedded_io::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, - _ => std::io::ErrorKind::Other, - }; - std::io::Error::new(kind, format!("{:?}", err)) +/// Convert a embedded-io error to a std::io::Error +pub fn to_std_error(err: T) -> std::io::Error { + std::io::Error::new(err.kind().into(), format!("{:?}", err)) } diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index d5038933e..520e4b4dc 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split trait adapters to the `embedded-io-adapters` crate. +- Add `std::io` impls for `ReadExactError` & `WriteAllError`. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 454ac8642..f6a8aa288 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -227,6 +227,33 @@ impl From for ReadExactError { } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From> for std::io::Error { + fn from(err: ReadExactError) -> Self { + match err { + ReadExactError::UnexpectedEof => std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "UnexpectedEof".to_owned(), + ), + ReadExactError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From> for std::io::Error { + fn from(err: WriteAllError) -> Self { + match err { + WriteAllError::WriteZero => { + std::io::Error::new(std::io::ErrorKind::WriteZero, "WriteZero".to_owned()) + } + WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + } + } +} + impl fmt::Display for ReadExactError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) @@ -234,6 +261,7 @@ impl fmt::Display for ReadExactError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for ReadExactError {} /// Error returned by [`Write::write_fmt`] @@ -260,6 +288,7 @@ impl fmt::Display for WriteFmtError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for WriteFmtError {} /// Error returned by [`Write::write_all`] @@ -284,6 +313,7 @@ impl fmt::Display for WriteAllError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for WriteAllError {} /// Blocking reader. From fa91e1facc77716d33d8c2e41d635ffcfb24410d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 16:27:26 +0200 Subject: [PATCH 027/201] doc: use include_str!(README.md) for embedded-hal crate docs. --- embedded-hal/README.md | 79 ++++++++++++++++++++++++++++++++++++++--- embedded-hal/src/lib.rs | 77 +-------------------------------------- 2 files changed, 76 insertions(+), 80 deletions(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 98871ebb9..97aff726e 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -9,9 +9,80 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -## [API reference] +**NOTE** This HAL is still is active development. Expect the traits presented here to be +tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 +release. -[API reference]: https://docs.rs/embedded-hal +**NOTE** If you want to use an alpha release of the 1.0.0 version, use an exact version +specifier in your `Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"`. + +## Companion crates + +The main `embedded-hal` crate contains only blocking traits, where the operation is done +synchronously before returning. Check out the following crates, which contain versions +of the traits for other execution models: + +- [`embedded-hal-async`](https://docs.rs/embedded-hal-async): async/await-based. +- [`embedded-hal-nb`](https://docs.rs/embedded-hal-nb): polling-based, using the `nb` crate. + +The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides utilities for sharing +SPI and I2C buses. + +Additionally, more domain-specific traits are available in separate crates: +- [`embedded-can`](https://docs.rs/embedded-can): Controller Area Network (CAN) + +## Design goals + +The HAL + +- Must *erase* device specific details. Neither register, register blocks or magic values should +appear in the API. + +- Must be generic *within* a device and *across* devices. The API to use a serial interface must +be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a +device or the UART0 peripheral of another device. + +- Where possible must *not* be tied to a specific asynchronous model. The API should be usable +in blocking mode, with the `futures` model, with an async/await model or with a callback model. +(cf. the [`nb`](https://docs.rs/nb) crate) + +- Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that +want higher level abstraction should *prefer to use this HAL* rather than *re-implement* +register manipulation code. + +- Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver +means a library crate that lets a target platform interface an external device like a digital +sensor or a wireless transceiver. The advantage of this system is that by writing the driver as +a generic library on top of `embedded-hal` driver authors can support any number of target +platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The +advantage for application developers is that by adopting `embedded-hal` they can unlock all +these drivers for their platform. + +- Trait methods must be fallible so that they can be used in any possible situation. +Nevertheless, HAL implementations can additionally provide infallible versions of the same methods +if they can never fail in their platform. This way, generic code can use the fallible abstractions +provided here but platform-specific code can avoid fallibility-related boilerplate if possible. + +## Out of scope + +- Initialization and configuration stuff like "ensure this serial interface and that SPI +interface are not using the same pins". The HAL will focus on *doing I/O*. + +## Reference implementation + +The [`stm32f1xx-hal`] crate contains a reference implementation of this HAL. + +[`stm32f1xx-hal`]: https://crates.io/crates/stm32f1xx-hal + +## Platform agnostic drivers + +You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching +for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). + +If you are writing a platform agnostic driver yourself you are highly encouraged to [add the +embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) +to your crate before publishing it! +// ## Minimum Supported Rust Version (MSRV) @@ -25,8 +96,8 @@ See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index ac09042b3..c195bb2a1 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -1,79 +1,4 @@ -//! A Hardware Abstraction Layer (HAL) for embedded systems -//! -//! **NOTE** This HAL is still is active development. Expect the traits presented here to be -//! tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 -//! release. -//! -//! **NOTE** If you want to use an alpha release of the 1.0.0 version, use an exact version -//! specifier in your `Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"`. -//! -//! # Companion crates -//! -//! The main `embedded-hal` crate contains only blocking traits, where the operation is done -//! synchronously before returning. Check out the following crates, which contain versions -//! of the traits for other execution models: -//! -//! - [`embedded-hal-async`](https://docs.rs/embedded-hal-async): async/await-based. -//! - [`embedded-hal-nb`](https://docs.rs/embedded-hal-nb): polling-based, using the `nb` crate. -//! -//! The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides utilities for sharing -//! SPI and I2C buses. -//! -//! Additionally, more domain-specific traits are available in separate crates: -//! - [`embedded-can`](https://docs.rs/embedded-can): Controller Area Network (CAN) -//! -//! # Design goals -//! -//! The HAL -//! -//! - Must *erase* device specific details. Neither register, register blocks or magic values should -//! appear in the API. -//! -//! - Must be generic *within* a device and *across* devices. The API to use a serial interface must -//! be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a -//! device or the UART0 peripheral of another device. -//! -//! - Where possible must *not* be tied to a specific asynchronous model. The API should be usable -//! in blocking mode, with the `futures` model, with an async/await model or with a callback model. -//! (cf. the [`nb`](https://docs.rs/nb) crate) -//! -//! - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that -//! want higher level abstraction should *prefer to use this HAL* rather than *re-implement* -//! register manipulation code. -//! -//! - Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver -//! means a library crate that lets a target platform interface an external device like a digital -//! sensor or a wireless transceiver. The advantage of this system is that by writing the driver as -//! a generic library on top of `embedded-hal` driver authors can support any number of target -//! platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The -//! advantage for application developers is that by adopting `embedded-hal` they can unlock all -//! these drivers for their platform. -//! -//! - Trait methods must be fallible so that they can be used in any possible situation. -//! Nevertheless, HAL implementations can additionally provide infallible versions of the same methods -//! if they can never fail in their platform. This way, generic code can use the fallible abstractions -//! provided here but platform-specific code can avoid fallibility-related boilerplate if possible. -//! -//! # Out of scope -//! -//! - Initialization and configuration stuff like "ensure this serial interface and that SPI -//! interface are not using the same pins". The HAL will focus on *doing I/O*. -//! -//! # Reference implementation -//! -//! The [`stm32f1xx-hal`] crate contains a reference implementation of this HAL. -//! -//! [`stm32f1xx-hal`]: https://crates.io/crates/stm32f1xx-hal -//! -//! # Platform agnostic drivers -//! -//! You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching -//! for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). -//! -//! If you are writing a platform agnostic driver yourself you are highly encouraged to [add the -//! embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) -//! to your crate before publishing it! - +#![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] From ebde20615071da4c3830e086853bf1ea11ee15f4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 18:07:13 +0200 Subject: [PATCH 028/201] readme: add io crates. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3b8692783..02cee6050 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ The main `embedded-hal` project is not tied to a specific execution model like | [embedded-hal-nb](./embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | | [embedded-hal-bus](./embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | | [embedded-can](./embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | +| [embedded-io](./embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | +| [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | +| [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | ## Releases From baccfdbc5aae5ed414060e77d28d2229f9b5c19d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 18:07:26 +0200 Subject: [PATCH 029/201] readme: remove stm32f1xx-hal as reference impl. It doesn't implement embedded-hal 1.0. --- embedded-hal/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 97aff726e..b496b9ae9 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -68,12 +68,6 @@ provided here but platform-specific code can avoid fallibility-related boilerpla - Initialization and configuration stuff like "ensure this serial interface and that SPI interface are not using the same pins". The HAL will focus on *doing I/O*. -## Reference implementation - -The [`stm32f1xx-hal`] crate contains a reference implementation of this HAL. - -[`stm32f1xx-hal`]: https://crates.io/crates/stm32f1xx-hal - ## Platform agnostic drivers You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching From 994c6c92be9f05c63cafd730bf4341b871ebbcf1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 18:08:04 +0200 Subject: [PATCH 030/201] readme: document that serial should use embedded-io. --- embedded-hal-async/README.md | 7 +++++++ embedded-hal/README.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index a0527605a..d22d658d8 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -10,6 +10,13 @@ This crate contains asynchronous versions of the [`embedded-hal`](https://crates This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). +## Serial/UART traits + +There is no serial traits in `embedded-hal-async`. Instead, use [`embedded-io-async`](https://crates.io/crates/embedded-io). +A serial port is essentially a byte-oriented stream, and that's what `embedded-io-async` models. Sharing the traits +with all byte streams has some advantages. For example, it allows generic code providing a command-line interface +or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. + ## Minimum Supported Rust Version (MSRV) This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for diff --git a/embedded-hal/README.md b/embedded-hal/README.md index b496b9ae9..3f3918bb0 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -78,6 +78,13 @@ embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#pa to your crate before publishing it! // +## Serial/UART traits + +There is no serial traits in `embedded-hal`. Instead, use [`embedded-io`](https://crates.io/crates/embedded-io). +A serial port is essentially a byte-oriented stream, and that's what `embedded-io` models. Sharing the traits +with all byte streams has some advantages. For example, it allows generic code providing a command-line interface +or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* From 1567dee785ddc3710369dd70e30c28762b1bf496 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 13:44:43 +0200 Subject: [PATCH 031/201] bus: add doc(cfg). --- embedded-hal-bus/Cargo.toml | 4 ++++ embedded-hal-bus/src/i2c/mutex.rs | 1 + embedded-hal-bus/src/lib.rs | 1 + embedded-hal-bus/src/spi/mutex.rs | 1 + 4 files changed, 7 insertions(+) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 5e6f40995..9ffa63d32 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -19,3 +19,7 @@ std = [] [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } critical-section = { version = "1.0" } + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs index 56f922c8c..ff9da7b2a 100644 --- a/embedded-hal-bus/src/i2c/mutex.rs +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -6,6 +6,7 @@ use std::sync::Mutex; /// Sharing is implemented with an `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is that /// it is only available in `std` targets. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct MutexDevice<'a, T> { bus: &'a Mutex, } diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index ca76c09c8..dabff2b25 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] pub mod i2c; pub mod spi; diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index bdf329217..7030ca02c 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -13,6 +13,7 @@ use super::DeviceError; /// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is /// it is only available in `std` targets. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct MutexDevice<'a, BUS, CS, D> { bus: &'a Mutex, cs: CS, From b2c2971592ec93d8e5bd72f7c566496ff7f63250 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 15:57:06 +0200 Subject: [PATCH 032/201] spi: move async ExclusiveDevice to embedded-hal-bus. --- embedded-hal-async/src/spi.rs | 152 ------------------- embedded-hal-bus/Cargo.toml | 4 +- embedded-hal-bus/src/asynch/mod.rs | 3 + embedded-hal-bus/src/asynch/spi/exclusive.rs | 147 ++++++++++++++++++ embedded-hal-bus/src/asynch/spi/mod.rs | 16 ++ embedded-hal-bus/src/lib.rs | 5 + 6 files changed, 174 insertions(+), 153 deletions(-) create mode 100644 embedded-hal-bus/src/asynch/mod.rs create mode 100644 embedded-hal-bus/src/asynch/spi/exclusive.rs create mode 100644 embedded-hal-bus/src/asynch/spi/mod.rs diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 1e852e34b..e9548359f 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -1,15 +1,9 @@ //! SPI master mode traits. -use core::fmt::Debug; - -use embedded_hal::digital::OutputPin; -use embedded_hal::spi as blocking; pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -use crate::delay::DelayUs; - /// SPI device trait /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected @@ -170,149 +164,3 @@ impl + ?Sized, Word: 'static + Copy> SpiBus for &mut T { T::flush(self).await } } - -/// Error type for [`ExclusiveDevice`] operations. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum ExclusiveDeviceError { - /// An inner SPI bus operation failed - Spi(BUS), - /// Asserting or deasserting CS failed - Cs(CS), -} - -impl Error for ExclusiveDeviceError -where - BUS: Error + Debug, - CS: Debug, -{ - fn kind(&self) -> ErrorKind { - match self { - Self::Spi(e) => e.kind(), - Self::Cs(_) => ErrorKind::ChipSelectFault, - } - } -} - -/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). -/// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], -/// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { - bus: BUS, - cs: CS, - delay: D, -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } - } - - /// Returns a reference to the underlying bus object. - pub fn bus(&self) -> &BUS { - &self.bus - } - - /// Returns a mutable reference to the underlying bus object. - pub fn bus_mut(&mut self) -> &mut BUS { - &mut self.bus - } -} - -impl ErrorType for ExclusiveDevice -where - BUS: ErrorType, - CS: OutputPin, -{ - type Error = ExclusiveDeviceError; -} - -impl blocking::SpiDevice for ExclusiveDevice -where - BUS: blocking::SpiBus, - CS: OutputPin, - D: embedded_hal::delay::DelayUs, -{ - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf), - Operation::Write(buf) => self.bus.write(buf), - Operation::Transfer(read, write) => self.bus.transfer(read, write), - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), - Operation::DelayUs(us) => match self.bus.flush() { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us); - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDevice for ExclusiveDevice -where - BUS: SpiBus, - CS: OutputPin, - D: DelayUs, -{ - async fn transaction( - &mut self, - operations: &mut [Operation<'_, Word>], - ) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf).await, - Operation::Write(buf) => self.bus.write(buf).await, - Operation::Transfer(read, write) => self.bus.transfer(read, write).await, - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, - Operation::DelayUs(us) => match self.bus.flush().await { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us).await; - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush().await; - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 9ffa63d32..66477fdfa 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -15,11 +15,13 @@ version = "0.1.0-alpha.3" [features] std = [] +async = ["dep:embedded-hal-async"] [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +embedded-hal-async = { version = "=0.2.0-alpha.2", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } [package.metadata.docs.rs] -features = ["std"] +features = ["std", "async"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-hal-bus/src/asynch/mod.rs b/embedded-hal-bus/src/asynch/mod.rs new file mode 100644 index 000000000..a9957d654 --- /dev/null +++ b/embedded-hal-bus/src/asynch/mod.rs @@ -0,0 +1,3 @@ +//! Bus sharing utilities for [`embedded-hal-async`] + +pub mod spi; diff --git a/embedded-hal-bus/src/asynch/spi/exclusive.rs b/embedded-hal-bus/src/asynch/spi/exclusive.rs new file mode 100644 index 000000000..4c80a698e --- /dev/null +++ b/embedded-hal-bus/src/asynch/spi/exclusive.rs @@ -0,0 +1,147 @@ +use embedded_hal::{digital::OutputPin, spi as blocking}; +use embedded_hal_async::{ + delay::DelayUs, + spi::{ErrorType, Operation, SpiBus, SpiDevice}, +}; + +pub use crate::spi::DeviceError; + +/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). +/// +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], +/// ideal for when no sharing is required (only one SPI device is present on the bus). +pub struct ExclusiveDevice { + bus: BUS, + cs: CS, + delay: D, +} + +impl ExclusiveDevice { + /// Create a new ExclusiveDevice + pub fn new(bus: BUS, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } + } + + /// Returns a reference to the underlying bus object. + pub fn bus(&self) -> &BUS { + &self.bus + } + + /// Returns a mutable reference to the underlying bus object. + pub fn bus_mut(&mut self) -> &mut BUS { + &mut self.bus + } +} + +impl ExclusiveDevice { + /// Create a new ExclusiveDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: BUS, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl ErrorType for ExclusiveDevice +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl blocking::SpiDevice for ExclusiveDevice +where + BUS: blocking::SpiBus, + CS: OutputPin, + D: embedded_hal::delay::DelayUs, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf), + Operation::Write(buf) => self.bus.write(buf), + Operation::Transfer(read, write) => self.bus.transfer(read, write), + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + Operation::DelayUs(us) => match self.bus.flush() { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us); + Ok(()) + } + }, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDevice for ExclusiveDevice +where + BUS: SpiBus, + CS: OutputPin, + D: DelayUs, +{ + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf).await, + Operation::Write(buf) => self.bus.write(buf).await, + Operation::Transfer(read, write) => self.bus.transfer(read, write).await, + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + Operation::DelayUs(us) => match self.bus.flush().await { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us).await; + Ok(()) + } + }, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush().await; + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} diff --git a/embedded-hal-bus/src/asynch/spi/mod.rs b/embedded-hal-bus/src/asynch/spi/mod.rs new file mode 100644 index 000000000..77731bf04 --- /dev/null +++ b/embedded-hal-bus/src/asynch/spi/mod.rs @@ -0,0 +1,16 @@ +//! `SpiDevice` implementations. + +mod exclusive; +pub use exclusive::*; + +pub use crate::spi::NoDelay; + +impl embedded_hal_async::delay::DelayUs for NoDelay { + async fn delay_ms(&mut self, _ms: u32) { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + } + + async fn delay_us(&mut self, _us: u32) { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + } +} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index dabff2b25..812ce1183 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -2,6 +2,11 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] pub mod i2c; pub mod spi; + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +pub mod asynch; From 5f8f7f932ea077ad082617815093f7ebd536a53c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 16:00:24 +0200 Subject: [PATCH 033/201] ci: add rustdoc check with deny warnings. Catches things like broken links. --- .github/workflows/rustdoc.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/rustdoc.yml diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml new file mode 100644 index 000000000..e0202bac8 --- /dev/null +++ b/.github/workflows/rustdoc.yml @@ -0,0 +1,17 @@ +on: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue + +name: Rustdoc check +jobs: + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2023-07-03 + - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features \ No newline at end of file From 7921e820fbb18e5eda725405eb9b75a41c8fb00b Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Mon, 17 Jul 2023 12:50:26 +0200 Subject: [PATCH 034/201] Fix typo --- embedded-hal/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 3f3918bb0..f29a8c599 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -76,7 +76,6 @@ for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). If you are writing a platform agnostic driver yourself you are highly encouraged to [add the embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) to your crate before publishing it! -// ## Serial/UART traits From 8d03c73d8e9b88ad55564f02efc9df7c41030b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 18 Jul 2023 22:48:44 +0200 Subject: [PATCH 035/201] Merge spi ExclusiveDevice implementations --- embedded-hal-bus/src/asynch/mod.rs | 3 - embedded-hal-bus/src/asynch/spi/exclusive.rs | 147 ------------------- embedded-hal-bus/src/asynch/spi/mod.rs | 16 -- embedded-hal-bus/src/lib.rs | 4 - embedded-hal-bus/src/spi/exclusive.rs | 63 ++++++++ 5 files changed, 63 insertions(+), 170 deletions(-) delete mode 100644 embedded-hal-bus/src/asynch/mod.rs delete mode 100644 embedded-hal-bus/src/asynch/spi/exclusive.rs delete mode 100644 embedded-hal-bus/src/asynch/spi/mod.rs diff --git a/embedded-hal-bus/src/asynch/mod.rs b/embedded-hal-bus/src/asynch/mod.rs deleted file mode 100644 index a9957d654..000000000 --- a/embedded-hal-bus/src/asynch/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Bus sharing utilities for [`embedded-hal-async`] - -pub mod spi; diff --git a/embedded-hal-bus/src/asynch/spi/exclusive.rs b/embedded-hal-bus/src/asynch/spi/exclusive.rs deleted file mode 100644 index 4c80a698e..000000000 --- a/embedded-hal-bus/src/asynch/spi/exclusive.rs +++ /dev/null @@ -1,147 +0,0 @@ -use embedded_hal::{digital::OutputPin, spi as blocking}; -use embedded_hal_async::{ - delay::DelayUs, - spi::{ErrorType, Operation, SpiBus, SpiDevice}, -}; - -pub use crate::spi::DeviceError; - -/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). -/// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], -/// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { - bus: BUS, - cs: CS, - delay: D, -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } - } - - /// Returns a reference to the underlying bus object. - pub fn bus(&self) -> &BUS { - &self.bus - } - - /// Returns a mutable reference to the underlying bus object. - pub fn bus_mut(&mut self) -> &mut BUS { - &mut self.bus - } -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice without support for in-transaction delays. - /// - /// # Panics - /// - /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. - pub fn new_no_delay(bus: BUS, cs: CS) -> Self { - Self { - bus, - cs, - delay: super::NoDelay, - } - } -} - -impl ErrorType for ExclusiveDevice -where - BUS: ErrorType, - CS: OutputPin, -{ - type Error = DeviceError; -} - -impl blocking::SpiDevice for ExclusiveDevice -where - BUS: blocking::SpiBus, - CS: OutputPin, - D: embedded_hal::delay::DelayUs, -{ - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf), - Operation::Write(buf) => self.bus.write(buf), - Operation::Transfer(read, write) => self.bus.transfer(read, write), - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), - Operation::DelayUs(us) => match self.bus.flush() { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us); - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDevice for ExclusiveDevice -where - BUS: SpiBus, - CS: OutputPin, - D: DelayUs, -{ - async fn transaction( - &mut self, - operations: &mut [Operation<'_, Word>], - ) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf).await, - Operation::Write(buf) => self.bus.write(buf).await, - Operation::Transfer(read, write) => self.bus.transfer(read, write).await, - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, - Operation::DelayUs(us) => match self.bus.flush().await { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us).await; - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush().await; - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} diff --git a/embedded-hal-bus/src/asynch/spi/mod.rs b/embedded-hal-bus/src/asynch/spi/mod.rs deleted file mode 100644 index 77731bf04..000000000 --- a/embedded-hal-bus/src/asynch/spi/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! `SpiDevice` implementations. - -mod exclusive; -pub use exclusive::*; - -pub use crate::spi::NoDelay; - -impl embedded_hal_async::delay::DelayUs for NoDelay { - async fn delay_ms(&mut self, _ms: u32) { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") - } - - async fn delay_us(&mut self, _us: u32) { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") - } -} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 812ce1183..cdb668ae3 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -6,7 +6,3 @@ pub mod i2c; pub mod spi; - -#[cfg(feature = "async")] -#[cfg_attr(docsrs, doc(cfg(feature = "async")))] -pub mod asynch; diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index cbec194dc..da2381945 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -3,6 +3,11 @@ use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; +#[cfg(feature = "async")] +use embedded_hal_async::{ + delay::DelayUs as AsyncDelayUs, + spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, +}; use super::DeviceError; @@ -21,6 +26,16 @@ impl ExclusiveDevice { pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } + + /// Returns a reference to the underlying bus object. + pub fn bus(&self) -> &BUS { + &self.bus + } + + /// Returns a mutable reference to the underlying bus object. + pub fn bus_mut(&mut self) -> &mut BUS { + &mut self.bus + } } impl ExclusiveDevice { @@ -79,3 +94,51 @@ where Ok(()) } } + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncSpiDevice for ExclusiveDevice +where + BUS: AsyncSpiBus, + CS: OutputPin, + D: AsyncDelayUs, +{ + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf).await, + Operation::Write(buf) => self.bus.write(buf).await, + Operation::Transfer(read, write) => self.bus.transfer(read, write).await, + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + Operation::DelayUs(us) => match self.bus.flush().await { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us).await; + Ok(()) + } + }, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush().await; + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} From ff94c7d109446c1dd17f77ce28f729a06126934b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 18 Jul 2023 22:53:43 +0200 Subject: [PATCH 036/201] Implement async DelayUs for NoDelay --- embedded-hal-bus/src/spi/mod.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 10f74e8d6..498dd1414 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -39,8 +39,25 @@ where /// Dummy `DelayUs` implementation that panics on use. pub struct NoDelay; +#[cold] +fn no_delay_panic() { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation."); +} + impl embedded_hal::delay::DelayUs for NoDelay { fn delay_us(&mut self, _us: u32) { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + no_delay_panic(); + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl embedded_hal_async::delay::DelayUs for NoDelay { + async fn delay_us(&mut self, _us: u32) { + no_delay_panic(); + } + + async fn delay_ms(&mut self, _us: u32) { + no_delay_panic(); } } From 428c6a14d14e5328a5af5d265b5bc1d4178b998b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 15:30:57 +0200 Subject: [PATCH 037/201] io/adapters: derive Clone for ToStd. --- embedded-io-adapters/src/std.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index 63978583d..ac6a606d3 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -66,6 +66,7 @@ impl embedded_io::Seek for FromStd { } /// Adapter to `std::io` traits. +#[derive(Clone)] pub struct ToStd { inner: T, } From 35b0dc33989f8edcf6208ee394562b05a85d69b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 15:32:54 +0200 Subject: [PATCH 038/201] io: add defmt support. --- embedded-io-async/Cargo.toml | 2 ++ embedded-io/Cargo.toml | 3 +++ embedded-io/src/lib.rs | 13 +++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index e8f19af39..011a65589 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -14,9 +14,11 @@ categories = [ [features] std = ["alloc", "embedded-io/std"] alloc = ["embedded-io/alloc"] +defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] [dependencies] embedded-io = { version = "0.5", path = "../embedded-io" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] features = ["std"] diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index d1f82254f..a7ca732f3 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -15,6 +15,9 @@ categories = [ std = ["alloc"] alloc = [] +[dependencies] +defmt-03 = { package = "defmt", version = "0.3", optional = true } + [package.metadata.docs.rs] features = ["std"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f6a8aa288..f798b5b6e 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -5,6 +5,10 @@ use core::fmt; +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; + #[cfg(feature = "alloc")] extern crate alloc; @@ -14,6 +18,7 @@ mod impls; /// /// This is the `embedded-io` equivalent of [`std::io::SeekFrom`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. Start(u64), @@ -47,8 +52,6 @@ impl From for SeekFrom { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[non_exhaustive] /// Possible kinds of errors. /// /// This list is intended to grow over time and it is not recommended to @@ -59,6 +62,9 @@ impl From for SeekFrom { /// /// - `WouldBlock` is removed, since `embedded-io` traits are always blocking. See the [crate-level documentation](crate) for details. /// - `WriteZero` is removed, since it is a separate variant in [`WriteAllError`] and [`WriteFmtError`]. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[non_exhaustive] pub enum ErrorKind { /// Unspecified error kind. Other, @@ -214,6 +220,7 @@ impl ErrorType for &mut T { /// Error returned by [`Read::read_exact`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum ReadExactError { /// An EOF error was encountered before reading the exact amount of requested bytes. UnexpectedEof, @@ -266,6 +273,7 @@ impl std::error::Error for ReadExactError {} /// Error returned by [`Write::write_fmt`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum WriteFmtError { /// [`Write::write`] wrote zero bytes WriteZero, @@ -293,6 +301,7 @@ impl std::error::Error for WriteFmtError {} /// Error returned by [`Write::write_all`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum WriteAllError { /// [`Write::write`] wrote zero bytes WriteZero, From 6ea5659babe340ed9330bd060158013c8d24920f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 12:43:48 +0200 Subject: [PATCH 039/201] io: Release embedded-io 0.5 --- embedded-io-adapters/CHANGELOG.md | 10 ++++++++++ embedded-io-adapters/Cargo.toml | 2 +- embedded-io-async/CHANGELOG.md | 10 ++++++++++ embedded-io/CHANGELOG.md | 4 ++-- 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 embedded-io-adapters/CHANGELOG.md create mode 100644 embedded-io-async/CHANGELOG.md diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md new file mode 100644 index 000000000..978a92d8b --- /dev/null +++ b/embedded-io-adapters/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.5.0 - 2023-08-06 + +- First release \ No newline at end of file diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 05905ad60..23814b62f 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.1.0" +version = "0.5.0" edition = "2021" description = "Adapters between the `embedded-io` traits and other I/O traits" repository = "https://github.com/rust-embedded/embedded-hal" diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md new file mode 100644 index 000000000..978a92d8b --- /dev/null +++ b/embedded-io-async/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.5.0 - 2023-08-06 + +- First release \ No newline at end of file diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 520e4b4dc..e042a7239 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.5.0 - 2023-08-06 - Add `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. - Add variants to `ErrorKind` mirroring `std::io::ErrorKind`. @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split trait adapters to the `embedded-io-adapters` crate. -- Add `std::io` impls for `ReadExactError` & `WriteAllError`. +- Add `std::error` impls for `ReadExactError` & `WriteAllError`. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 From 9fd6ebcfb06dc8ffb39b8f697fa3e6315dd97a04 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 18:56:21 +0200 Subject: [PATCH 040/201] hal: add optional defmt support. --- embedded-hal-async/Cargo.toml | 4 ++++ embedded-hal-bus/Cargo.toml | 2 ++ embedded-hal-bus/src/lib.rs | 4 ++++ embedded-hal-bus/src/spi/mod.rs | 6 ++++++ embedded-hal/Cargo.toml | 3 +++ embedded-hal/src/digital.rs | 5 +++++ embedded-hal/src/i2c.rs | 6 ++++++ embedded-hal/src/lib.rs | 4 ++++ embedded-hal/src/pwm.rs | 4 ++++ embedded-hal/src/spi.rs | 10 +++++++++- 10 files changed, 47 insertions(+), 1 deletion(-) diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index dbae01c7d..918514052 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -14,5 +14,9 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0-alpha.2" rust-version = "1.65.0" +[features] +defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] + [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 66477fdfa..6edcdc75c 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -16,11 +16,13 @@ version = "0.1.0-alpha.3" [features] std = [] async = ["dep:embedded-hal-async"] +defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } embedded-hal-async = { version = "=0.2.0-alpha.2", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] features = ["std", "async"] diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index cdb668ae3..6feef548b 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -4,5 +4,9 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; + pub mod i2c; pub mod spi; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 498dd1414..e3e888c8e 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -14,8 +14,12 @@ pub use mutex::*; mod critical_section; pub use self::critical_section::*; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Error type for [`ExclusiveDevice`] operations. #[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum DeviceError { /// An inner SPI bus operation failed Spi(BUS), @@ -37,6 +41,8 @@ where } /// Dummy `DelayUs` implementation that panics on use. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct NoDelay; #[cold] diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index c70b3106a..ad9a0fdc3 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,3 +14,6 @@ name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "1.0.0-alpha.11" + +[dependencies] +defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index 677bb64d1..3f024ca2c 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -2,6 +2,9 @@ use core::{convert::From, ops::Not}; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Error pub trait Error: core::fmt::Debug { /// Convert error to a generic error kind @@ -24,6 +27,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// A different error occurred. The original error may contain more information. @@ -74,6 +78,7 @@ impl ErrorType for &mut T { /// assert_eq!(!state, PinState::High); /// ``` #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum PinState { /// Low pin state Low, diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 735408758..9195308b8 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -154,6 +154,9 @@ use crate::private; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// I2C error pub trait Error: core::fmt::Debug { /// Convert error to a generic I2C error kind @@ -176,6 +179,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common I2C errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// Bus error occurred. e.g. A START or a STOP condition is detected and is not @@ -199,6 +203,7 @@ pub enum ErrorKind { /// response was received to an address versus a no acknowledge to a data byte. /// Where it is not possible to differentiate, `Unknown` should be indicated. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum NoAcknowledgeSource { /// The device did not acknowledge its address. The device may be missing. Address, @@ -272,6 +277,7 @@ impl AddressMode for TenBitAddress {} /// /// Several operations can be combined as part of a transaction. #[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a> { /// Read data into the provided buffer Read(&'a mut [u8]), diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index c195bb2a1..f5eb76c32 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -15,3 +15,7 @@ mod private { impl Sealed for SevenBitAddress {} impl Sealed for TenBitAddress {} } + +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 4c0f6ac60..3893eaade 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -1,5 +1,8 @@ //! Pulse Width Modulation (PWM) traits +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Error pub trait Error: core::fmt::Debug { /// Convert error to a generic error kind @@ -22,6 +25,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// A different error occurred. The original error may contain more information. diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 54bf8bcea..3813b9a6c 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -163,8 +163,12 @@ use core::fmt::Debug; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Clock polarity #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Polarity { /// Clock signal low when idle IdleLow, @@ -174,6 +178,7 @@ pub enum Polarity { /// Clock phase #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Phase { /// Data in "captured" on the first clock transition CaptureOnFirstTransition, @@ -183,6 +188,7 @@ pub enum Phase { /// SPI mode #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct Mode { /// Clock polarity pub polarity: Polarity, @@ -236,6 +242,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common SPI errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// The peripheral receive buffer was overrun @@ -295,7 +302,8 @@ impl ErrorType for &mut T { /// SPI transaction operation. /// /// This allows composition of SPI operations into a single bus transaction -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a, Word: 'static> { /// Read data into the provided buffer. /// From 680a2b381c3c2131b7e860d7ca858f716d7a80aa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 20:35:45 +0200 Subject: [PATCH 041/201] ci: actually test all feature combinations, add defmt. the GHA `include` field is nonsense. --- .github/workflows/test.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2fa0d9d7..bb7bf5e56 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,14 +23,18 @@ jobs: - x86_64-unknown-linux-gnu - thumbv6m-none-eabi - thumbv7m-none-eabi + features: + - '' include: - - target: x86_64-unknown-linux-gnu + - rust: stable + target: x86_64-unknown-linux-gnu features: std - - target: x86_64-unknown-linux-gnu + - rust: stable + target: x86_64-unknown-linux-gnu features: alloc - - target: x86_64-unknown-linux-gnu - features: std,tokio-1,futures-03 - rust: nightly + - rust: nightly + target: x86_64-unknown-linux-gnu + features: std,tokio-1,futures-03,defmt-03 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master From 3505e76a0c30b4d1f2ce2fceff8bbdb9111d6106 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 20:46:23 +0200 Subject: [PATCH 042/201] ci: don't build for both thumbv6 and thumbv7. We have 16 jobs now, starts to hit the GHA free limit of 20. There's no advantage in testing thumbv6 and thumbv7 because they're almost identical and nothing in e-h depends on them, so remove thumbv6. --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb7bf5e56..19ff36502 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,6 @@ jobs: - nightly target: - x86_64-unknown-linux-gnu - - thumbv6m-none-eabi - thumbv7m-none-eabi features: - '' From cb1fdf2ec479b570b73a79c42710282305abe885 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 15 Aug 2023 22:20:24 +0800 Subject: [PATCH 043/201] Fix doc links for adapters, add features The doc links in the README needed updating Include tokio and futures features in docs --- embedded-io-adapters/Cargo.toml | 2 +- embedded-io-adapters/README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 23814b62f..5dbcf4354 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -24,5 +24,5 @@ futures = { version = "0.3.21", features = ["std"], default-features = false, op tokio = { version = "1", default-features = false, optional = true } [package.metadata.docs.rs] -features = ["std"] +features = ["std", "tokio-1", "futures-03"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md index 0cdb8c558..6edd3c47b 100644 --- a/embedded-io-adapters/README.md +++ b/embedded-io-adapters/README.md @@ -1,6 +1,6 @@ -[![crates.io](https://img.shields.io/crates/d/embedded-io.svg)](https://crates.io/crates/embedded-io) -[![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) -[![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) +[![crates.io](https://img.shields.io/crates/d/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) +[![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) +[![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) # `embedded-io-adapters` From 575cee33438b64aec5de49cb505643437f47aadc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 19:18:16 +0200 Subject: [PATCH 044/201] Release embedded-hal{,-async,-nb} v1.0.0-rc.1, embedded-hal-bus v0.1.0-rc.1 --- embedded-hal-async/CHANGELOG.md | 12 +++++++++--- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-async/README.md | 12 ++++++++++-- embedded-hal-bus/CHANGELOG.md | 11 ++++++++++- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-bus/README.md | 6 ++++-- embedded-hal-nb/CHANGELOG.md | 5 +++++ embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 11 +++++++++-- embedded-hal/Cargo.toml | 4 ++-- embedded-hal/README.md | 4 ++++ embedded-io-async/README.md | 6 ++++++ embedded-io/CHANGELOG.md | 1 + embedded-io/README.md | 6 ++++++ 14 files changed, 73 insertions(+), 19 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 5ea703cdd..478e994f7 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,8 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -### Added -- spi: added `ExclusiveDevice::{bus, bus_mut}`. +## [v1.0.0-rc.1] - 2023-08-15 + +- Updated `embedded-hal` to version `1.0.0-rc.1`. +- Add optional `defmt` 0.3 support. +- Remove serial traits, the replacement is the `embedded-io` crate. +- Added `+ ?Sized` to all blanket impls. +- Moved `ExclusiveDevice` to `embedded-hal-bus`. ## [v0.2.0-alpha.2] - 2023-07-04 @@ -64,7 +69,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...HEAD +[v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 [v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 [v0.2.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...embedded-hal-async-v0.2.0-alpha.1 [v0.2.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.3...embedded-hal-async-v0.2.0-alpha.0 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 918514052..b10cfc734 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.2.0-alpha.2" +version = "1.0.0-rc.1" rust-version = "1.65.0" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index d22d658d8..c53790751 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -17,13 +17,21 @@ A serial port is essentially a byte-oriented stream, and that's what `embedded-i with all byte streams has some advantages. For example, it allows generic code providing a command-line interface or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. +## Optional Cargo features + +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for -`async fn` in traits (AFIT), which is not stable yet. +`async fn` in traits (AFIT), which is not stable yet. Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features -at any time. +at any time. If this happens, we might do changes that increase the minimum required nightly +version in any patch release. + +When AFIT becomes stable, MSRV will be bumped to the Rust version that stabilizes it, after which +point the [standard MSRV bump policy](../docs/msrv.md) will apply. ## License diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index c97121910..ead1de58f 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.0-rc.1] - 2023-08-15 + +- Updated `embedded-hal`, `embedded-hal-async` to version `1.0.0-rc.1`. +- The Minimum Supported Rust Version (MSRV) is now 1.60.0 +- Added `embedded-hal-async` support to SPI `ExclusiveDevice`. +- Added methods to access the inner bus to SPI `ExclusiveDevice`. +- Add optional `defmt` 0.3 support. + ## [v0.1.0-alpha.3] - 2023-07-04 ### Changed @@ -31,7 +39,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...HEAD +[v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 [v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...embedded-hal-bus-v0.1.0-alpha.2 [v0.1.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.0...embedded-hal-bus-v0.1.0-alpha.1 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 6edcdc75c..bfd2a754d 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-alpha.3" +version = "0.1.0-rc.1" [features] std = [] @@ -19,8 +19,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } -embedded-hal-async = { version = "=0.2.0-alpha.2", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } +embedded-hal-async = { version = "=1.0.0-rc.1", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 394ffb88b..d0b9bbdb6 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -28,9 +28,11 @@ This crate provides mechanisms to connect a `SpiBus` and a `SpiDevice`. In the case of I2C, the same `I2c` `embedded-hal` trait represents either an entire bus, or a device on a bus. This crate provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` instance, sharing the bus. -## Features +## Optional Cargo features -- `std`: enable shared bus implementations using `std::sync::Mutex`. +- **`std`**: enable shared bus implementations using `std::sync::Mutex`. +- **`async`**: enable `embedded-hal-async` support. +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 11ebab3b0..83b7e1510 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ... +## [v1.0.0-rc.1] - 2023-08-15 + +- Updated `embedded-hal` to version `1.0.0-rc.1`. +- Added `+ ?Sized` to all blanket impls. + ## [v1.0.0-alpha.3] - 2023-07-04 ### Changed diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 0bf3d7346..66e941923 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-alpha.3" +version = "1.0.0-rc.1" edition = "2021" categories = ["embedded", "hardware-support", "no-std"] @@ -12,7 +12,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index ccb13c888..62f78ff36 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v1.0.0-rc.1] - 2023-08-15 + +- The Minimum Supported Rust Version (MSRV) is now 1.60.0 +- Add optional `defmt` 0.3 support. +- Remove serial traits, the replacement is the `embedded-io` crate. +- Added `+ ?Sized` to all blanket impls. ## [v1.0.0-alpha.11] - 2023-07-04 @@ -65,7 +71,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). *** This is (also) an alpha release with breaking changes (sorry) *** ### Changed -- The Minimum Supported Rust Version (MSRV) is now 1.60.0 +- The Minimum Supported Rust Version (MSRV) is now 1.59.0 - `spi`: unify all traits into `SpiReadBus`, `SpiWriteBus` and `SpiBus` (read-write). - `spi`: Add `SpiDevice` trait to represent a single device in a (possibly shared) bus, with managed chip-select (CS) pin. - `spi`: Clarify that implementations are allowed to return before operations are finished, add `flush` to wait until finished. @@ -291,7 +297,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...HEAD +[v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 [v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 [v1.0.0-alpha.10]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...v1.0.0-alpha.10 [v1.0.0-alpha.9]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.8...v1.0.0-alpha.9 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index ad9a0fdc3..396cf972b 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -13,7 +13,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-alpha.11" +version = "1.0.0-rc.1" [dependencies] -defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file +defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal/README.md b/embedded-hal/README.md index f29a8c599..bed3c40c5 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -84,6 +84,10 @@ A serial port is essentially a byte-oriented stream, and that's what `embedded-i with all byte streams has some advantages. For example, it allows generic code providing a command-line interface or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. +## Optional Cargo features + +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md index 557a0866a..6e6ddf18a 100644 --- a/embedded-io-async/README.md +++ b/embedded-io-async/README.md @@ -10,6 +10,12 @@ This crate contains asynchronous versions of the [`embedded-io`](https://crates. This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). +## Optional Cargo features + +- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index e042a7239..f836114ae 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Split trait adapters to the `embedded-io-adapters` crate. - Add `std::error` impls for `ReadExactError` & `WriteAllError`. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. +- Added optional `defmt` 0.3 support. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/README.md b/embedded-io/README.md index 3318daa2b..d647ffe9d 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -19,6 +19,12 @@ while avoiding `dyn` or `Box`. This is consistent with how errors are handled in - In `std::io`, the `Read`/`Write` traits might be blocking or non-blocking (i.e. returning `WouldBlock` errors) depending on the file descriptor's mode, which is only known at run-time. This allows passing a non-blocking stream to code that expects a blocking stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. +## Optional Cargo features + +- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* From aa5e4754ea269b03929b2657775fef07f5f92e67 Mon Sep 17 00:00:00 2001 From: Daniel Larsen <44644910+daniel-larsen@users.noreply.github.com> Date: Thu, 17 Aug 2023 17:53:52 -0300 Subject: [PATCH 045/201] Fix typo in Changelog --- embedded-io/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index f836114ae..937a43140 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Split async traits to the `embedded-io-async` crate. - Split trait adapters to the `embedded-io-adapters` crate. - Add `std::error` impls for `ReadExactError` & `WriteAllError`. -- Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. +- Rename trait `Io` to `ErrorType`, for consistency with `embedded-hal`. - Added optional `defmt` 0.3 support. ## 0.4.0 - 2022-11-25 From ed96f03b3ad425e79fc6cb97b60d0d650a28e557 Mon Sep 17 00:00:00 2001 From: Anatol Ulrich <45840+spookyvision@users.noreply.github.com> Date: Mon, 21 Aug 2023 17:30:55 +0200 Subject: [PATCH 046/201] Update README.md typo: "is still is" --- embedded-hal/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index bed3c40c5..193c63b3a 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -9,7 +9,7 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -**NOTE** This HAL is still is active development. Expect the traits presented here to be +**NOTE** This HAL is still in active development. Expect the traits presented here to be tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 release. From a0e1f49b1f34fc99524474faf6e0d483ec7c9da8 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:07:01 +0800 Subject: [PATCH 047/201] e-h: amend documents to add periods (.), add inline hints when necessary In documentation style of the Rust standard library, first sentence of all modules, types, and functions documentation has a period. We follow Rust standard library style to make it easier for users to read. All functions in embedded-hal (especially type conversations, function fowarding calls) are now marked #[inline] to allow further optimizations. Signed-off-by: Zhouqi Jiang --- embedded-hal/src/delay.rs | 8 ++-- embedded-hal/src/digital.rs | 49 +++++++++++++++---------- embedded-hal/src/i2c.rs | 47 +++++++++++++++--------- embedded-hal/src/pwm.rs | 19 +++++++--- embedded-hal/src/spi.rs | 73 +++++++++++++++++++++++-------------- 5 files changed, 123 insertions(+), 73 deletions(-) diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index da4cd7e2d..32f085729 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -1,7 +1,6 @@ -//! Delays +//! Delays. -/// Microsecond delay -/// +/// Microsecond delay. pub trait DelayUs { /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. @@ -9,6 +8,7 @@ pub trait DelayUs { /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. + #[inline] fn delay_ms(&mut self, ms: u32) { for _ in 0..ms { self.delay_us(1000); @@ -20,10 +20,12 @@ impl DelayUs for &mut T where T: DelayUs + ?Sized, { + #[inline] fn delay_us(&mut self, us: u32) { T::delay_us(self, us) } + #[inline] fn delay_ms(&mut self, ms: u32) { T::delay_ms(self, ms) } diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index 3f024ca2c..f54c72153 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -1,11 +1,11 @@ -//! Digital I/O +//! Digital I/O. use core::{convert::From, ops::Not}; #[cfg(feature = "defmt-03")] use crate::defmt; -/// Error +/// Error. pub trait Error: core::fmt::Debug { /// Convert error to a generic error kind /// @@ -21,7 +21,7 @@ impl Error for core::convert::Infallible { } } -/// Error kind +/// Error kind. /// /// This represents a common set of operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -35,12 +35,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Other => write!( @@ -51,7 +53,7 @@ impl core::fmt::Display for ErrorKind { } } -/// Error type trait +/// Error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -67,7 +69,7 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Digital output pin state +/// Digital output pin state. /// /// Conversion from `bool` and logical negation are also implemented /// for this type. @@ -80,9 +82,9 @@ impl ErrorType for &mut T { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum PinState { - /// Low pin state + /// Low pin state. Low, - /// High pin state + /// High pin state. High, } @@ -118,24 +120,25 @@ impl From for bool { } } -/// Single digital push-pull output pin +/// Single digital push-pull output pin. pub trait OutputPin: ErrorType { - /// Drives the pin low + /// Drives the pin low. /// /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external - /// electrical sources + /// electrical sources. fn set_low(&mut self) -> Result<(), Self::Error>; - /// Drives the pin high + /// Drives the pin high. /// /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external - /// electrical sources + /// electrical sources. fn set_high(&mut self) -> Result<(), Self::Error>; - /// Drives the pin high or low depending on the provided value + /// Drives the pin high or low depending on the provided value. /// /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external - /// electrical sources + /// electrical sources. + #[inline] fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { match state { PinState::Low => self.set_low(), @@ -145,55 +148,61 @@ pub trait OutputPin: ErrorType { } impl OutputPin for &mut T { + #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { T::set_low(self) } + #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { T::set_high(self) } + #[inline] fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { T::set_state(self, state) } } -/// Push-pull output pin that can read its output state +/// Push-pull output pin that can read its output state. pub trait StatefulOutputPin: OutputPin { /// Is the pin in drive high mode? /// - /// *NOTE* this does *not* read the electrical state of the pin + /// *NOTE* this does *not* read the electrical state of the pin. fn is_set_high(&self) -> Result; /// Is the pin in drive low mode? /// - /// *NOTE* this does *not* read the electrical state of the pin + /// *NOTE* this does *not* read the electrical state of the pin. fn is_set_low(&self) -> Result; } impl StatefulOutputPin for &mut T { + #[inline] fn is_set_high(&self) -> Result { T::is_set_high(self) } + #[inline] fn is_set_low(&self) -> Result { T::is_set_low(self) } } -/// Output pin that can be toggled +/// Output pin that can be toggled. pub trait ToggleableOutputPin: ErrorType { /// Toggle pin output. fn toggle(&mut self) -> Result<(), Self::Error>; } impl ToggleableOutputPin for &mut T { + #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { T::toggle(self) } } -/// Single digital input pin +/// Single digital input pin. pub trait InputPin: ErrorType { /// Is the input pin high? fn is_high(&self) -> Result; @@ -203,10 +212,12 @@ pub trait InputPin: ErrorType { } impl InputPin for &T { + #[inline] fn is_high(&self) -> Result { T::is_high(self) } + #[inline] fn is_low(&self) -> Result { T::is_low(self) } diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 9195308b8..e42b16ebc 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -1,4 +1,4 @@ -//! Blocking I2C API +//! Blocking I2C API. //! //! This API supports 7-bit and 10-bit addresses. Traits feature an [`AddressMode`] //! marker type parameter. Two implementation of the [`AddressMode`] exist: @@ -157,9 +157,9 @@ use crate::private; #[cfg(feature = "defmt-03")] use crate::defmt; -/// I2C error +/// I2C error. pub trait Error: core::fmt::Debug { - /// Convert error to a generic I2C error kind + /// Convert error to a generic I2C error kind. /// /// By using this method, I2C errors freely defined by HAL implementations /// can be converted to a set of generic I2C errors upon which generic @@ -168,12 +168,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// I2C error kind +/// I2C error kind. /// /// This represents a common set of I2C operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -185,19 +186,19 @@ pub enum ErrorKind { /// Bus error occurred. e.g. A START or a STOP condition is detected and is not /// located after a multiple of 9 SCL clock pulses. Bus, - /// The arbitration was lost, e.g. electrical problems with the clock signal + /// The arbitration was lost, e.g. electrical problems with the clock signal. ArbitrationLoss, /// A bus operation was not acknowledged, e.g. due to the addressed device not /// being available on the bus or the device not being ready to process requests - /// at the moment + /// at the moment. NoAcknowledge(NoAcknowledgeSource), - /// The peripheral receive buffer was overrun + /// The peripheral receive buffer was overrun. Overrun, /// A different error occurred. The original error may contain more information. Other, } -/// I2C no acknowledge error source +/// I2C no acknowledge error source. /// /// In cases where it is possible, a device should indicate if a no acknowledge /// response was received to an address versus a no acknowledge to a data byte. @@ -216,12 +217,14 @@ pub enum NoAcknowledgeSource { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Bus => write!(f, "Bus error occurred"), @@ -237,6 +240,7 @@ impl core::fmt::Display for ErrorKind { } impl core::fmt::Display for NoAcknowledgeSource { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Address => write!(f, "The device did not acknowledge its address"), @@ -246,7 +250,7 @@ impl core::fmt::Display for NoAcknowledgeSource { } } -/// I2C error type trait +/// I2C error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -258,15 +262,15 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Address mode (7-bit / 10-bit) +/// Address mode (7-bit / 10-bit). /// /// Note: This trait is sealed and should not be implemented outside of this crate. pub trait AddressMode: private::Sealed + 'static {} -/// 7-bit address mode type +/// 7-bit address mode type. pub type SevenBitAddress = u8; -/// 10-bit address mode type +/// 10-bit address mode type. pub type TenBitAddress = u16; impl AddressMode for SevenBitAddress {} @@ -279,15 +283,15 @@ impl AddressMode for TenBitAddress {} #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a> { - /// Read data into the provided buffer + /// Read data into the provided buffer. Read(&'a mut [u8]), - /// Write data from the provided buffer + /// Write data from the provided buffer. Write(&'a [u8]), } -/// Blocking I2C +/// Blocking I2C. pub trait I2c: ErrorType { - /// Reads enough bytes from slave with `address` to fill `read` + /// Reads enough bytes from slave with `address` to fill `read`. /// /// # I2C Events (contract) /// @@ -305,11 +309,12 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Read(read)]) } - /// Writes bytes to slave with address `address` + /// Writes bytes to slave with address `address`. /// /// # I2C Events (contract) /// @@ -325,12 +330,13 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition + #[inline] fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Write(write)]) } /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a - /// single transaction* + /// single transaction*. /// /// # I2C Events (contract) /// @@ -351,6 +357,7 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { self.transaction( address, @@ -379,18 +386,22 @@ pub trait I2c: ErrorType { } impl + ?Sized> I2c for &mut T { + #[inline] fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read) } + #[inline] fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { T::write(self, address, write) } + #[inline] fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { T::write_read(self, address, write, read) } + #[inline] fn transaction( &mut self, address: A, diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 3893eaade..7f3b0828c 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -1,11 +1,11 @@ -//! Pulse Width Modulation (PWM) traits +//! Pulse Width Modulation (PWM) traits. #[cfg(feature = "defmt-03")] use crate::defmt; /// Error pub trait Error: core::fmt::Debug { - /// Convert error to a generic error kind + /// Convert error to a generic error kind. /// /// By using this method, errors freely defined by HAL implementations /// can be converted to a set of generic errors upon which generic @@ -14,12 +14,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// Error kind +/// Error kind. /// /// This represents a common set of operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -33,12 +34,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Other => write!( @@ -49,7 +52,7 @@ impl core::fmt::Display for ErrorKind { } } -/// Error type trait +/// Error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -61,7 +64,7 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Single PWM channel / pin +/// Single PWM channel / pin. pub trait SetDutyCycle: ErrorType { /// Get the maximum duty cycle value. /// @@ -106,26 +109,32 @@ pub trait SetDutyCycle: ErrorType { } impl SetDutyCycle for &mut T { + #[inline] fn get_max_duty_cycle(&self) -> u16 { T::get_max_duty_cycle(self) } + #[inline] fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { T::set_duty_cycle(self, duty) } + #[inline] fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { T::set_duty_cycle_fully_off(self) } + #[inline] fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { T::set_duty_cycle_fully_on(self) } + #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { T::set_duty_cycle_fraction(self, num, denom) } + #[inline] fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { T::set_duty_cycle_percent(self, percent) } diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 3813b9a6c..e855aa47a 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -166,63 +166,63 @@ use core::fmt::Debug; #[cfg(feature = "defmt-03")] use crate::defmt; -/// Clock polarity +/// Clock polarity. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Polarity { - /// Clock signal low when idle + /// Clock signal low when idle. IdleLow, - /// Clock signal high when idle + /// Clock signal high when idle. IdleHigh, } -/// Clock phase +/// Clock phase. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Phase { - /// Data in "captured" on the first clock transition + /// Data in "captured" on the first clock transition. CaptureOnFirstTransition, - /// Data in "captured" on the second clock transition + /// Data in "captured" on the second clock transition. CaptureOnSecondTransition, } -/// SPI mode +/// SPI mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct Mode { - /// Clock polarity + /// Clock polarity. pub polarity: Polarity, - /// Clock phase + /// Clock phase. pub phase: Phase, } -/// Helper for CPOL = 0, CPHA = 0 +/// Helper for CPOL = 0, CPHA = 0. pub const MODE_0: Mode = Mode { polarity: Polarity::IdleLow, phase: Phase::CaptureOnFirstTransition, }; -/// Helper for CPOL = 0, CPHA = 1 +/// Helper for CPOL = 0, CPHA = 1. pub const MODE_1: Mode = Mode { polarity: Polarity::IdleLow, phase: Phase::CaptureOnSecondTransition, }; -/// Helper for CPOL = 1, CPHA = 0 +/// Helper for CPOL = 1, CPHA = 0. pub const MODE_2: Mode = Mode { polarity: Polarity::IdleHigh, phase: Phase::CaptureOnFirstTransition, }; -/// Helper for CPOL = 1, CPHA = 1 +/// Helper for CPOL = 1, CPHA = 1. pub const MODE_3: Mode = Mode { polarity: Polarity::IdleHigh, phase: Phase::CaptureOnSecondTransition, }; -/// SPI error +/// SPI error. pub trait Error: core::fmt::Debug { - /// Convert error to a generic SPI error kind + /// Convert error to a generic SPI error kind. /// /// By using this method, SPI errors freely defined by HAL implementations /// can be converted to a set of generic SPI errors upon which generic @@ -231,12 +231,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// SPI error kind +/// SPI error kind. /// /// This represents a common set of SPI operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -245,11 +246,11 @@ impl Error for core::convert::Infallible { #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { - /// The peripheral receive buffer was overrun + /// The peripheral receive buffer was overrun. Overrun, - /// Multiple devices on the SPI bus are trying to drive the slave select pin, e.g. in a multi-master setup + /// Multiple devices on the SPI bus are trying to drive the slave select pin, e.g. in a multi-master setup. ModeFault, - /// Received data does not conform to the peripheral configuration + /// Received data does not conform to the peripheral configuration. FrameFormat, /// An error occurred while asserting or deasserting the Chip Select pin. ChipSelectFault, @@ -258,12 +259,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), @@ -287,11 +290,11 @@ impl core::fmt::Display for ErrorKind { } } -/// SPI error type trait +/// SPI error type trait. /// /// This just defines the error type, to be used by the other SPI traits. pub trait ErrorType { - /// Error type + /// Error type. type Error: Error; } @@ -301,7 +304,7 @@ impl ErrorType for &mut T { /// SPI transaction operation. /// -/// This allows composition of SPI operations into a single bus transaction +/// This allows composition of SPI operations into a single bus transaction. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a, Word: 'static> { @@ -309,7 +312,7 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::read`]. Read(&'a mut [Word]), - /// Write data from the provided buffer, discarding read data + /// Write data from the provided buffer, discarding read data. /// /// Equivalent to [`SpiBus::write`]. Write(&'a [Word]), @@ -317,15 +320,15 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::transfer`]. Transfer(&'a mut [Word], &'a [Word]), - /// Write data out while reading data into the provided buffer + /// Write data out while reading data into the provided buffer. /// /// Equivalent to [`SpiBus::transfer_in_place`]. TransferInPlace(&'a mut [Word]), - /// Delay for at least the specified number of microseconds + /// Delay for at least the specified number of microseconds. DelayUs(u32), } -/// SPI device trait +/// SPI device trait. /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. @@ -354,6 +357,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Read(buf)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::read`] + #[inline] fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Read(buf)]) } @@ -363,6 +367,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Write(buf)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::write`] + #[inline] fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Write(buf)]) } @@ -372,6 +377,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)]`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] + #[inline] fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Transfer(read, write)]) } @@ -381,34 +387,40 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)]`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] + #[inline] fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::TransferInPlace(buf)]) } } impl + ?Sized> SpiDevice for &mut T { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { T::transaction(self, operations) } + #[inline] fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::read(self, buf) } + #[inline] fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { T::write(self, buf) } + #[inline] fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) } + #[inline] fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, buf) } } -/// SPI bus +/// SPI bus. /// /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// @@ -423,7 +435,7 @@ pub trait SpiBus: ErrorType { /// complete. See the [module-level documentation](self) for details. fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; - /// Write `words` to the slave, ignoring all the incoming words + /// Write `words` to the slave, ignoring all the incoming words. /// /// Implementations are allowed to return before the operation is /// complete. See the [module-level documentation](self) for details. @@ -457,22 +469,27 @@ pub trait SpiBus: ErrorType { } impl + ?Sized, Word: Copy + 'static> SpiBus for &mut T { + #[inline] fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words) } + #[inline] fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { T::write(self, words) } + #[inline] fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) } + #[inline] fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, words) } + #[inline] fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self) } From 41a61cb00127709bf3333829388d2d650b7dcbd3 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:16:09 +0800 Subject: [PATCH 048/201] e-h-async: document fixes and inline hints. Signed-off-by: Zhouqi Jiang --- embedded-hal-async/src/delay.rs | 6 ++++-- embedded-hal-async/src/digital.rs | 7 ++++++- embedded-hal-async/src/i2c.rs | 15 +++++++++++---- embedded-hal-async/src/spi.rs | 20 +++++++++++++++++--- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index acf18f2af..c1d57c8e6 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -1,6 +1,6 @@ -//! Delays +//! Delays. -/// Microsecond delay +/// Microsecond delay. pub trait DelayUs { /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. @@ -15,10 +15,12 @@ impl DelayUs for &mut T where T: DelayUs + ?Sized, { + #[inline] async fn delay_us(&mut self, us: u32) { T::delay_us(self, us).await } + #[inline] async fn delay_ms(&mut self, ms: u32) { T::delay_ms(self, ms).await } diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 8f09eb78d..4fb3843cb 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -1,4 +1,4 @@ -//! Asynchronous digital I/O +//! Asynchronous digital I/O. //! //! # Example //! @@ -49,22 +49,27 @@ pub trait Wait: embedded_hal::digital::ErrorType { } impl Wait for &mut T { + #[inline] async fn wait_for_high(&mut self) -> Result<(), Self::Error> { T::wait_for_high(self).await } + #[inline] async fn wait_for_low(&mut self) -> Result<(), Self::Error> { T::wait_for_low(self).await } + #[inline] async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { T::wait_for_rising_edge(self).await } + #[inline] async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { T::wait_for_falling_edge(self).await } + #[inline] async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { T::wait_for_any_edge(self).await } diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 0837da8dd..1a8f07a48 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -1,4 +1,4 @@ -//! Async I2C API +//! Async I2C API. //! //! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode` //! marker type parameter. Two implementation of the `AddressMode` exist: @@ -21,9 +21,9 @@ pub use embedded_hal::i2c::{ AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, SevenBitAddress, TenBitAddress, }; -/// Async i2c +/// Async I2c. pub trait I2c: ErrorType { - /// Reads enough bytes from slave with `address` to fill `buffer` + /// Reads enough bytes from slave with `address` to fill `buffer`. /// /// # I2C Events (contract) /// @@ -41,12 +41,13 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Read(read)]) .await } - /// Writes bytes to slave with address `address` + /// Writes bytes to slave with address `address`. /// /// # I2C Events (contract) /// @@ -62,6 +63,7 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition + #[inline] async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Write(write)]) .await @@ -89,6 +91,7 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] async fn write_read( &mut self, address: A, @@ -123,14 +126,17 @@ pub trait I2c: ErrorType { } impl + ?Sized> I2c for &mut T { + #[inline] async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read).await } + #[inline] async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { T::write(self, address, write).await } + #[inline] async fn write_read( &mut self, address: A, @@ -140,6 +146,7 @@ impl + ?Sized> I2c for &mut T { T::write_read(self, address, write, read).await } + #[inline] async fn transaction( &mut self, address: A, diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index e9548359f..9c71f7e0d 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -4,7 +4,7 @@ pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -/// SPI device trait +/// SPI device trait. /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. @@ -36,6 +36,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.read_transaction(&mut [buf])`. /// /// See also: [`SpiDevice::transaction`], [`SpiDevice::read`] + #[inline] async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Read(buf)]).await } @@ -45,6 +46,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.write_transaction(&mut [buf])`. /// /// See also: [`SpiDevice::transaction`], [`SpiDevice::write`] + #[inline] async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Write(buf)]).await } @@ -54,6 +56,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer(read, write))`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] + #[inline] async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Transfer(read, write)]) .await @@ -64,6 +67,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer_in_place(buf))`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] + #[inline] async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::TransferInPlace(buf)]) .await @@ -71,6 +75,7 @@ pub trait SpiDevice: ErrorType { } impl + ?Sized> SpiDevice for &mut T { + #[inline] async fn transaction( &mut self, operations: &mut [Operation<'_, Word>], @@ -78,24 +83,28 @@ impl + ?Sized> SpiDevice for &mut T::transaction(self, operations).await } + #[inline] async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::read(self, buf).await } + #[inline] async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { T::write(self, buf).await } + #[inline] async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write).await } + #[inline] async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, buf).await } } -/// SPI bus +/// SPI bus. /// /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// @@ -110,7 +119,7 @@ pub trait SpiBus: ErrorType { /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; - /// Write `words` to the slave, ignoring all the incoming words + /// Write `words` to the slave, ignoring all the incoming words. /// /// Implementations are allowed to return before the operation is /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. @@ -144,22 +153,27 @@ pub trait SpiBus: ErrorType { } impl + ?Sized, Word: 'static + Copy> SpiBus for &mut T { + #[inline] async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words).await } + #[inline] async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { T::write(self, words).await } + #[inline] async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write).await } + #[inline] async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, words).await } + #[inline] async fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self).await } From d81f7b8142966a8c65ce20b60fbef24fd787d05c Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:20:19 +0800 Subject: [PATCH 049/201] e-h-bus: document fixes and inline hints. Signed-off-by: Zhouqi Jiang --- embedded-hal-bus/src/i2c/critical_section.rs | 7 ++++++- embedded-hal-bus/src/i2c/mutex.rs | 7 ++++++- embedded-hal-bus/src/i2c/refcell.rs | 7 ++++++- embedded-hal-bus/src/spi/critical_section.rs | 5 ++++- embedded-hal-bus/src/spi/exclusive.rs | 8 +++++++- embedded-hal-bus/src/spi/mod.rs | 10 +++++++--- embedded-hal-bus/src/spi/mutex.rs | 5 ++++- embedded-hal-bus/src/spi/refcell.rs | 5 ++++- 8 files changed, 44 insertions(+), 10 deletions(-) diff --git a/embedded-hal-bus/src/i2c/critical_section.rs b/embedded-hal-bus/src/i2c/critical_section.rs index 32334cbcc..19ca73ee2 100644 --- a/embedded-hal-bus/src/i2c/critical_section.rs +++ b/embedded-hal-bus/src/i2c/critical_section.rs @@ -14,7 +14,8 @@ pub struct CriticalSectionDevice<'a, T> { } impl<'a, T> CriticalSectionDevice<'a, T> { - /// Create a new `CriticalSectionDevice` + /// Create a new `CriticalSectionDevice`. + #[inline] pub fn new(bus: &'a Mutex>) -> Self { Self { bus } } @@ -31,6 +32,7 @@ impl<'a, T> I2c for CriticalSectionDevice<'a, T> where T: I2c, { + #[inline] fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); @@ -38,6 +40,7 @@ where }) } + #[inline] fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); @@ -45,6 +48,7 @@ where }) } + #[inline] fn write_read( &mut self, address: u8, @@ -57,6 +61,7 @@ where }) } + #[inline] fn transaction( &mut self, address: u8, diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs index ff9da7b2a..3b95eec98 100644 --- a/embedded-hal-bus/src/i2c/mutex.rs +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -12,7 +12,8 @@ pub struct MutexDevice<'a, T> { } impl<'a, T> MutexDevice<'a, T> { - /// Create a new `MutexDevice` + /// Create a new `MutexDevice`. + #[inline] pub fn new(bus: &'a Mutex) -> Self { Self { bus } } @@ -29,16 +30,19 @@ impl<'a, T> I2c for MutexDevice<'a, T> where T: I2c, { + #[inline] fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); bus.read(address, read) } + #[inline] fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); bus.write(address, write) } + #[inline] fn write_read( &mut self, address: u8, @@ -49,6 +53,7 @@ where bus.write_read(address, write, read) } + #[inline] fn transaction( &mut self, address: u8, diff --git a/embedded-hal-bus/src/i2c/refcell.rs b/embedded-hal-bus/src/i2c/refcell.rs index 3d6564434..1519a751b 100644 --- a/embedded-hal-bus/src/i2c/refcell.rs +++ b/embedded-hal-bus/src/i2c/refcell.rs @@ -68,7 +68,8 @@ pub struct RefCellDevice<'a, T> { } impl<'a, T> RefCellDevice<'a, T> { - /// Create a new `RefCellDevice` + /// Create a new `RefCellDevice`. + #[inline] pub fn new(bus: &'a RefCell) -> Self { Self { bus } } @@ -85,16 +86,19 @@ impl<'a, T> I2c for RefCellDevice<'a, T> where T: I2c, { + #[inline] fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); bus.read(address, read) } + #[inline] fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); bus.write(address, write) } + #[inline] fn write_read( &mut self, address: u8, @@ -105,6 +109,7 @@ where bus.write_read(address, write, read) } + #[inline] fn transaction( &mut self, address: u8, diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 2a4c09788..2a93323a6 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -23,7 +23,8 @@ pub struct CriticalSectionDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice + /// Create a new ExclusiveDevice. + #[inline] pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } @@ -36,6 +37,7 @@ impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { Self { bus, @@ -59,6 +61,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index da2381945..5d5c035d9 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -22,17 +22,20 @@ pub struct ExclusiveDevice { } impl ExclusiveDevice { - /// Create a new ExclusiveDevice + /// Create a new ExclusiveDevice. + #[inline] pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } /// Returns a reference to the underlying bus object. + #[inline] pub fn bus(&self) -> &BUS { &self.bus } /// Returns a mutable reference to the underlying bus object. + #[inline] pub fn bus_mut(&mut self) -> &mut BUS { &mut self.bus } @@ -45,6 +48,7 @@ impl ExclusiveDevice { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: BUS, cs: CS) -> Self { Self { bus, @@ -68,6 +72,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { self.cs.set_low().map_err(DeviceError::Cs)?; @@ -103,6 +108,7 @@ where CS: OutputPin, D: AsyncDelayUs, { + #[inline] async fn transaction( &mut self, operations: &mut [Operation<'_, Word>], diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index e3e888c8e..090f44649 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -21,9 +21,9 @@ use crate::defmt; #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum DeviceError { - /// An inner SPI bus operation failed + /// An inner SPI bus operation failed. Spi(BUS), - /// Asserting or deasserting CS failed + /// Asserting or deasserting CS failed. Cs(CS), } @@ -32,6 +32,7 @@ where BUS: Error + Debug, CS: Debug, { + #[inline] fn kind(&self) -> ErrorKind { match self { Self::Spi(e) => e.kind(), @@ -51,6 +52,7 @@ fn no_delay_panic() { } impl embedded_hal::delay::DelayUs for NoDelay { + #[inline] fn delay_us(&mut self, _us: u32) { no_delay_panic(); } @@ -59,11 +61,13 @@ impl embedded_hal::delay::DelayUs for NoDelay { #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] impl embedded_hal_async::delay::DelayUs for NoDelay { + #[inline] async fn delay_us(&mut self, _us: u32) { no_delay_panic(); } - async fn delay_ms(&mut self, _us: u32) { + #[inline] + async fn delay_ms(&mut self, _ms: u32) { no_delay_panic(); } } diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 7030ca02c..b37c975ac 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -21,7 +21,8 @@ pub struct MutexDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice + /// Create a new MutexDevice. + #[inline] pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } @@ -34,6 +35,7 @@ impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { Self { bus, @@ -57,6 +59,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index a08e8a262..6524b8a93 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -20,7 +20,8 @@ pub struct RefCellDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice + /// Create a new RefCellDevice. + #[inline] pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } @@ -33,6 +34,7 @@ impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { Self { bus, @@ -56,6 +58,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); From 8296af2e29b320aa8c041618ca078342e71b3b6c Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:22:20 +0800 Subject: [PATCH 050/201] e-h-nb: inline hints, document fixes. Signed-off-by: Zhouqi Jiang --- embedded-hal-nb/src/serial.rs | 23 +++++++++++++++-------- embedded-hal-nb/src/spi.rs | 6 ++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index 624aadee3..18d1265c2 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -1,6 +1,6 @@ -//! Serial interface +//! Serial interface. -/// Serial error +/// Serial error. pub trait Error: core::fmt::Debug { /// Convert error to a generic serial error kind /// @@ -11,12 +11,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// Serial error kind +/// Serial error kind. /// /// This represents a common set of serial operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -38,12 +39,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), @@ -61,7 +64,7 @@ impl core::fmt::Display for ErrorKind { } } -/// Serial error type trait +/// Serial error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -73,7 +76,7 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Read half of a serial interface +/// Read half of a serial interface. /// /// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.); /// This can be encoded in this trait via the `Word` type parameter. @@ -83,25 +86,28 @@ pub trait Read: ErrorType { } impl + ?Sized, Word: Copy> Read for &mut T { + #[inline] fn read(&mut self) -> nb::Result { T::read(self) } } -/// Write half of a serial interface +/// Write half of a serial interface. pub trait Write: ErrorType { - /// Writes a single word to the serial interface + /// Writes a single word to the serial interface. fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; - /// Ensures that none of the previously written words are still buffered + /// Ensures that none of the previously written words are still buffered. fn flush(&mut self) -> nb::Result<(), Self::Error>; } impl + ?Sized, Word: Copy> Write for &mut T { + #[inline] fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { T::write(self, word) } + #[inline] fn flush(&mut self) -> nb::Result<(), Self::Error> { T::flush(self) } @@ -115,6 +121,7 @@ impl core::fmt::Write for dyn Write, { + #[inline] fn write_str(&mut self, s: &str) -> core::fmt::Result { let _ = s .bytes() diff --git a/embedded-hal-nb/src/spi.rs b/embedded-hal-nb/src/spi.rs index ecdb88ffb..06689db4c 100644 --- a/embedded-hal-nb/src/spi.rs +++ b/embedded-hal-nb/src/spi.rs @@ -4,11 +4,11 @@ pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -/// Full duplex SPI (master mode) +/// Full duplex SPI (master mode). /// /// # Notes /// -/// - It's the task of the user of this interface to manage the slave select lines +/// - It's the task of the user of this interface to manage the slave select lines. /// /// - Due to how full duplex SPI works each `read` call must be preceded by a `write` call. /// @@ -32,10 +32,12 @@ pub trait FullDuplex: ErrorType { } impl + ?Sized, Word: Copy> FullDuplex for &mut T { + #[inline] fn read(&mut self) -> nb::Result { T::read(self) } + #[inline] fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { T::write(self, word) } From d2f099e54fba531987207f39482137709a0953ba Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:26:47 +0800 Subject: [PATCH 051/201] e-h, e-h-async, e-h-bus, e-h-nb: add changelog entries Signed-off-by: Zhouqi Jiang --- embedded-hal-async/CHANGELOG.md | 3 +++ embedded-hal-bus/CHANGELOG.md | 3 +++ embedded-hal-nb/CHANGELOG.md | 3 ++- embedded-hal/CHANGELOG.md | 3 +++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 478e994f7..c94153a3e 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal-async` functions. + ## [v1.0.0-rc.1] - 2023-08-15 - Updated `embedded-hal` to version `1.0.0-rc.1`. diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index ead1de58f..59b398d1a 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal-bus` functions. + ## [v0.1.0-rc.1] - 2023-08-15 - Updated `embedded-hal`, `embedded-hal-async` to version `1.0.0-rc.1`. diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 83b7e1510..f4a1e7944 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -... +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal-nb` functions. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 62f78ff36..8c6109d6c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal` functions. + ## [v1.0.0-rc.1] - 2023-08-15 - The Minimum Supported Rust Version (MSRV) is now 1.60.0 From 2c9db0d682b37c931ffdd011d1c81cf2ebcc1976 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 25 Aug 2023 01:11:15 +0200 Subject: [PATCH 052/201] io-adapters: add support for BufRead for `futures` and `tokio`. --- embedded-io-adapters/CHANGELOG.md | 4 ++++ embedded-io-adapters/Cargo.toml | 2 +- embedded-io-adapters/src/futures_03.rs | 12 ++++++++++++ embedded-io-adapters/src/tokio_1.rs | 12 ++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index 978a92d8b..bf9f2d391 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Add support for adapting `BufRead` from `futures` and `tokio`. + ## 0.5.0 - 2023-08-06 - First release \ No newline at end of file diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 5dbcf4354..a849b1f12 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -21,7 +21,7 @@ embedded-io = { version = "0.5", path = "../embedded-io" } embedded-io-async = { version = "0.5", path = "../embedded-io-async", optional = true } futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } -tokio = { version = "1", default-features = false, optional = true } +tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } [package.metadata.docs.rs] features = ["std", "tokio-1", "futures-03"] diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index b672215ed..a3402cbb9 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -3,6 +3,8 @@ use core::future::poll_fn; use core::pin::Pin; +use futures::AsyncBufReadExt; + /// Adapter from `futures::io` traits. #[derive(Clone)] pub struct FromFutures { @@ -43,6 +45,16 @@ impl embedded_io_async::Read for Fro } } +impl embedded_io_async::BufRead for FromFutures { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + Pin::new(&mut self.inner).consume(amt) + } +} + impl embedded_io_async::Write for FromFutures { async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 04e1cf1aa..92060a4fe 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -4,6 +4,8 @@ use core::future::poll_fn; use core::pin::Pin; use core::task::Poll; +use tokio::io::AsyncBufReadExt; + /// Adapter from `tokio::io` traits. #[derive(Clone)] pub struct FromTokio { @@ -54,6 +56,16 @@ impl embedded_io_async::Read for FromT } } +impl embedded_io_async::BufRead for FromTokio { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + Pin::new(&mut self.inner).consume(amt) + } +} + impl embedded_io_async::Write for FromTokio { async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await From 10a7effe167f4d0134de3410aefb293b14b63801 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 31 Aug 2023 23:10:52 -0400 Subject: [PATCH 053/201] Inline a few format args for readability This existed since 1.58, so safe for MSRV --- embedded-io-adapters/src/std.rs | 2 +- embedded-io/src/lib.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index ac6a606d3..cd3df2d75 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -118,5 +118,5 @@ impl std::io::Seek for ToStd { /// Convert a embedded-io error to a std::io::Error pub fn to_std_error(err: T) -> std::io::Error { - std::io::Error::new(err.kind().into(), format!("{:?}", err)) + std::io::Error::new(err.kind().into(), format!("{err:?}")) } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f798b5b6e..b0867ed62 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -243,7 +243,7 @@ impl From> for std::io::Error { std::io::ErrorKind::UnexpectedEof, "UnexpectedEof".to_owned(), ), - ReadExactError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + ReadExactError::Other(e) => std::io::Error::new(e.kind(), format!("{e:?}")), } } } @@ -256,14 +256,14 @@ impl From> for std::io::Error { WriteAllError::WriteZero => { std::io::Error::new(std::io::ErrorKind::WriteZero, "WriteZero".to_owned()) } - WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{e:?}")), } } } impl fmt::Display for ReadExactError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } @@ -291,7 +291,7 @@ impl From for WriteFmtError { impl fmt::Display for WriteFmtError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } @@ -317,7 +317,7 @@ impl From for WriteAllError { impl fmt::Display for WriteAllError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } From 7bcaa871d63f4c38e7815248361d8e8eee14f8c0 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 31 Aug 2023 23:59:18 -0400 Subject: [PATCH 054/201] Fix doc links and one incorrect type name --- embedded-hal-async/src/spi.rs | 14 +++++++------- embedded-hal-bus/src/spi/critical_section.rs | 6 +++--- embedded-hal-bus/src/spi/exclusive.rs | 4 ++-- embedded-hal-bus/src/spi/refcell.rs | 4 ++-- embedded-hal-nb/src/lib.rs | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 9c71f7e0d..38d471d9a 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -9,7 +9,7 @@ pub use embedded_hal::spi::{ /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. /// -/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. +/// See [the docs on embedded-hal](embedded_hal::spi) for important information on SPI Bus vs Device traits. pub trait SpiDevice: ErrorType { /// Perform a transaction against the device. /// @@ -108,7 +108,7 @@ impl + ?Sized> SpiDevice for &mut /// /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// -/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. +/// See [the docs on embedded-hal][embedded_hal::spi] for important information on SPI Bus vs Device traits. pub trait SpiBus: ErrorType { /// Read `words` from the slave. /// @@ -116,13 +116,13 @@ pub trait SpiBus: ErrorType { /// typically `0x00`, `0xFF`, or configurable. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; /// Write `words` to the slave, ignoring all the incoming words. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; /// Write and read simultaneously. `write` is written to the slave on MOSI and @@ -135,7 +135,7 @@ pub trait SpiBus: ErrorType { /// typically `0x00`, `0xFF`, or configurable. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; /// Write and read simultaneously. The contents of `words` are @@ -143,12 +143,12 @@ pub trait SpiBus: ErrorType { /// `words` buffer, overwriting it. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; /// Wait until all operations have completed and the bus is idle. /// - /// See (the docs on embedded-hal)[embedded_hal::spi] for information on flushing. + /// See [the docs on embedded-hal][embedded_hal::spi] for information on flushing. async fn flush(&mut self) -> Result<(), Self::Error>; } diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 2a93323a6..baf080167 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -23,7 +23,7 @@ pub struct CriticalSectionDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice. + /// Create a new [`CriticalSectionDevice`]. #[inline] pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -31,12 +31,12 @@ impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { - /// Create a new CriticalSectionDevice without support for in-transaction delays. + /// Create a new [`CriticalSectionDevice`] without support for in-transaction delays. /// /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayUs`]. #[inline] pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 5d5c035d9..a8266e296 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -22,7 +22,7 @@ pub struct ExclusiveDevice { } impl ExclusiveDevice { - /// Create a new ExclusiveDevice. + /// Create a new [`ExclusiveDevice`]. #[inline] pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -42,7 +42,7 @@ impl ExclusiveDevice { } impl ExclusiveDevice { - /// Create a new ExclusiveDevice without support for in-transaction delays. + /// Create a new [`ExclusiveDevice`] without support for in-transaction delays. /// /// # Panics /// diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 6524b8a93..021852080 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -20,7 +20,7 @@ pub struct RefCellDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { - /// Create a new RefCellDevice. + /// Create a new [`RefCellDevice`]. #[inline] pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -28,7 +28,7 @@ impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { - /// Create a new RefCellDevice without support for in-transaction delays. + /// Create a new [`RefCellDevice`] without support for in-transaction delays. /// /// # Panics /// diff --git a/embedded-hal-nb/src/lib.rs b/embedded-hal-nb/src/lib.rs index d2089c5af..4cdf90dc1 100644 --- a/embedded-hal-nb/src/lib.rs +++ b/embedded-hal-nb/src/lib.rs @@ -57,7 +57,7 @@ //! [`svd2rust`]: https://crates.io/crates/svd2rust //! //! Shown below is an implementation of some of the HAL traits for the [`stm32f1xx-hal`] crate. This -//! single implementation will work for *any* microcontroller in the STM32F1xx family. +//! single implementation will work for *any* microcontroller in the `STM32F1xx` family. //! //! [`stm32f1`]: https://crates.io/crates/stm32f1 //! From 3005247c9bbe3da70554f12a388adeca13e1c593 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 00:03:36 -0400 Subject: [PATCH 055/201] A few clippy lint fixes * added `[must_use]` * added a few semicolons --- embedded-can/src/id.rs | 7 +++++++ embedded-hal-async/src/delay.rs | 4 ++-- embedded-hal/src/delay.rs | 4 ++-- embedded-io-async/src/lib.rs | 8 ++++---- embedded-io/src/lib.rs | 8 ++++---- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index 6fb377190..2774b6c3d 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -15,6 +15,7 @@ impl StandardId { /// /// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`). #[inline] + #[must_use] pub const fn new(raw: u16) -> Option { if raw <= 0x7FF { Some(Self(raw)) @@ -28,12 +29,14 @@ impl StandardId { /// # Safety /// Using this method can create an invalid ID and is thus marked as unsafe. #[inline] + #[must_use] pub const unsafe fn new_unchecked(raw: u16) -> Self { Self(raw) } /// Returns this CAN Identifier as a raw 16-bit integer. #[inline] + #[must_use] pub const fn as_raw(&self) -> u16 { self.0 } @@ -54,6 +57,7 @@ impl ExtendedId { /// /// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`). #[inline] + #[must_use] pub const fn new(raw: u32) -> Option { if raw <= 0x1FFF_FFFF { Some(Self(raw)) @@ -67,17 +71,20 @@ impl ExtendedId { /// # Safety /// Using this method can create an invalid ID and is thus marked as unsafe. #[inline] + #[must_use] pub const unsafe fn new_unchecked(raw: u32) -> Self { Self(raw) } /// Returns this CAN Identifier as a raw 32-bit integer. #[inline] + #[must_use] pub const fn as_raw(&self) -> u32 { self.0 } /// Returns the Base ID part of this extended identifier. + #[must_use] pub fn standard_id(&self) -> StandardId { // ID-28 to ID-18 StandardId((self.0 >> 18) as u16) diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index c1d57c8e6..62bd742f2 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -17,11 +17,11 @@ where { #[inline] async fn delay_us(&mut self, us: u32) { - T::delay_us(self, us).await + T::delay_us(self, us).await; } #[inline] async fn delay_ms(&mut self, ms: u32) { - T::delay_ms(self, ms).await + T::delay_ms(self, ms).await; } } diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 32f085729..77b5633d7 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -22,11 +22,11 @@ where { #[inline] fn delay_us(&mut self, us: u32) { - T::delay_us(self, us) + T::delay_us(self, us); } #[inline] fn delay_ms(&mut self, ms: u32) { - T::delay_ms(self, ms) + T::delay_ms(self, ms); } } diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index c94e349a4..98a656be8 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -58,10 +58,10 @@ pub trait Read: ErrorType { Err(e) => return Err(ReadExactError::Other(e)), } } - if !buf.is_empty() { - Err(ReadExactError::UnexpectedEof) - } else { + if buf.is_empty() { Ok(()) + } else { + Err(ReadExactError::UnexpectedEof) } } } @@ -170,7 +170,7 @@ impl BufRead for &mut T { } fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f798b5b6e..28a496743 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -362,10 +362,10 @@ pub trait Read: ErrorType { Err(e) => return Err(ReadExactError::Other(e)), } } - if !buf.is_empty() { - Err(ReadExactError::UnexpectedEof) - } else { + if buf.is_empty() { Ok(()) + } else { + Err(ReadExactError::UnexpectedEof) } } } @@ -535,7 +535,7 @@ impl BufRead for &mut T { } fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } From 4e18fc0d6f74c2d641c9f73436c619dd8ce2fcd5 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 00:22:41 -0400 Subject: [PATCH 056/201] Use explicit lossless conversion functions Per Clippy, [cast_lossless](https://rust-lang.github.io/rust-clippy/master/index.html#/cast_lossless) is preferred --- embedded-hal/src/pwm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 7f3b0828c..e97a93548 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -95,7 +95,7 @@ pub trait SetDutyCycle: ErrorType { /// and that `denom` is not zero. #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - let duty = num as u32 * self.get_max_duty_cycle() as u32 / denom as u32; + let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); self.set_duty_cycle(duty as u16) } @@ -104,7 +104,7 @@ pub trait SetDutyCycle: ErrorType { /// The caller is responsible for ensuring that `percent` is less than or equal to 100. #[inline] fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { - self.set_duty_cycle_fraction(percent as u16, 100) + self.set_duty_cycle_fraction(u16::from(percent), 100) } } From b0b4f4668db1e8ab5fc0db728ffed8d66697107d Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:25:34 -0400 Subject: [PATCH 057/201] A few more doc cleanups --- embedded-hal-bus/src/i2c/critical_section.rs | 2 +- embedded-hal-bus/src/i2c/mutex.rs | 2 +- embedded-hal-bus/src/spi/critical_section.rs | 4 ++-- embedded-hal-bus/src/spi/exclusive.rs | 2 +- embedded-hal-bus/src/spi/mutex.rs | 4 ++-- embedded-hal-bus/src/spi/refcell.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embedded-hal-bus/src/i2c/critical_section.rs b/embedded-hal-bus/src/i2c/critical_section.rs index 19ca73ee2..6830f284f 100644 --- a/embedded-hal-bus/src/i2c/critical_section.rs +++ b/embedded-hal-bus/src/i2c/critical_section.rs @@ -4,7 +4,7 @@ use embedded_hal::i2c::{ErrorType, I2c}; /// `critical-section`-based shared bus [`I2c`] implementation. /// -/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for +/// Sharing is implemented with a `critical-section` [`Mutex`]. A critical section is taken for /// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs index 3b95eec98..ef4a9f510 100644 --- a/embedded-hal-bus/src/i2c/mutex.rs +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; /// `std` `Mutex`-based shared bus [`I2c`] implementation. /// -/// Sharing is implemented with an `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, +/// Sharing is implemented with an `std` [`Mutex`]. It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is that /// it is only available in `std` targets. #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index baf080167..5bad960af 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -8,10 +8,10 @@ use super::DeviceError; /// `critical-section`-based shared bus [`SpiDevice`] implementation. /// -/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// -/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for +/// Sharing is implemented with a `critical-section` [`Mutex`]. A critical section is taken for /// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index a8266e296..1642269e7 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -13,7 +13,7 @@ use super::DeviceError; /// [`SpiDevice`] implementation with exclusive access to the bus (not shared). /// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], /// ideal for when no sharing is required (only one SPI device is present on the bus). pub struct ExclusiveDevice { bus: BUS, diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index b37c975ac..d1288b995 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -7,10 +7,10 @@ use super::DeviceError; /// `std` `Mutex`-based shared bus [`SpiDevice`] implementation. /// -/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// -/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, +/// Sharing is implemented with a `std` [`Mutex`]. It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is /// it is only available in `std` targets. #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 021852080..d407620be 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -7,7 +7,7 @@ use super::DeviceError; /// `RefCell`-based shared bus [`SpiDevice`] implementation. /// -/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// /// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, From f3095b3d01bb813128af1054488b67447bf43085 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:30:51 -0400 Subject: [PATCH 058/201] A few minor spelling/grammar fixes --- README.md | 2 +- embedded-can/src/lib.rs | 2 +- embedded-hal/CHANGELOG.md | 6 +++--- embedded-hal/README.md | 8 ++++---- embedded-hal/src/i2c.rs | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 02cee6050..3d86509eb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Scope -`embedded-hal` serves as a foundation for building an ecosystem of platform agnostic drivers. +`embedded-hal` serves as a foundation for building an ecosystem of platform-agnostic drivers. (driver meaning library crates that let a target platform interface an external device like a digital sensor or a wireless transceiver). diff --git a/embedded-can/src/lib.rs b/embedded-can/src/lib.rs index db80c455b..2de2b6ebc 100644 --- a/embedded-can/src/lib.rs +++ b/embedded-can/src/lib.rs @@ -22,7 +22,7 @@ pub trait Frame: Sized { /// This will return `None` if the data length code (DLC) is not valid. fn new_remote(id: impl Into, dlc: usize) -> Option; - /// Returns true if this frame is a extended frame. + /// Returns true if this frame is an extended frame. fn is_extended(&self) -> bool; /// Returns true if this frame is a standard frame. diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 8c6109d6c..019080d5c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -93,7 +93,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed blanket impl of `DelayUs` not covering the `delay_ms` method. ### Changed -- `spi`: traits now enforce all impls on the same struct (eg `Transfer` and `Write`) have the same `Error` type. +- `spi`: traits now enforce all impls on the same struct (e.g. `Transfer` and `Write`) have the same `Error` type. - `digital`: traits now enforce all impls on the same struct have the same `Error` type. - `serial`: traits now enforce all impls on the same struct have the same `Error` type. - `i2c`: traits now enforce all impls on the same struct have the same `Error` type. @@ -150,7 +150,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Swap PWM channel arguments to references - All trait methods have been renamed to remove the `try_` prefix (i.e. `try_send` -> `send`) for consistency. -- Moved all traits into two sub modules for each feature depending on the execution model: `blocking` and `nb` (non-blocking). For example, the spi traits can now be found under `embedded_hal::spi::blocking` or `embedded_hal::spi::nb`. +- Moved all traits into two submodules for each feature depending on the execution model: `blocking` and `nb` (non-blocking). For example, the spi traits can now be found under `embedded_hal::spi::blocking` or `embedded_hal::spi::nb`. - Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules. - Re-export `nb::{block!, Error, Result}` to avoid version mismatches. These should be used instead of importing the `nb` crate directly in dependent crates. @@ -281,7 +281,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- Re-export most / unchanged traits from embedded-hal v0.2.x to allow inter-operation between HAL +- Re-export most / unchanged traits from embedded-hal v0.2.x to allow interoperation between HAL implementations and drivers that are using different minor versions. ## [v0.1.2] - 2018-02-14 diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 193c63b3a..391599ec5 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -35,7 +35,7 @@ Additionally, more domain-specific traits are available in separate crates: The HAL -- Must *erase* device specific details. Neither register, register blocks or magic values should +- Must *erase* device specific details. Neither register, register blocks, nor magic values should appear in the API. - Must be generic *within* a device and *across* devices. The API to use a serial interface must @@ -50,7 +50,7 @@ in blocking mode, with the `futures` model, with an async/await model or with a want higher level abstraction should *prefer to use this HAL* rather than *re-implement* register manipulation code. -- Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver +- Serve as a foundation for building an ecosystem of platform-agnostic drivers. Here driver means a library crate that lets a target platform interface an external device like a digital sensor or a wireless transceiver. The advantage of this system is that by writing the driver as a generic library on top of `embedded-hal` driver authors can support any number of target @@ -70,10 +70,10 @@ interface are not using the same pins". The HAL will focus on *doing I/O*. ## Platform agnostic drivers -You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching +You can find platform-agnostic drivers built on top of `embedded-hal` on crates.io by [searching for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). -If you are writing a platform agnostic driver yourself you are highly encouraged to [add the +If you are writing a platform-agnostic driver yourself you are highly encouraged to [add the embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) to your crate before publishing it! diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index e42b16ebc..8f4848a28 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -35,7 +35,7 @@ //! //! The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides several //! implementations for sharing I2C buses. You can use them to take an exclusive instance -//! you've received from a HAL and "split" it into mulitple shared ones, to instantiate +//! you've received from a HAL and "split" it into multiple shared ones, to instantiate //! several drivers on the same bus. //! //! # For driver authors From 6e461a0c4303077b0d3b5e91d747199d675d98ed Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:33:45 -0400 Subject: [PATCH 059/201] Add missing LICENSE-* files to embedded-io-async --- embedded-io-async/LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++ embedded-io-async/LICENSE-MIT | 25 ++++ 2 files changed, 226 insertions(+) create mode 100644 embedded-io-async/LICENSE-APACHE create mode 100644 embedded-io-async/LICENSE-MIT diff --git a/embedded-io-async/LICENSE-APACHE b/embedded-io-async/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/embedded-io-async/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/embedded-io-async/LICENSE-MIT b/embedded-io-async/LICENSE-MIT new file mode 100644 index 000000000..3f5b8019c --- /dev/null +++ b/embedded-io-async/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The embedded-io-async authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From 0df21a83a249a28cf9f04fa9dfa57a0df552cb82 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:36:01 -0400 Subject: [PATCH 060/201] Remove a few un-needed qualifiers for consistency Some types are referenced inconsistently, so removing qualifiers to keep them uniform --- embedded-hal/src/spi.rs | 2 +- embedded-io/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index e855aa47a..c8c8224fd 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -221,7 +221,7 @@ pub const MODE_3: Mode = Mode { }; /// SPI error. -pub trait Error: core::fmt::Debug { +pub trait Error: Debug { /// Convert error to a generic SPI error kind. /// /// By using this method, SPI errors freely defined by HAL implementations diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f798b5b6e..2daca4448 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -175,7 +175,7 @@ impl From for ErrorKind { /// /// This trait allows generic code to do limited inspecting of errors, /// to react differently to different kinds. -pub trait Error: core::fmt::Debug { +pub trait Error: fmt::Debug { /// Get the kind of this error. fn kind(&self) -> ErrorKind; } @@ -194,8 +194,8 @@ impl Error for ErrorKind { #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl crate::Error for std::io::Error { - fn kind(&self) -> crate::ErrorKind { +impl Error for std::io::Error { + fn kind(&self) -> ErrorKind { self.kind().into() } } From 6db17645557f260fbceebbbdb601e93c71a696ad Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 00:02:03 -0400 Subject: [PATCH 061/201] Cleanup Cargo features and optionals * Make sure all optional dependencies are not exposed automatically, but rather explicit --- embedded-io/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index a7ca732f3..696823e7b 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -14,6 +14,7 @@ categories = [ [features] std = ["alloc"] alloc = [] +defmt-03 = ["dep:defmt-03"] [dependencies] defmt-03 = { package = "defmt", version = "0.3", optional = true } From 90e2c5e877d968cc8ce9a759884cfa07b48c4bb1 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Wed, 13 Sep 2023 02:08:26 +0100 Subject: [PATCH 062/201] Clarify write() may only return Ok(0) if data is empty. In other situations an error should be returned instead; update ErrorKind to add this and update out slice_mut implementation to use it. --- embedded-io-async/src/lib.rs | 8 ++-- embedded-io/CHANGELOG.md | 12 +++++- embedded-io/src/impls/slice_mut.rs | 41 ++++++++++++++++-- embedded-io/src/lib.rs | 68 ++++++------------------------ 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 98a656be8..b38d13abf 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -10,7 +10,7 @@ extern crate alloc; mod impls; pub use embedded_io::{ - Error, ErrorKind, ErrorType, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, + Error, ErrorKind, ErrorType, ReadExactError, ReadReady, SeekFrom, WriteReady, }; /// Async reader. @@ -125,13 +125,13 @@ pub trait Write: ErrorType { /// /// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned /// future that hasn't completed yet, some bytes might have already been written. - async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteAllError> { + async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { let mut buf = buf; while !buf.is_empty() { match self.write(buf).await { - Ok(0) => return Err(WriteAllError::WriteZero), + Ok(0) => panic!("write() returned Ok(0)"), Ok(n) => buf = &buf[n..], - Err(e) => return Err(WriteAllError::Other(e)), + Err(e) => return Err(e), } } Ok(()) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 937a43140..f5a60a3df 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Prohibit `Write::write` implementations returning `Ok(0)` unless there is no + data to write; consequently remove `WriteAllError` and the `WriteAllError` + variant of `WriteFmtError`. Update the `&mut [u8]` impl to possibly return + a new `SliceWriteError` if the slice is full instead of `Ok(0)`. +- Add `WriteZero` variant to `ErrorKind` for implementations that previously + may have returned `Ok(0)` to indicate no further data could be written. +- `Write::write_all` now panics if the `write()` implementation returns `Ok(0)`. + ## 0.5.0 - 2023-08-06 - Add `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. @@ -37,4 +47,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.2.0 - 2022-05-07 -- First release \ No newline at end of file +- First release diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index b89b72182..30bb80877 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -1,10 +1,41 @@ -use crate::{ErrorType, Write}; +use crate::{Error, ErrorKind, ErrorType, Write}; use core::mem; +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; + +/// Errors that could be returned by `Write` on `&mut [u8]`. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[non_exhaustive] +pub enum SliceWriteError { + /// The target slice was full and so could not receive any new data. + Full, +} + +impl Error for SliceWriteError { + fn kind(&self) -> ErrorKind { + match self { + SliceWriteError::Full => ErrorKind::WriteZero, + } + } +} + impl ErrorType for &mut [u8] { - type Error = core::convert::Infallible; + type Error = SliceWriteError; } +impl core::fmt::Display for SliceWriteError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{self:?}") + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for SliceWriteError {} + /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting /// its data. /// @@ -12,12 +43,14 @@ impl ErrorType for &mut [u8] { /// The slice will be empty when it has been completely overwritten. /// /// If the number of bytes to be written exceeds the size of the slice, write operations will -/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of -/// kind `ErrorKind::WriteZero`. +/// return short writes: ultimately, a `SliceWriteError::Full`. impl Write for &mut [u8] { #[inline] fn write(&mut self, buf: &[u8]) -> Result { let amt = core::cmp::min(buf.len(), self.len()); + if !buf.is_empty() && amt == 0 { + return Err(SliceWriteError::Full); + } let (a, b) = mem::take(self).split_at_mut(amt); a.copy_from_slice(&buf[..amt]); *self = b; diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 1422a7b4b..1b91f7a7c 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -61,7 +61,6 @@ impl From for SeekFrom { /// This is the `embedded-io` equivalent of [`std::io::ErrorKind`], except with the following changes: /// /// - `WouldBlock` is removed, since `embedded-io` traits are always blocking. See the [crate-level documentation](crate) for details. -/// - `WriteZero` is removed, since it is a separate variant in [`WriteAllError`] and [`WriteFmtError`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] @@ -117,6 +116,8 @@ pub enum ErrorKind { /// An operation could not be completed, because it failed /// to allocate enough memory. OutOfMemory, + /// An attempted write could not write any data. + WriteZero, } #[cfg(feature = "std")] @@ -248,19 +249,6 @@ impl From> for std::io::Error { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From> for std::io::Error { - fn from(err: WriteAllError) -> Self { - match err { - WriteAllError::WriteZero => { - std::io::Error::new(std::io::ErrorKind::WriteZero, "WriteZero".to_owned()) - } - WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{e:?}")), - } - } -} - impl fmt::Display for ReadExactError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self:?}") @@ -275,8 +263,6 @@ impl std::error::Error for ReadExactError {} #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum WriteFmtError { - /// [`Write::write`] wrote zero bytes - WriteZero, /// An error was encountered while formatting. FmtError, /// Error returned by the inner Write. @@ -299,32 +285,6 @@ impl fmt::Display for WriteFmtError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for WriteFmtError {} -/// Error returned by [`Write::write_all`] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] -pub enum WriteAllError { - /// [`Write::write`] wrote zero bytes - WriteZero, - /// Error returned by the inner Write. - Other(E), -} - -impl From for WriteAllError { - fn from(err: E) -> Self { - Self::Other(err) - } -} - -impl fmt::Display for WriteAllError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:?}") - } -} - -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for WriteAllError {} - /// Blocking reader. /// /// This trait is the `embedded-io` equivalent of [`std::io::Read`]. @@ -401,11 +361,12 @@ pub trait Write: ErrorType { /// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be /// ready to accept more bytes immediately. /// - /// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not - /// able to accept more bytes and likely never will are better indicated with errors. + /// Implementations must not return `Ok(0)` unless `buf` is empty. Situations where the + /// writer is not able to accept more bytes must instead be indicated with an error, + /// where the `ErrorKind` is `WriteZero`. /// - /// If `buf.len() == 0`, `write` returns without blocking, with either `Ok(0)` or an error. - /// The `Ok(0)` doesn't indicate an error. + /// If `buf` is empty, `write` returns without blocking, with either `Ok(0)` or an error. + /// `Ok(0)` doesn't indicate an error. fn write(&mut self, buf: &[u8]) -> Result; /// Flush this output stream, blocking until all intermediately buffered contents reach their destination. @@ -419,12 +380,14 @@ pub trait Write: ErrorType { /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will /// not block, so this function may still block in subsequent calls. - fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError> { + /// + /// This function will panic if `write()` returns `Ok(0)`. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::Error> { while !buf.is_empty() { match self.write(buf) { - Ok(0) => return Err(WriteAllError::WriteZero), + Ok(0) => panic!("write() returned Ok(0)"), Ok(n) => buf = &buf[n..], - Err(e) => return Err(WriteAllError::Other(e)), + Err(e) => return Err(e), } } Ok(()) @@ -443,7 +406,7 @@ pub trait Write: ErrorType { // off I/O errors. instead of discarding them struct Adapter<'a, T: Write + ?Sized + 'a> { inner: &'a mut T, - error: Result<(), WriteAllError>, + error: Result<(), T::Error>, } impl fmt::Write for Adapter<'_, T> { @@ -466,10 +429,7 @@ pub trait Write: ErrorType { Ok(()) => Ok(()), Err(..) => match output.error { // check if the error came from the underlying `Write` or not - Err(e) => match e { - WriteAllError::WriteZero => Err(WriteFmtError::WriteZero), - WriteAllError::Other(e) => Err(WriteFmtError::Other(e)), - }, + Err(e) => Err(WriteFmtError::Other(e)), Ok(()) => Err(WriteFmtError::FmtError), }, } From 9d40f6b1423078d776a84928cc2dbc7521fbb947 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Thu, 14 Sep 2023 00:41:02 +0100 Subject: [PATCH 063/201] Update embedded-io-adapters to swap Ok(0) for Err when required --- embedded-io-adapters/CHANGELOG.md | 4 +++- embedded-io-adapters/src/futures_03.rs | 6 +++++- embedded-io-adapters/src/std.rs | 14 ++++++++++++-- embedded-io-adapters/src/tokio_1.rs | 6 +++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index bf9f2d391..107b9f98c 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -8,7 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Add support for adapting `BufRead` from `futures` and `tokio`. +- Return an error when a wrapped `std`/`futures`/`tokio` `write()` call returns + `Ok(0)` to comply with `embedded_io::Write` requirements. ## 0.5.0 - 2023-08-06 -- First release \ No newline at end of file +- First release diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index a3402cbb9..7fc95aa48 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -57,7 +57,11 @@ impl embedded_io_async::BufRead f impl embedded_io_async::Write for FromFutures { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + match poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await { + Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), + Ok(n) => Ok(n), + Err(e) => Err(e), + } } async fn flush(&mut self) -> Result<(), Self::Error> { diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index cd3df2d75..fe2248bcb 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -1,5 +1,7 @@ //! Adapters to/from `std::io` traits. +use embedded_io::Error; + /// Adapter from `std::io` traits. #[derive(Clone)] pub struct FromStd { @@ -52,7 +54,11 @@ impl embedded_io::BufRead for FromStd { impl embedded_io::Write for FromStd { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf) + match self.inner.write(buf) { + Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), + Ok(n) => Ok(n), + Err(e) => Err(e), + } } fn flush(&mut self) -> Result<(), Self::Error> { self.inner.flush() @@ -103,7 +109,11 @@ impl std::io::Read for ToStd { impl std::io::Write for ToStd { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf).map_err(to_std_error) + match self.inner.write(buf) { + Ok(n) => Ok(n), + Err(e) if e.kind() == embedded_io::ErrorKind::WriteZero => Ok(0), + Err(e) => Err(to_std_error(e)), + } } fn flush(&mut self) -> Result<(), std::io::Error> { self.inner.flush().map_err(to_std_error) diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 92060a4fe..9369efe07 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -68,7 +68,11 @@ impl embedded_io_async::BufRead for impl embedded_io_async::Write for FromTokio { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + match poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await { + Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), + Ok(n) => Ok(n), + Err(e) => Err(e), + } } async fn flush(&mut self) -> Result<(), Self::Error> { From 21abf1d9a26546c220e67c65f438170748ae0cee Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Mon, 18 Sep 2023 16:02:17 +0100 Subject: [PATCH 064/201] document write_fmt length trick for slice --- embedded-io/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 1b91f7a7c..6358d5286 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -401,6 +401,17 @@ pub trait Write: ErrorType { /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will /// not block, so this function may still block in subsequent calls. + /// + /// Unlike [`Write::write`], the number of bytes written is not returned. However, in the case of + /// writing to an `&mut [u8]` its possible to calculate the number of bytes written by subtracting + /// the length of the slice after the write, from the initial length of the slice. + /// + /// ```rust + /// # use embedded_io::Write; + /// let mut buf: &mut [u8] = &mut [0u8; 256]; + /// let start = buf.len(); + /// let len = write!(buf, "{}", "Test").and_then(|_| Ok(start - buf.len())); + /// ``` fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError> { // Create a shim which translates a Write to a fmt::Write and saves // off I/O errors. instead of discarding them From e632f161698a6f5dea3082669d882f6b70c45dcf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Oct 2023 15:29:20 +0200 Subject: [PATCH 065/201] Release embedded-io{,-async,-adapters} v0.6.0 --- embedded-io-adapters/CHANGELOG.md | 4 ++++ embedded-io-adapters/Cargo.toml | 6 +++--- embedded-io-async/CHANGELOG.md | 16 +++++++++++++++- embedded-io-async/Cargo.toml | 4 ++-- embedded-io/CHANGELOG.md | 2 +- embedded-io/Cargo.toml | 2 +- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index 107b9f98c..a81afe1a9 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +Add unreleased changes here + +## 0.6.0 - 2023-10-02 + - Add support for adapting `BufRead` from `futures` and `tokio`. - Return an error when a wrapped `std`/`futures`/`tokio` `write()` call returns `Ok(0)` to comply with `embedded_io::Write` requirements. diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index a849b1f12..4734b840f 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.5.0" +version = "0.6.0" edition = "2021" description = "Adapters between the `embedded-io` traits and other I/O traits" repository = "https://github.com/rust-embedded/embedded-hal" @@ -17,8 +17,8 @@ tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std" futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"] [dependencies] -embedded-io = { version = "0.5", path = "../embedded-io" } -embedded-io-async = { version = "0.5", path = "../embedded-io-async", optional = true } +embedded-io = { version = "0.6", path = "../embedded-io" } +embedded-io-async = { version = "0.6", path = "../embedded-io-async", optional = true } futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md index 978a92d8b..94648ee7e 100644 --- a/embedded-io-async/CHANGELOG.md +++ b/embedded-io-async/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +Add unreleased changes here + +## 0.6.0 - 2023-10-02 + +- Prohibit `Write::write` implementations returning `Ok(0)` unless there is no + data to write; consequently remove `WriteAllError`. + Update the `&mut [u8]` impl to possibly return + a new `SliceWriteError` if the slice is full instead of `Ok(0)`. +- Add `WriteZero` variant to `ErrorKind` for implementations that previously + may have returned `Ok(0)` to indicate no further data could be written. +- `Write::write_all` now panics if the `write()` implementation returns `Ok(0)`. + ## 0.5.0 - 2023-08-06 -- First release \ No newline at end of file +- First release diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 011a65589..fc704646b 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-async" -version = "0.5.0" +version = "0.6.0" edition = "2021" description = "Async embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" @@ -17,7 +17,7 @@ alloc = ["embedded-io/alloc"] defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] [dependencies] -embedded-io = { version = "0.5", path = "../embedded-io" } +embedded-io = { version = "0.6", path = "../embedded-io" } defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index f5a60a3df..bed5fc16f 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.6.0 - 2023-10-02 - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no data to write; consequently remove `WriteAllError` and the `WriteAllError` diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index 696823e7b..d96ff0b47 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io" -version = "0.5.0" +version = "0.6.0" edition = "2021" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" From a4dcba4cde473c681c7756748a999d04632eba33 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Oct 2023 15:31:54 +0200 Subject: [PATCH 066/201] Add allow(stable_features). Needed to pass deny-warnings on CI without breaking older nightlies. --- embedded-hal-async/src/lib.rs | 5 +++++ embedded-hal-bus/src/lib.rs | 5 +++++ embedded-io-adapters/src/lib.rs | 12 ++++++++++-- embedded-io-async/src/lib.rs | 7 ++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 5fe9168e7..e724207b7 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -1,6 +1,11 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![allow(stable_features)] #![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 6feef548b..501534947 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -2,6 +2,11 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![cfg_attr(feature = "async", allow(stable_features))] #![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index d11316405..533acc1a7 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -1,11 +1,19 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![cfg_attr( + any(feature = "tokio-1", feature = "futures-03"), + allow(stable_features) +)] #![cfg_attr( any(feature = "tokio-1", feature = "futures-03"), feature(async_fn_in_trait, impl_trait_projections) )] -#![warn(missing_docs)] -#![doc = include_str!("../README.md")] #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index b38d13abf..a726147d9 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -1,8 +1,13 @@ -#![feature(async_fn_in_trait, impl_trait_projections)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![allow(stable_features)] +#![feature(async_fn_in_trait, impl_trait_projections)] #[cfg(feature = "alloc")] extern crate alloc; From 739fa02ebe38e9cb29f0c3c9c05ad93dde49c57d Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 15 Oct 2023 23:11:22 +0000 Subject: [PATCH 067/201] async: disable `Send` lint in async traits Adds `#[allow(async_fn_in_trait)]` to disable the lint in `async` trait functions recommending `Send` in the return type. Adding the type annotation is currently unstable, has little-to-no utility in current crates using `embedded-hal`, and will break those users. See for details and discussion. --- embedded-hal-async/src/lib.rs | 2 ++ embedded-io-async/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index e724207b7..cc3060708 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -6,6 +6,8 @@ // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. #![allow(stable_features)] +#![allow(unknown_lints)] +#![allow(async_fn_in_trait)] #![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index a726147d9..2970aa82f 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -7,6 +7,8 @@ // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. #![allow(stable_features)] +#![allow(unknown_lints)] +#![allow(async_fn_in_trait)] #![feature(async_fn_in_trait, impl_trait_projections)] #[cfg(feature = "alloc")] From fb30c9b7475610379c807b3d5e6083ced652c35e Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 15 Oct 2023 23:42:37 +0000 Subject: [PATCH 068/201] ci: update nightly versions for clippy and rustdoc --- .github/workflows/clippy.yml | 4 ++-- .github/workflows/rustdoc.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 1479e4a56..dcea25c5e 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -15,6 +15,6 @@ jobs: with: # embedded-hal-async needs nightly. # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) - toolchain: nightly-2023-07-03 + toolchain: nightly-2023-10-14 components: clippy - - run: cargo clippy --all-features -- --deny=warnings \ No newline at end of file + - run: cargo clippy --all-features -- --deny=warnings diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index e0202bac8..cef117665 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -13,5 +13,5 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2023-07-03 - - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features \ No newline at end of file + toolchain: nightly-2023-10-14 + - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features From b2c1c0de1d97ce6f2a756c892b75c5d7e56c01e7 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Thu, 12 Oct 2023 22:03:14 +0100 Subject: [PATCH 069/201] Make SliceWriteError publicly accessible --- embedded-io/src/impls/slice_mut.rs | 15 +-------------- embedded-io/src/lib.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index 30bb80877..4608ee45b 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -1,19 +1,6 @@ -use crate::{Error, ErrorKind, ErrorType, Write}; +use crate::{Error, ErrorKind, ErrorType, SliceWriteError, Write}; use core::mem; -// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. -#[cfg(feature = "defmt-03")] -use defmt_03 as defmt; - -/// Errors that could be returned by `Write` on `&mut [u8]`. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] -#[non_exhaustive] -pub enum SliceWriteError { - /// The target slice was full and so could not receive any new data. - Full, -} - impl Error for SliceWriteError { fn kind(&self) -> ErrorKind { match self { diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 6358d5286..9f95e636a 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -259,6 +259,15 @@ impl fmt::Display for ReadExactError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for ReadExactError {} +/// Errors that could be returned by `Write` on `&mut [u8]`. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[non_exhaustive] +pub enum SliceWriteError { + /// The target slice was full and so could not receive any new data. + Full, +} + /// Error returned by [`Write::write_fmt`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] From 7414e2129b3d753cdd5b7a664c81bef353f8430e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Vi=C3=B6l?= Date: Sat, 26 Aug 2023 09:20:24 +0200 Subject: [PATCH 070/201] Use rust-version field --- embedded-can/Cargo.toml | 1 + embedded-hal-bus/Cargo.toml | 1 + embedded-hal-nb/Cargo.toml | 1 + embedded-hal/Cargo.toml | 1 + embedded-io-adapters/Cargo.toml | 1 + embedded-io/Cargo.toml | 1 + 6 files changed, 6 insertions(+) diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 22620d786..7f8903204 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-can" version = "0.4.1" edition = "2021" +rust-version = "1.60" description = "HAL traits for Controller Area Network (CAN) devices." categories = ["embedded", "hardware-support", "no-std"] diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index bfd2a754d..ab69c254f 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -6,6 +6,7 @@ categories = ["embedded", "hardware-support", "no-std"] description = "Bus/Device connection mechanisms for embedded-hal, a Hardware Abstraction Layer (HAL) for embedded systems" documentation = "https://docs.rs/embedded-hal-bus" edition = "2021" +rust-version = "1.60" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal-bus" diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 66e941923..d7319d558 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-hal-nb" version = "1.0.0-rc.1" edition = "2021" +rust-version = "1.60" categories = ["embedded", "hardware-support", "no-std"] description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 396cf972b..6fa3df6cb 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,6 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" +rust-version = "1.60" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 4734b840f..8cf97eceb 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-io-adapters" version = "0.6.0" edition = "2021" +rust-version = "1.60" description = "Adapters between the `embedded-io` traits and other I/O traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index d96ff0b47..8db36ea55 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-io" version = "0.6.0" edition = "2021" +rust-version = "1.60" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" From 689408b255b9ad6710c255fa6a2c340541545937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Vi=C3=B6l?= Date: Sat, 21 Oct 2023 10:13:08 +0200 Subject: [PATCH 071/201] Reduce MSRV --- embedded-can/Cargo.toml | 2 +- embedded-hal-nb/Cargo.toml | 2 +- embedded-hal/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 7f8903204..6279fa80d 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-can" version = "0.4.1" edition = "2021" -rust-version = "1.60" +rust-version = "1.56" description = "HAL traits for Controller Area Network (CAN) devices." categories = ["embedded", "hardware-support", "no-std"] diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index d7319d558..7e5e3c5a8 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-hal-nb" version = "1.0.0-rc.1" edition = "2021" -rust-version = "1.60" +rust-version = "1.56" categories = ["embedded", "hardware-support", "no-std"] description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 6fa3df6cb..58d70fc10 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" -rust-version = "1.60" +rust-version = "1.56" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" From 6990b070ca5584e08b2651fa6d548e0efb95838c Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 22 Oct 2023 17:26:13 +0100 Subject: [PATCH 072/201] prepare v0.6.1 --- embedded-io/CHANGELOG.md | 4 ++++ embedded-io/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index bed5fc16f..286d1b707 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.6.1 - 2023-10-22 + +- Make `SliceWriteError` publicly available. + ## 0.6.0 - 2023-10-02 - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index d96ff0b47..448b752f2 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io" -version = "0.6.0" +version = "0.6.1" edition = "2021" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" From ff1eb9cb756bc7bd910505b3501ebbc991d3847b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 22 Oct 2023 21:47:48 +0200 Subject: [PATCH 073/201] io-async: make slice write impl consistent with blocking impl. --- embedded-io-async/src/impls/slice_mut.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embedded-io-async/src/impls/slice_mut.rs b/embedded-io-async/src/impls/slice_mut.rs index 4a195e3f4..bd64d1320 100644 --- a/embedded-io-async/src/impls/slice_mut.rs +++ b/embedded-io-async/src/impls/slice_mut.rs @@ -1,5 +1,7 @@ -use crate::Write; use core::mem; +use embedded_io::SliceWriteError; + +use crate::Write; /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting /// its data. @@ -14,6 +16,9 @@ impl Write for &mut [u8] { #[inline] async fn write(&mut self, buf: &[u8]) -> Result { let amt = core::cmp::min(buf.len(), self.len()); + if !buf.is_empty() && amt == 0 { + return Err(SliceWriteError::Full); + } let (a, b) = mem::take(self).split_at_mut(amt); a.copy_from_slice(&buf[..amt]); *self = b; From 1f9da539b462c5b50c79967fd6abb80b9a20c533 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 6 Nov 2023 08:57:39 +0100 Subject: [PATCH 074/201] Handle reading from Tokio with empty buf as a special case --- embedded-io-adapters/src/tokio_1.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 9369efe07..263229c5f 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -42,6 +42,14 @@ impl embedded_io::ErrorType for FromTokio { impl embedded_io_async::Read for FromTokio { async fn read(&mut self, buf: &mut [u8]) -> Result { + // The current tokio implementation (https://github.com/tokio-rs/tokio/blob/tokio-1.33.0/tokio/src/io/poll_evented.rs#L165) + // does not consider the case of buf.is_empty() as a special case, + // which can cause Poll::Pending to be returned at the end of the stream when called with an empty buffer. + // This poll will, however, never become ready, as no more bytes will be received. + if buf.is_empty() { + return Ok(0); + } + poll_fn(|cx| { let mut buf = tokio::io::ReadBuf::new(buf); match Pin::new(&mut self.inner).poll_read(cx, &mut buf) { From ab46bcece0ce01b243ccc4d7b84ea4f0a7a11f5a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 6 Nov 2023 13:52:45 +0100 Subject: [PATCH 075/201] Update CHANGELOG.md --- embedded-io-adapters/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index a81afe1a9..e9205d681 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Add unreleased changes here +- Handle reading from `FromTokio` with empty buffer, ensuring `Ok(0)` is always returned. + ## 0.6.0 - 2023-10-02 - Add support for adapting `BufRead` from `futures` and `tokio`. From b1d84ed3d79e9ddbdd826d64436ed54544f4fac2 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 22 Nov 2023 17:07:22 -0500 Subject: [PATCH 076/201] Make Cargo feature explicit To keep consistent with all other crates, and for clarity, it is best to explicitly declare `defmt-03` as a feature of the `embedded-hal` crate. This way people can look at the `[features]` section rather than search through any optional dependencies. In this case it's not that big of a deal, but it keeps the pattern. --- embedded-hal/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 396cf972b..b67a46680 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -15,5 +15,8 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "1.0.0-rc.1" +[features] +defmt-03 = ["dep:defmt-03"] + [dependencies] defmt-03 = { package = "defmt", version = "0.3", optional = true } From 7fa580567a6b4f5ba907c0ea20bc7d36db0134d2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 25 Nov 2023 00:26:06 +0100 Subject: [PATCH 077/201] Workaround https://github.com/tokio-rs/tokio/issues/6165 --- .github/workflows/rustdoc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index cef117665..c4b46425e 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -14,4 +14,5 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: nightly-2023-10-14 - - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features + # tokio/net required to workaround https://github.com/tokio-rs/tokio/issues/6165 + - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features --features tokio/net From a7eb603bc4c2c4bfab975a5b46312c97745afb0c Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 22 Nov 2023 18:09:00 -0500 Subject: [PATCH 078/201] A few minor clippy nits * semicolons * doc links * unused Error namespace --- embedded-hal-bus/src/spi/mutex.rs | 4 ++-- embedded-io-adapters/src/futures_03.rs | 2 +- embedded-io-adapters/src/std.rs | 6 +++--- embedded-io-adapters/src/tokio_1.rs | 2 +- embedded-io-async/src/impls/boxx.rs | 2 +- embedded-io/src/impls/boxx.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index d1288b995..a5f67f9e0 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -21,7 +21,7 @@ pub struct MutexDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { - /// Create a new MutexDevice. + /// Create a new [`MutexDevice`]. #[inline] pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -29,7 +29,7 @@ impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { - /// Create a new MutexDevice without support for in-transaction delays. + /// Create a new [`MutexDevice`] without support for in-transaction delays. /// /// # Panics /// diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index 7fc95aa48..a40370a24 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -51,7 +51,7 @@ impl embedded_io_async::BufRead f } fn consume(&mut self, amt: usize) { - Pin::new(&mut self.inner).consume(amt) + Pin::new(&mut self.inner).consume(amt); } } diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index fe2248bcb..90a2f8525 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -1,6 +1,6 @@ //! Adapters to/from `std::io` traits. -use embedded_io::Error; +use embedded_io::Error as _; /// Adapter from `std::io` traits. #[derive(Clone)] @@ -48,7 +48,7 @@ impl embedded_io::BufRead for FromStd { } fn consume(&mut self, amt: usize) { - self.inner.consume(amt) + self.inner.consume(amt); } } @@ -126,7 +126,7 @@ impl std::io::Seek for ToStd { } } -/// Convert a embedded-io error to a std::io::Error +/// Convert a embedded-io error to a [`std::io::Error`] pub fn to_std_error(err: T) -> std::io::Error { std::io::Error::new(err.kind().into(), format!("{err:?}")) } diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 263229c5f..418284b93 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -70,7 +70,7 @@ impl embedded_io_async::BufRead for } fn consume(&mut self, amt: usize) { - Pin::new(&mut self.inner).consume(amt) + Pin::new(&mut self.inner).consume(amt); } } diff --git a/embedded-io-async/src/impls/boxx.rs b/embedded-io-async/src/impls/boxx.rs index 677bf328d..d6186b0c7 100644 --- a/embedded-io-async/src/impls/boxx.rs +++ b/embedded-io-async/src/impls/boxx.rs @@ -18,7 +18,7 @@ impl BufRead for Box { #[inline] fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs index 6e61ebf8d..037a6be0c 100644 --- a/embedded-io/src/impls/boxx.rs +++ b/embedded-io/src/impls/boxx.rs @@ -21,7 +21,7 @@ impl BufRead for Box { } fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } From b39aaed103f3829acee598b875569f622805b770 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 22 Nov 2023 17:55:06 -0500 Subject: [PATCH 079/201] Consolidate identical SPI transaction impl Consolidate three identical implementations of the `SpiDevice::transaction`. Fewer lines of code, faster compilation, fewer bugs... --- embedded-hal-bus/src/spi/critical_section.rs | 25 +---------- embedded-hal-bus/src/spi/mod.rs | 2 + embedded-hal-bus/src/spi/mutex.rs | 25 +---------- embedded-hal-bus/src/spi/refcell.rs | 25 +---------- embedded-hal-bus/src/spi/shared.rs | 44 ++++++++++++++++++++ 5 files changed, 52 insertions(+), 69 deletions(-) create mode 100644 embedded-hal-bus/src/spi/shared.rs diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 5bad960af..4334cf1fe 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -5,6 +5,7 @@ use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; +use crate::spi::shared::transaction; /// `critical-section`-based shared bus [`SpiDevice`] implementation. /// @@ -66,29 +67,7 @@ where critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => bus.read(buf), - Operation::Write(buf) => bus.write(buf), - Operation::Transfer(read, write) => bus.transfer(read, write), - Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, bus, &mut self.delay, &mut self.cs) }) } } diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 090f44649..8a5f2b83f 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -12,6 +12,8 @@ mod mutex; #[cfg(feature = "std")] pub use mutex::*; mod critical_section; +mod shared; + pub use self::critical_section::*; #[cfg(feature = "defmt-03")] diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index d1288b995..059fae26e 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -4,6 +4,7 @@ use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use std::sync::Mutex; use super::DeviceError; +use crate::spi::shared::transaction; /// `std` `Mutex`-based shared bus [`SpiDevice`] implementation. /// @@ -63,28 +64,6 @@ where fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => bus.read(buf), - Operation::Write(buf) => bus.write(buf), - Operation::Transfer(read, write) => bus.transfer(read, write), - Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, bus, &mut self.delay, &mut self.cs) } } diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index d407620be..414f4d12e 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -4,6 +4,7 @@ use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; +use crate::spi::shared::transaction; /// `RefCell`-based shared bus [`SpiDevice`] implementation. /// @@ -62,28 +63,6 @@ where fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => bus.read(buf), - Operation::Write(buf) => bus.write(buf), - Operation::Transfer(read, write) => bus.transfer(read, write), - Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, bus, &mut self.delay, &mut self.cs) } } diff --git a/embedded-hal-bus/src/spi/shared.rs b/embedded-hal-bus/src/spi/shared.rs new file mode 100644 index 000000000..233169ced --- /dev/null +++ b/embedded-hal-bus/src/spi/shared.rs @@ -0,0 +1,44 @@ +use embedded_hal::delay::DelayUs; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ErrorType, Operation, SpiBus}; + +use crate::spi::DeviceError; + +/// Common implementation to perform a transaction against the device. +#[inline] +pub fn transaction( + operations: &mut [Operation], + bus: &mut BUS, + delay: &mut D, + cs: &mut CS, +) -> Result<(), DeviceError> +where + BUS: SpiBus + ErrorType, + CS: OutputPin, + D: DelayUs, + Word: Copy, +{ + cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + delay.delay_us(*us); + Ok(()) + } + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) +} From 3471d21ca74b046843031dbed9297d62b42e9e5a Mon Sep 17 00:00:00 2001 From: Anton Patrushev Date: Sun, 26 Nov 2023 23:55:15 +0400 Subject: [PATCH 080/201] fix embedded-io-async link in readme --- embedded-hal-async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index c53790751..2f739993a 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -12,7 +12,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Serial/UART traits -There is no serial traits in `embedded-hal-async`. Instead, use [`embedded-io-async`](https://crates.io/crates/embedded-io). +There is no serial traits in `embedded-hal-async`. Instead, use [`embedded-io-async`](https://crates.io/crates/embedded-io-async). A serial port is essentially a byte-oriented stream, and that's what `embedded-io-async` models. Sharing the traits with all byte streams has some advantages. For example, it allows generic code providing a command-line interface or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. From 8ce43e5a3abcc3b649cba2f2625322b7ba80ba2b Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 23 Nov 2023 11:14:18 -0500 Subject: [PATCH 081/201] Make downcasting safer --- embedded-hal/src/pwm.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index e97a93548..d84abbcb4 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -95,8 +95,14 @@ pub trait SetDutyCycle: ErrorType { /// and that `denom` is not zero. #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + debug_assert!(num <= denom); let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); - self.set_duty_cycle(duty as u16) + + // This is safe because we know that `num <= denom`, so `duty <= self.get_max_duty_cycle()` (u16) + #[allow(clippy::cast_possible_truncation)] + { + self.set_duty_cycle(duty as u16) + } } /// Set the duty cycle to `percent / 100` From 6eeb8c4c6b768280cafe7c050f867b305cce8ee6 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 27 Nov 2023 13:24:43 -0500 Subject: [PATCH 082/201] Ensure denum is not zero in debug build --- embedded-hal/src/pwm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index d84abbcb4..bf06e52a5 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -95,6 +95,7 @@ pub trait SetDutyCycle: ErrorType { /// and that `denom` is not zero. #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + debug_assert!(denom != 0); debug_assert!(num <= denom); let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); From c9fbac0d80a9aa69ceb4c927fb498682b6120cbc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 19:35:14 +0100 Subject: [PATCH 083/201] Use `feature()` on nightly toolchains only. `feature()` is only allowed on Nightly, it's completely disallowed on stable and beta even for already-stabilized features. So, we autodetect whether the user is using nightly and conditionally use `feature()`. This allows the crates to Just Work on current 1.75 beta and will also Just Work when 1.75 stable is out. Keeping `feature()` is desirable to keep support for: - Espressif's xtensa rustc fork. (they build from the stable branch but enable use of `feature()`, so latest xtensa rustc still requires `feature()`) - Users of older nightlies Once xtensa rust 1.75 is out, we can remove this (upstream nightlies that require `feature()` will be quite old by then, so dropping support for them should be OK). I decided to not use already-made crates like `rustversion` to do this because they're quite big and do way more than what we need, so I felt badd adding another dep. The code is inspired from `rustversion`'s build.rs. --- embedded-hal-async/build.rs | 18 ++++++++++++++++++ embedded-hal-async/src/lib.rs | 5 ++--- embedded-hal-bus/build.rs | 18 ++++++++++++++++++ embedded-hal-bus/src/lib.rs | 7 +++++-- embedded-io-adapters/build.rs | 18 ++++++++++++++++++ embedded-io-adapters/src/lib.rs | 4 ++-- embedded-io-async/build.rs | 18 ++++++++++++++++++ embedded-io-async/src/lib.rs | 5 ++--- 8 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 embedded-hal-async/build.rs create mode 100644 embedded-hal-bus/build.rs create mode 100644 embedded-io-adapters/build.rs create mode 100644 embedded-io-async/build.rs diff --git a/embedded-hal-async/build.rs b/embedded-hal-async/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-hal-async/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index cc3060708..44901deca 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -5,10 +5,9 @@ // Needed to pass CI, because we deny warnings. // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. -#![allow(stable_features)] -#![allow(unknown_lints)] +#![cfg_attr(nightly, allow(stable_features, unknown_lints))] +#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] -#![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; pub mod digital; diff --git a/embedded-hal-bus/build.rs b/embedded-hal-bus/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-hal-bus/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 501534947..e7e75ec76 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -6,8 +6,11 @@ // Needed to pass CI, because we deny warnings. // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. -#![cfg_attr(feature = "async", allow(stable_features))] -#![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] +#![cfg_attr(all(feature = "async", nightly), allow(stable_features))] +#![cfg_attr( + all(feature = "async", nightly), + feature(async_fn_in_trait, impl_trait_projections) +)] // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. #[cfg(feature = "defmt-03")] diff --git a/embedded-io-adapters/build.rs b/embedded-io-adapters/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-io-adapters/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index 533acc1a7..f9d0f1a5c 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -7,11 +7,11 @@ // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. #![cfg_attr( - any(feature = "tokio-1", feature = "futures-03"), + all(any(feature = "tokio-1", feature = "futures-03"), nightly), allow(stable_features) )] #![cfg_attr( - any(feature = "tokio-1", feature = "futures-03"), + all(any(feature = "tokio-1", feature = "futures-03"), nightly), feature(async_fn_in_trait, impl_trait_projections) )] diff --git a/embedded-io-async/build.rs b/embedded-io-async/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-io-async/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 2970aa82f..1171d42a0 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -6,10 +6,9 @@ // Needed to pass CI, because we deny warnings. // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. -#![allow(stable_features)] -#![allow(unknown_lints)] +#![cfg_attr(nightly, allow(stable_features, unknown_lints))] +#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] -#![feature(async_fn_in_trait, impl_trait_projections)] #[cfg(feature = "alloc")] extern crate alloc; From 2af28b46fe5e9bd6cac8167013a0b520b82cbec1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 21:07:02 +0100 Subject: [PATCH 084/201] pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. --- embedded-hal/CHANGELOG.md | 1 + embedded-hal/src/pwm.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 019080d5c..0b126ff95 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Minor document fixes. - Add #[inline] hints to most of `embedded-hal` functions. +- pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index bf06e52a5..782c45fff 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -69,7 +69,7 @@ pub trait SetDutyCycle: ErrorType { /// Get the maximum duty cycle value. /// /// This value corresponds to a 100% duty cycle. - fn get_max_duty_cycle(&self) -> u16; + fn max_duty_cycle(&self) -> u16; /// Set the duty cycle to `duty / max_duty`. /// @@ -86,7 +86,7 @@ pub trait SetDutyCycle: ErrorType { /// Set the duty cycle to 100%, or always active. #[inline] fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { - self.set_duty_cycle(self.get_max_duty_cycle()) + self.set_duty_cycle(self.max_duty_cycle()) } /// Set the duty cycle to `num / denom`. @@ -97,9 +97,9 @@ pub trait SetDutyCycle: ErrorType { fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { debug_assert!(denom != 0); debug_assert!(num <= denom); - let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); + let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); - // This is safe because we know that `num <= denom`, so `duty <= self.get_max_duty_cycle()` (u16) + // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) #[allow(clippy::cast_possible_truncation)] { self.set_duty_cycle(duty as u16) @@ -117,8 +117,8 @@ pub trait SetDutyCycle: ErrorType { impl SetDutyCycle for &mut T { #[inline] - fn get_max_duty_cycle(&self) -> u16 { - T::get_max_duty_cycle(self) + fn max_duty_cycle(&self) -> u16 { + T::max_duty_cycle(self) } #[inline] From 4ec58c85d81a191a0efddcf7269d0a4c753b2b45 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 21:31:22 +0100 Subject: [PATCH 085/201] Rename `DelayUs` to `DelayNs`, add `delay_ns`. --- embedded-hal-async/CHANGELOG.md | 3 ++ embedded-hal-async/src/delay.rs | 34 ++++++++++++++++---- embedded-hal-bus/src/spi/critical_section.rs | 4 +-- embedded-hal-bus/src/spi/exclusive.rs | 6 ++-- embedded-hal-bus/src/spi/mod.rs | 13 +++----- embedded-hal-bus/src/spi/mutex.rs | 4 +-- embedded-hal-bus/src/spi/refcell.rs | 4 +-- embedded-hal-bus/src/spi/shared.rs | 4 +-- embedded-hal/CHANGELOG.md | 4 +++ embedded-hal/src/delay.rs | 33 ++++++++++++++----- 10 files changed, 75 insertions(+), 34 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index c94153a3e..66e660182 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-async` functions. +- delay: Rename `DelayUs` to `DelayNs` +- delay: Add `DelayNs::delay_ns()` +- delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index 62bd742f2..87c540d2d 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -1,20 +1,42 @@ //! Delays. -/// Microsecond delay. -pub trait DelayUs { +/// Delay with up to nanosecond precision. +pub trait DelayNs { + /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer + /// if the implementation requires it due to precision/timing issues. + async fn delay_ns(&mut self, ns: u32); + /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. - async fn delay_us(&mut self, us: u32); + async fn delay_us(&mut self, mut us: u32) { + while us > 4_294_967 { + us -= 4_294_967; + self.delay_ns(4_294_967_000).await; + } + self.delay_ns(us * 1_000).await; + } /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. - async fn delay_ms(&mut self, ms: u32); + #[inline] + async fn delay_ms(&mut self, mut ms: u32) { + while ms > 4294 { + ms -= 4294; + self.delay_ns(4_294_000_000).await; + } + self.delay_ns(ms * 1_000_000).await; + } } -impl DelayUs for &mut T +impl DelayNs for &mut T where - T: DelayUs + ?Sized, + T: DelayNs + ?Sized, { + #[inline] + async fn delay_ns(&mut self, ns: u32) { + T::delay_ns(self, ns).await; + } + #[inline] async fn delay_us(&mut self, us: u32) { T::delay_us(self, us).await; diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 4334cf1fe..0b37c3ed9 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use critical_section::Mutex; -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -60,7 +60,7 @@ impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for CriticalSectionDe where BUS: SpiBus, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 1642269e7..fcc8c6114 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -1,11 +1,11 @@ //! SPI bus sharing mechanisms. -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; #[cfg(feature = "async")] use embedded_hal_async::{ - delay::DelayUs as AsyncDelayUs, + delay::DelayNs as AsyncDelayUs, spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, }; @@ -70,7 +70,7 @@ impl SpiDevice for ExclusiveDevice, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 8a5f2b83f..15c9e7046 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -53,23 +53,18 @@ fn no_delay_panic() { panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation."); } -impl embedded_hal::delay::DelayUs for NoDelay { +impl embedded_hal::delay::DelayNs for NoDelay { #[inline] - fn delay_us(&mut self, _us: u32) { + fn delay_ns(&mut self, _ns: u32) { no_delay_panic(); } } #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -impl embedded_hal_async::delay::DelayUs for NoDelay { +impl embedded_hal_async::delay::DelayNs for NoDelay { #[inline] - async fn delay_us(&mut self, _us: u32) { - no_delay_panic(); - } - - #[inline] - async fn delay_ms(&mut self, _ms: u32) { + async fn delay_ns(&mut self, _ns: u32) { no_delay_panic(); } } diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 69a0822e7..449a449b3 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -1,4 +1,4 @@ -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use std::sync::Mutex; @@ -58,7 +58,7 @@ impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for MutexDevice<'a, B where BUS: SpiBus, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 414f4d12e..3c8b465fe 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -57,7 +57,7 @@ impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for RefCellDevice<'a, where BUS: SpiBus, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/shared.rs b/embedded-hal-bus/src/spi/shared.rs index 233169ced..bc0a70d56 100644 --- a/embedded-hal-bus/src/spi/shared.rs +++ b/embedded-hal-bus/src/spi/shared.rs @@ -1,4 +1,4 @@ -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus}; @@ -15,7 +15,7 @@ pub fn transaction( where BUS: SpiBus + ErrorType, CS: OutputPin, - D: DelayUs, + D: DelayNs, Word: Copy, { cs.set_low().map_err(DeviceError::Cs)?; diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 0b126ff95..0f171b32c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Minor document fixes. - Add #[inline] hints to most of `embedded-hal` functions. - pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. +- delay: Rename `DelayUs` to `DelayNs` +- delay: Add `DelayNs::delay_ns()` +- delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. +- delay: Make the default impl of `delay_ms` more efficient, it now does less calls to the underlying `delay_ns` (previously `delay_us`). ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 77b5633d7..8a1fc3c9d 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -1,25 +1,42 @@ //! Delays. -/// Microsecond delay. -pub trait DelayUs { +/// Delay with up to nanosecond precision. +pub trait DelayNs { + /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer + /// if the implementation requires it due to precision/timing issues. + fn delay_ns(&mut self, ns: u32); + /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. - fn delay_us(&mut self, us: u32); + fn delay_us(&mut self, mut us: u32) { + while us > 4_294_967 { + us -= 4_294_967; + self.delay_ns(4_294_967_000); + } + self.delay_ns(us * 1_000); + } /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. #[inline] - fn delay_ms(&mut self, ms: u32) { - for _ in 0..ms { - self.delay_us(1000); + fn delay_ms(&mut self, mut ms: u32) { + while ms > 4294 { + ms -= 4294; + self.delay_ns(4_294_000_000); } + self.delay_ns(ms * 1_000_000); } } -impl DelayUs for &mut T +impl DelayNs for &mut T where - T: DelayUs + ?Sized, + T: DelayNs + ?Sized, { + #[inline] + fn delay_ns(&mut self, ns: u32) { + T::delay_ns(self, ns); + } + #[inline] fn delay_us(&mut self, us: u32) { T::delay_us(self, us); From 493dfd82cb3113d13d994286b833f4dd085558c3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 21:34:23 +0100 Subject: [PATCH 086/201] bus: use shared `fn transaction` for exclusive bus too. --- embedded-hal-bus/src/spi/exclusive.rs | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index fcc8c6114..e83551eba 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -74,29 +74,7 @@ where { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => self.bus.read(buf), - Operation::Write(buf) => self.bus.write(buf), - Operation::Transfer(read, write) => self.bus.transfer(read, write), - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - self.bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, &mut self.bus, &mut self.delay, &mut self.cs) } } From c2eab4d4bc76349dca736a5b14bcb8148c732b1d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 22:47:27 +0100 Subject: [PATCH 087/201] spi: make `Operation::DelayNs` with nanosecond granularity. --- embedded-hal-async/CHANGELOG.md | 1 + embedded-hal-bus/src/spi/critical_section.rs | 2 +- embedded-hal-bus/src/spi/exclusive.rs | 11 ++++++----- embedded-hal-bus/src/spi/mod.rs | 4 ++-- embedded-hal-bus/src/spi/mutex.rs | 2 +- embedded-hal-bus/src/spi/refcell.rs | 2 +- embedded-hal-bus/src/spi/shared.rs | 4 ++-- embedded-hal/CHANGELOG.md | 1 + embedded-hal/src/spi.rs | 4 ++-- 9 files changed, 17 insertions(+), 14 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 66e660182..f692512c3 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - delay: Rename `DelayUs` to `DelayNs` - delay: Add `DelayNs::delay_ns()` - delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. +- spi: Rename `Operation::DelayUs` to `Operation::DelayNs`, with nanosecond precision. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 0b37c3ed9..7c87eeaaa 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -37,7 +37,7 @@ impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type [`Operation::DelayUs`]. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index e83551eba..3717844c1 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -5,10 +5,11 @@ use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; #[cfg(feature = "async")] use embedded_hal_async::{ - delay::DelayNs as AsyncDelayUs, + delay::DelayNs as AsyncDelayNs, spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, }; +use super::shared::transaction; use super::DeviceError; /// [`SpiDevice`] implementation with exclusive access to the bus (not shared). @@ -47,7 +48,7 @@ impl ExclusiveDevice { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: BUS, cs: CS) -> Self { Self { @@ -84,7 +85,7 @@ impl AsyncSpiDevice for ExclusiveDevice< where BUS: AsyncSpiBus, CS: OutputPin, - D: AsyncDelayUs, + D: AsyncDelayNs, { #[inline] async fn transaction( @@ -100,10 +101,10 @@ where Operation::Write(buf) => self.bus.write(buf).await, Operation::Transfer(read, write) => self.bus.transfer(read, write).await, Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, - Operation::DelayUs(us) => match self.bus.flush().await { + Operation::DelayNs(ns) => match self.bus.flush().await { Err(e) => Err(e), Ok(()) => { - self.delay.delay_us(*us).await; + self.delay.delay_ns(*ns).await; Ok(()) } }, diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 15c9e7046..8b51242d8 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -43,14 +43,14 @@ where } } -/// Dummy `DelayUs` implementation that panics on use. +/// Dummy [`DelayNs`](embedded_hal::delay::DelayNs) implementation that panics on use. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct NoDelay; #[cold] fn no_delay_panic() { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation."); + panic!("You've tried to execute a SPI transaction containing a `Operation::DelayNs` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayNs` implementation."); } impl embedded_hal::delay::DelayNs for NoDelay { diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 449a449b3..3167c72d3 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -35,7 +35,7 @@ impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 3c8b465fe..da8602624 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -34,7 +34,7 @@ impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/shared.rs b/embedded-hal-bus/src/spi/shared.rs index bc0a70d56..95730ba19 100644 --- a/embedded-hal-bus/src/spi/shared.rs +++ b/embedded-hal-bus/src/spi/shared.rs @@ -25,9 +25,9 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { + Operation::DelayNs(ns) => { bus.flush()?; - delay.delay_us(*us); + delay.delay_ns(*ns); Ok(()) } }); diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 0f171b32c..c207cfa0a 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - delay: Add `DelayNs::delay_ns()` - delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. - delay: Make the default impl of `delay_ms` more efficient, it now does less calls to the underlying `delay_ns` (previously `delay_us`). +- spi: Rename `Operation::DelayUs` to `Operation::DelayNs`, with nanosecond precision. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index c8c8224fd..ca61269e7 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -324,8 +324,8 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::transfer_in_place`]. TransferInPlace(&'a mut [Word]), - /// Delay for at least the specified number of microseconds. - DelayUs(u32), + /// Delay for at least the specified number of nanoseconds. + DelayNs(u32), } /// SPI device trait. From 2767cbdce1ab46d11e3d1281446b585aa1517cca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 29 Nov 2023 14:48:23 +0100 Subject: [PATCH 088/201] release embedded-hal-* v1.0.0-rc.2, embedded-io-{async,adapters} v0.6.1 --- embedded-hal-async/CHANGELOG.md | 9 ++++++++- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-bus/CHANGELOG.md | 9 ++++++++- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-nb/CHANGELOG.md | 9 ++++++++- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 7 ++++++- embedded-hal/Cargo.toml | 2 +- embedded-io-adapters/CHANGELOG.md | 3 +++ embedded-io-adapters/Cargo.toml | 4 ++-- embedded-io-async/CHANGELOG.md | 4 ++++ embedded-io-async/Cargo.toml | 2 +- 12 files changed, 48 insertions(+), 15 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index f692512c3..468c3b711 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,12 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + +- Updated `embedded-hal` to version `1.0.0-rc.2`. - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-async` functions. - delay: Rename `DelayUs` to `DelayNs` - delay: Add `DelayNs::delay_ns()` - delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. - spi: Rename `Operation::DelayUs` to `Operation::DelayNs`, with nanosecond precision. +- Use `feature()` on nightly toolchains only. This adds support for 1.75 beta and stable. ## [v1.0.0-rc.1] - 2023-08-15 @@ -76,7 +82,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...HEAD +[v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...embedded-hal-async-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 [v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 [v0.2.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...embedded-hal-async-v0.2.0-alpha.1 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index b10cfc734..231f00e2d 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" rust-version = "1.65.0" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 59b398d1a..6858e9c19 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,8 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + +- Updated `embedded-hal(-async)` to version `1.0.0-rc.2`. - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-bus` functions. +- Use `feature()` on nightly toolchains only. This adds async support for 1.75 beta and stable. ## [v0.1.0-rc.1] - 2023-08-15 @@ -42,7 +48,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...HEAD +[v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 [v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 [v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...embedded-hal-bus-v0.1.0-alpha.2 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index ab69c254f..937a49b2c 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-rc.1" +version = "0.1.0-rc.2" [features] std = [] @@ -20,8 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } -embedded-hal-async = { version = "=1.0.0-rc.1", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } +embedded-hal-async = { version = "=1.0.0-rc.2", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index f4a1e7944..11753a27f 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + +- Updated `embedded-hal` to version `1.0.0-rc.2`. - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-nb` functions. @@ -35,7 +40,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...HEAD +[v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.1...embedded-hal-nb-v1.0.0-rc.2 +[v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...embedded-hal-nb-v1.0.0-rc.1 [v1.0.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...embedded-hal-nb-v1.0.0-alpha.3 [v1.0.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...embedded-hal-nb-v1.0.0-alpha.2 [v1.0.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.0...embedded-hal-nb-v1.0.0-alpha.1 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 7e5e3c5a8..52c6755ea 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" edition = "2021" rust-version = "1.56" @@ -13,7 +13,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index c207cfa0a..a177ba7f1 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + - Minor document fixes. - Add #[inline] hints to most of `embedded-hal` functions. - pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. @@ -306,7 +310,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...HEAD +[v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 [v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 [v1.0.0-alpha.10]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...v1.0.0-alpha.10 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index fb549a7e7..a26ae3372 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" [features] defmt-03 = ["dep:defmt-03"] diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index e9205d681..b540b67b4 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -9,7 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Add unreleased changes here +## 0.6.1 - 2023-11-28 + - Handle reading from `FromTokio` with empty buffer, ensuring `Ok(0)` is always returned. +- Use `feature()` on nightly toolchains only. This adds async support for 1.75 beta and stable. ## 0.6.0 - 2023-10-02 diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 8cf97eceb..89721f1bb 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.6.0" +version = "0.6.1" edition = "2021" rust-version = "1.60" description = "Adapters between the `embedded-io` traits and other I/O traits" @@ -19,7 +19,7 @@ futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async? [dependencies] embedded-io = { version = "0.6", path = "../embedded-io" } -embedded-io-async = { version = "0.6", path = "../embedded-io-async", optional = true } +embedded-io-async = { version = "0.6.1", path = "../embedded-io-async", optional = true } futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md index 94648ee7e..5f421e351 100644 --- a/embedded-io-async/CHANGELOG.md +++ b/embedded-io-async/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Add unreleased changes here +## 0.6.1 - 2023-11-28 + +- Use `feature()` on nightly toolchains only. This adds support for 1.75 beta and stable. + ## 0.6.0 - 2023-10-02 - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index fc704646b..87891795c 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-async" -version = "0.6.0" +version = "0.6.1" edition = "2021" description = "Async embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" From ca8ef80477914652c1f442c39dd240400e838f3b Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 30 Nov 2023 14:05:05 +0100 Subject: [PATCH 089/201] Use named constants to clarify delay-stepdown behavior --- embedded-hal/src/delay.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 8a1fc3c9d..213c42491 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -1,5 +1,10 @@ //! Delays. +/// Nanoseconds per microsecond +const NANOS_PER_MICRO: u32 = 1_000; +/// Nanoseconds per millisecond +const NANOS_PER_MILLI: u32 = 1_000_000; + /// Delay with up to nanosecond precision. pub trait DelayNs { /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer @@ -9,22 +14,30 @@ pub trait DelayNs { /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. fn delay_us(&mut self, mut us: u32) { - while us > 4_294_967 { - us -= 4_294_967; - self.delay_ns(4_294_967_000); + const MAX_MICROS: u32 = u32::MAX / NANOS_PER_MICRO; + + // Avoid potential overflow if micro -> micro conversion is too large + while us > MAX_MICROS { + us -= MAX_MICROS; + self.delay_ns(MAX_MICROS * NANOS_PER_MICRO); } - self.delay_ns(us * 1_000); + + self.delay_ns(us * NANOS_PER_MICRO); } /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. #[inline] fn delay_ms(&mut self, mut ms: u32) { - while ms > 4294 { - ms -= 4294; - self.delay_ns(4_294_000_000); + const MAX_MILLIS: u32 = u32::MAX / NANOS_PER_MILLI; + + // Avoid potential overflow if milli -> micro conversion is too large + while ms > MAX_MILLIS { + ms -= MAX_MILLIS; + self.delay_ns(MAX_MILLIS * NANOS_PER_MILLI); } - self.delay_ns(ms * 1_000_000); + + self.delay_ns(ms * NANOS_PER_MILLI); } } From 5656d678f09a9045b9be8d26a4fa88af0c0b0ee5 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 30 Nov 2023 14:09:20 +0100 Subject: [PATCH 090/201] Fix comments --- embedded-hal/src/delay.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 213c42491..01e563750 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -16,7 +16,7 @@ pub trait DelayNs { fn delay_us(&mut self, mut us: u32) { const MAX_MICROS: u32 = u32::MAX / NANOS_PER_MICRO; - // Avoid potential overflow if micro -> micro conversion is too large + // Avoid potential overflow if micro -> nano conversion is too large while us > MAX_MICROS { us -= MAX_MICROS; self.delay_ns(MAX_MICROS * NANOS_PER_MICRO); @@ -31,7 +31,7 @@ pub trait DelayNs { fn delay_ms(&mut self, mut ms: u32) { const MAX_MILLIS: u32 = u32::MAX / NANOS_PER_MILLI; - // Avoid potential overflow if milli -> micro conversion is too large + // Avoid potential overflow if milli -> nano conversion is too large while ms > MAX_MILLIS { ms -= MAX_MILLIS; self.delay_ns(MAX_MILLIS * NANOS_PER_MILLI); From 5051c5b6a3bfde01eb0719293db395093417b50b Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 8 Dec 2023 10:19:47 +0100 Subject: [PATCH 091/201] Add an explanation of how 7-bit addresses should be aligned within a u8 --- embedded-hal/src/i2c.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 8f4848a28..c2a1efb3a 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -268,6 +268,17 @@ impl ErrorType for &mut T { pub trait AddressMode: private::Sealed + 'static {} /// 7-bit address mode type. +/// +/// Note that 7-bit addresses defined by drivers should be specified in **right-aligned** form, +/// e.g. in the range `0x00..=0x7F`. +/// +/// For example, a device that has the seven bit address of `0b011_0010`, and therefore is addressed on the wire using: +/// +/// * `0b0110010_0` or `0x64` for *writes* +/// * `0b0110010_1` or `0x65` for *reads* +/// +/// Should be specified as `0b0011_0010` or `0x32`, NOT `0x64` or `0x65`. Care should be taken by both HAL and driver +/// crate writers to use this scheme consistently. pub type SevenBitAddress = u8; /// 10-bit address mode type. From 4857fefc90276c2d6bfe244d994865a2b4e754b8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 13 Dec 2023 01:06:42 +0100 Subject: [PATCH 092/201] ci: simplify, test MSRV. - Use separate jobs instead of `matrix`. The Github Actions matrix is very cursed, especially if you use `include`. We don't have that many combinations, IMO doing separate jobs is way more straightforward and only slightly more verbose. - `embedded-hal` needs `rust-version = 1.60` because it uses `dep:`. Fixes #538 --- .github/workflows/test.yml | 70 +++++++++++++++++++--------------- Cargo.stable.toml | 20 ---------- embedded-hal-async/Cargo.toml | 2 +- embedded-hal-async/README.md | 11 ++---- embedded-hal-bus/README.md | 2 + embedded-hal/Cargo.toml | 2 +- embedded-io-adapters/README.md | 5 +-- embedded-io-async/Cargo.toml | 1 + embedded-io-async/README.md | 7 ++-- 9 files changed, 52 insertions(+), 68 deletions(-) delete mode 100644 Cargo.stable.toml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19ff36502..f7abf9027 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,38 +13,48 @@ env: jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - rust: - - stable - - 1.60.0 # MSRV - - nightly - target: - - x86_64-unknown-linux-gnu - - thumbv7m-none-eabi - features: - - '' - include: - - rust: stable - target: x86_64-unknown-linux-gnu - features: std - - rust: stable - target: x86_64-unknown-linux-gnu - features: alloc - - rust: nightly - target: x86_64-unknown-linux-gnu - features: std,tokio-1,futures-03,defmt-03 steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - target: ${{ matrix.target }} + - uses: dtolnay/rust-toolchain@beta + - run: cargo test --workspace + + test-all-features: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@beta + - run: cargo test --workspace --all-features - - run: mv Cargo.stable.toml Cargo.toml - if: matrix.rust != 'nightly' + build-nostd: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@beta + with: + target: thumbv7m-none-eabi + - run: > + cargo build + --workspace + --target thumbv7m-none-eabi + --features async,defmt-03 - - run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }} + msrv-1-60: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@1.60 + - run: > + cargo test + -p embedded-hal:1.0.0-rc.2 + -p embedded-hal-bus + -p embedded-hal-nb + -p embedded-io + -p embedded-io-adapters + -p embedded-can - - run: cargo test --target=${{ matrix.target }} --features=${{ matrix.features }} - if: contains(matrix.target, 'linux') + msrv-1-75: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@beta + - run: cargo test --workspace --all-features diff --git a/Cargo.stable.toml b/Cargo.stable.toml deleted file mode 100644 index 78f6fd3cd..000000000 --- a/Cargo.stable.toml +++ /dev/null @@ -1,20 +0,0 @@ -# CI moves this file to `Cargo.toml` when building for stable. - -[workspace] -resolver = "2" - -members = [ - "embedded-hal", - "embedded-hal-nb", - "embedded-hal-bus", - "embedded-can", - "embedded-io", - "embedded-io-adapters", -] - -# Cargo implicitly adds path dependencies to the workspace. -# Even if they're optional and not enabled. This prevents that. -exclude = [ - "embedded-hal-async", - "embedded-io-async", -] \ No newline at end of file diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 231f00e2d..e14722974 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -12,7 +12,7 @@ name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "1.0.0-rc.2" -rust-version = "1.65.0" +rust-version = "1.75" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index 2f739993a..39440d5c7 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -23,15 +23,10 @@ or a console to operate either on hardware serial ports or on virtual ones like ## Minimum Supported Rust Version (MSRV) -This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for -`async fn` in traits (AFIT), which is not stable yet. +This crate is guaranteed to compile on stable Rust 1.75 and up. It *might* +compile with older versions but that may change in any new patch release. -Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features -at any time. If this happens, we might do changes that increase the minimum required nightly -version in any patch release. - -When AFIT becomes stable, MSRV will be bumped to the Rust version that stabilizes it, after which -point the [standard MSRV bump policy](../docs/msrv.md) will apply. +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. ## License diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index d0b9bbdb6..3cfae9448 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -41,6 +41,8 @@ compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. +Enabling the `async` Cargo features requires Rust 1.75 or higher. + ## License Licensed under either of diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index a26ae3372..fcc145e74 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" -rust-version = "1.56" +rust-version = "1.60" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md index 6edd3c47b..a069d1fac 100644 --- a/embedded-io-adapters/README.md +++ b/embedded-io-adapters/README.md @@ -34,10 +34,7 @@ compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. -Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust nightly newer than -`nightly-2022-11-22`, due to requiring support for `async fn` in traits (AFIT), -which is not stable yet. Keep in mind Rust nightlies can make backwards-incompatible -changes to unstable features at any time. +Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust 1.75 or higher. ## License diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 87891795c..6cd60c9c7 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-io-async" version = "0.6.1" edition = "2021" +rust-version = "1.75" description = "Async embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md index 6e6ddf18a..744f6e6f2 100644 --- a/embedded-io-async/README.md +++ b/embedded-io-async/README.md @@ -18,11 +18,10 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for -`async fn` in traits (AFIT), which is not stable yet. +This crate is guaranteed to compile on stable Rust 1.75 and up. It *might* +compile with older versions but that may change in any new patch release. -Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features -at any time. +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. ## License From fc2cc4c3afa028ce51e4f662ac73f3bc659b0176 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 13 Dec 2023 15:00:38 +0100 Subject: [PATCH 093/201] gpio: require `&mut self` in `InputPin` and `StatefulOutputPin`. --- embedded-hal/CHANGELOG.md | 2 +- embedded-hal/src/digital.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index a177ba7f1..c64a5ddbc 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +- gpio: require `&mut self` in `InputPin` and `StatefulOutputPin`. ## [v1.0.0-rc.2] - 2023-11-28 diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index f54c72153..f65fc0bfd 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -169,22 +169,22 @@ pub trait StatefulOutputPin: OutputPin { /// Is the pin in drive high mode? /// /// *NOTE* this does *not* read the electrical state of the pin. - fn is_set_high(&self) -> Result; + fn is_set_high(&mut self) -> Result; /// Is the pin in drive low mode? /// /// *NOTE* this does *not* read the electrical state of the pin. - fn is_set_low(&self) -> Result; + fn is_set_low(&mut self) -> Result; } impl StatefulOutputPin for &mut T { #[inline] - fn is_set_high(&self) -> Result { + fn is_set_high(&mut self) -> Result { T::is_set_high(self) } #[inline] - fn is_set_low(&self) -> Result { + fn is_set_low(&mut self) -> Result { T::is_set_low(self) } } @@ -205,20 +205,20 @@ impl ToggleableOutputPin for &mut T { /// Single digital input pin. pub trait InputPin: ErrorType { /// Is the input pin high? - fn is_high(&self) -> Result; + fn is_high(&mut self) -> Result; /// Is the input pin low? - fn is_low(&self) -> Result; + fn is_low(&mut self) -> Result; } -impl InputPin for &T { +impl InputPin for &mut T { #[inline] - fn is_high(&self) -> Result { + fn is_high(&mut self) -> Result { T::is_high(self) } #[inline] - fn is_low(&self) -> Result { + fn is_low(&mut self) -> Result { T::is_low(self) } } From 38f6070118272c0aa6c599578476489dea435f30 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Dec 2023 14:24:10 +0100 Subject: [PATCH 094/201] Release v1.0.0-rc.3 --- .github/workflows/test.yml | 2 +- embedded-hal-async/CHANGELOG.md | 7 ++++++- embedded-hal-async/Cargo.toml | 6 +++--- embedded-hal-bus/CHANGELOG.md | 9 +++++++-- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-nb/CHANGELOG.md | 7 ++++++- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 7 ++++++- embedded-hal/Cargo.toml | 2 +- 9 files changed, 35 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f7abf9027..e1d7c5664 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,7 @@ jobs: - uses: dtolnay/rust-toolchain@1.60 - run: > cargo test - -p embedded-hal:1.0.0-rc.2 + -p embedded-hal:1.0.0-rc.3 -p embedded-hal-bus -p embedded-hal-nb -p embedded-io diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 468c3b711..314e0ebaa 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v1.0.0-rc.3] - 2023-12-14 + +- Updated `embedded-hal` to version `1.0.0-rc.3`. + ## [v1.0.0-rc.2] - 2023-11-28 - Updated `embedded-hal` to version `1.0.0-rc.2`. @@ -82,7 +86,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.3...HEAD +[v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...embedded-hal-async-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...embedded-hal-async-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 [v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index e14722974..fe09580fe 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" rust-version = "1.75" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } -defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file +embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 6858e9c19..8155db960 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes -## [v1.0.0-rc.2] - 2023-11-28 +## [v0.1.0-rc.3] - 2023-12-14 + +- Updated `embedded-hal` to version `1.0.0-rc.3`. + +## [v0.1.0-rc.2] - 2023-11-28 - Updated `embedded-hal(-async)` to version `1.0.0-rc.2`. - Minor document fixes. @@ -48,7 +52,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...HEAD +[v0.1.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...embedded-hal-bus-v0.1.0-rc.3 [v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 [v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 937a49b2c..7f04bbded 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-rc.2" +version = "0.1.0-rc.3" [features] std = [] @@ -20,8 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } -embedded-hal-async = { version = "=1.0.0-rc.2", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +embedded-hal-async = { version = "=1.0.0-rc.3", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 11753a27f..6ac3be4ed 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v1.0.0-rc.3] - 2023-12-14 + +- Updated `embedded-hal` to version `1.0.0-rc.3`. + ## [v1.0.0-rc.2] - 2023-11-28 - Updated `embedded-hal` to version `1.0.0-rc.2`. @@ -40,7 +44,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.3...HEAD +[v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...embedded-hal-nb-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.1...embedded-hal-nb-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...embedded-hal-nb-v1.0.0-rc.1 [v1.0.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...embedded-hal-nb-v1.0.0-alpha.3 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 52c6755ea..d0c006e76 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" edition = "2021" rust-version = "1.56" @@ -13,7 +13,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index c64a5ddbc..7dfdd866c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes yet + +## [v1.0.0-rc.3] - 2023-12-14 + - gpio: require `&mut self` in `InputPin` and `StatefulOutputPin`. ## [v1.0.0-rc.2] - 2023-11-28 @@ -310,7 +314,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.3...HEAD +[v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 [v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index fcc145e74..b91f45f44 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" [features] defmt-03 = ["dep:defmt-03"] From c5c076a9a0f66118768b28c5786b12dd87d909e8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Dec 2023 20:55:16 +0100 Subject: [PATCH 095/201] gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. --- embedded-hal/CHANGELOG.md | 2 +- embedded-hal/src/digital.rs | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 7dfdd866c..9d3ac625b 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes yet +- gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. ## [v1.0.0-rc.3] - 2023-12-14 diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index f65fc0bfd..afcf03963 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -175,6 +175,12 @@ pub trait StatefulOutputPin: OutputPin { /// /// *NOTE* this does *not* read the electrical state of the pin. fn is_set_low(&mut self) -> Result; + + /// Toggle pin output. + fn toggle(&mut self) -> Result<(), Self::Error> { + let was_low: bool = self.is_set_low()?; + self.set_state(PinState::from(was_low)) + } } impl StatefulOutputPin for &mut T { @@ -187,15 +193,7 @@ impl StatefulOutputPin for &mut T { fn is_set_low(&mut self) -> Result { T::is_set_low(self) } -} - -/// Output pin that can be toggled. -pub trait ToggleableOutputPin: ErrorType { - /// Toggle pin output. - fn toggle(&mut self) -> Result<(), Self::Error>; -} -impl ToggleableOutputPin for &mut T { #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { T::toggle(self) From d065eb572fd344197b07a8be18c656aa95a1e831 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 16 Jun 2021 19:23:50 +0200 Subject: [PATCH 096/201] Add initial version of migration guide --- MIGRATING-0.2-1.0.md | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 MIGRATING-0.2-1.0.md diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md new file mode 100644 index 000000000..70c83bb08 --- /dev/null +++ b/MIGRATING-0.2-1.0.md @@ -0,0 +1,96 @@ +# Migrating from embedded-hal 0.2.x to 1.0.0 + +## Table of contents + +- [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100) + - [Table of contents](#table-of-contents) + - [Trait organization](#trait-organization) + - [Fallibility](#fallibility) + - [Method renaming](#method-renaming) + - [`nb` dependency](#nb-dependency) + - [Prelude](#prelude) + - [Features](#features) + - [Use-case-specific help](#use-case-specific-help) + - [For driver authors](#for-driver-authors) + - [I2C traits](#i2c-traits) + - [SPI traits](#spi-traits) + - [For HAL authors](#for-hal-authors) + +## Trait organization + +All traits have been organized in modules depending on their execution model. That includes `blocking` and `nb` for +non-blocking. In the future when we add asynchronous traits, we envision adding a `futures` (or similarly-named) module. + +## Fallibility + +All trait methods are now fallible so that they can be used in any possible situation. +However, HAL implementations can also provide infallible versions of the methods. + +## Method renaming + +The methods in `SPI`, `I2C` and `Serial` traits for both `blocking` and `nb` execution models have been renamed +to `write()`, `read()` and `flush()`. + +## `nb` dependency + +The `Result` type and `block!` macro from the [`nb`] crate are now reexported in `embeddeh_hal::nb`. +This ensures there are no version mismatches. +You should remove the `nb` crate dependency in your `Cargo.toml` in any version and use the reexported items. + +In your `Cargo.toml`: +```diff +- nb = "1" +``` + +In your code: +```diff +- use nb; ++ use embedded_hal::nb; +``` +You can also drop `#[macro_use]` if you are using Rust edition 2018. + +Alternatively (needs Rust edition 2018): +```diff +- use nb::{Result, block}; ++ use embedded_hal::nb::{Result, block}; +``` + +## Prelude + +The prelude has been removed because it could make method calls ambiguous, since the method names are now +the same across execution models. +To overcome this, simply import the traits you wish to use individually. +If you run into ambiguous method calls, you can disambiguate using fully-qualified syntax (the error message +from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code +to limit the scope of the trait imports and thus avoid ambiguity. +Please note that it is also possible to import traits *inside a function*. + +## Features + +The `unproven` feature has been removed and the traits have been marked as proven. +In the past, managing unproven features, and having "sort of breaking" changes have been a struggling point. +Also, people tended to adopt `unproven` features quickly, but the features would take a very +long time to stabilize. + +Instead, we would like to push experimentation OUT of the `embedded-hal` crate, allowing people to +experiment externally, and merge when some kind of feasability had been proven. + +## Use-case-specific help + +### For driver authors + +### I2C traits + +Nothing changed. + +#### SPI traits + +For the blocking traits nothing changed. +For the non-blocking traits, TODO + +### For HAL authors + +TODO + + +[MeetingSummary]: https://hackmd.io/ck-xRXtMTmKYXdK5bEh82A From 533af846aa4aa1031c95f7515b96b3d17b3a5256 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Thu, 17 Jun 2021 09:26:33 +0200 Subject: [PATCH 097/201] Update MIGRATING-0.2-1.0.md Co-authored-by: Ryan --- MIGRATING-0.2-1.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 70c83bb08..b2cb9746a 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -59,7 +59,7 @@ Alternatively (needs Rust edition 2018): The prelude has been removed because it could make method calls ambiguous, since the method names are now the same across execution models. -To overcome this, simply import the traits you wish to use individually. +To overcome this, please import the traits you wish to use individually. If you run into ambiguous method calls, you can disambiguate using fully-qualified syntax (the error message from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code to limit the scope of the trait imports and thus avoid ambiguity. From a255e9097531e097d09aae462060042460627949 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 30 Jun 2021 09:51:58 +0200 Subject: [PATCH 098/201] Add more content --- MIGRATING-0.2-1.0.md | 57 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index b2cb9746a..5ee5c4652 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -9,6 +9,7 @@ - [Method renaming](#method-renaming) - [`nb` dependency](#nb-dependency) - [Prelude](#prelude) + - [`rng` module](#rng-module) - [Features](#features) - [Use-case-specific help](#use-case-specific-help) - [For driver authors](#for-driver-authors) @@ -26,10 +27,54 @@ non-blocking. In the future when we add asynchronous traits, we envision adding All trait methods are now fallible so that they can be used in any possible situation. However, HAL implementations can also provide infallible versions of the methods. +For example, an implementation similar to the one below would allow to use the GPIO pins as `OutputPin`s +in any generic driver or implementation-agnostic code (by importing the `OutputPin` trait), +as well as using the infallible methods in non-generic code, thus avoiding the need to use `unwrap()` +the results in many cases and resulting in more succinct code. + +It should be noted that given this implementation, importing the `OutputPin` trait can result in +ambiguous calls, so please remove the trait imports if you do not need them. + +```rust +use core::convert::Infallible; +use embedded_hal::blocking::digital::OutputPin; + +struct GpioPin; + +impl OutputPin for GpioPin { + type Error = Infallible; + + fn set_high(&mut self) -> Result<(), Self::Error> { + // ... + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + // ... + Ok(()) + } +} + +impl GpioPin { + fn set_high(&mut self) { + // ... + } + + fn set_low(&mut self) { + // ... + } +} +``` + ## Method renaming The methods in `SPI`, `I2C` and `Serial` traits for both `blocking` and `nb` execution models have been renamed -to `write()`, `read()` and `flush()`. +to `write()`, `read()` and `flush()` for consistency. + +In order to avoid method call ambiguity, only the traits from the corresponding execution model should be imported +into the relevant scope. This is the reason why we have removed the prelude. + +For more on this, see [Prelude](#prelude). ## `nb` dependency @@ -65,15 +110,21 @@ from the compiler should already tell you how it should look like in your case) to limit the scope of the trait imports and thus avoid ambiguity. Please note that it is also possible to import traits *inside a function*. +## `rng` module + +The `rng` module and its traits have been removed in favor of the [`rand_core`] traits. + +[`rand_core`]: https://crates.io/crates/rand_core + ## Features The `unproven` feature has been removed and the traits have been marked as proven. -In the past, managing unproven features, and having "sort of breaking" changes have been a struggling point. +In the past, managing unproven features, and having "sort of breaking" changes has been a struggling point. Also, people tended to adopt `unproven` features quickly, but the features would take a very long time to stabilize. Instead, we would like to push experimentation OUT of the `embedded-hal` crate, allowing people to -experiment externally, and merge when some kind of feasability had been proven. +experiment externally, and merge when some kind of feasibility had been proven. ## Use-case-specific help From da438d492083e493464c89b9f8ad727f0d872011 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Sat, 11 Sep 2021 19:23:16 +0200 Subject: [PATCH 099/201] Update guide to current state --- MIGRATING-0.2-1.0.md | 46 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 5ee5c4652..dce695fc0 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -7,9 +7,12 @@ - [Trait organization](#trait-organization) - [Fallibility](#fallibility) - [Method renaming](#method-renaming) + - [SPI transfer return type](#spi-transfer-return-type) + - [Error type bounds](#error-type-bounds) - [`nb` dependency](#nb-dependency) - [Prelude](#prelude) - [`rng` module](#rng-module) + - [Removed blanket implementations](#removed-blanket-implementations) - [Features](#features) - [Use-case-specific help](#use-case-specific-help) - [For driver authors](#for-driver-authors) @@ -19,9 +22,11 @@ ## Trait organization -All traits have been organized in modules depending on their execution model. That includes `blocking` and `nb` for +All traits have been organized in modules for each feature, each of these containing sub-modules depending on their execution model. That includes `blocking` and `nb` for non-blocking. In the future when we add asynchronous traits, we envision adding a `futures` (or similarly-named) module. +Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules. + ## Fallibility All trait methods are now fallible so that they can be used in any possible situation. @@ -76,6 +81,38 @@ into the relevant scope. This is the reason why we have removed the prelude. For more on this, see [Prelude](#prelude). +## SPI transfer return type + +The `transfer()` method in the trait `spi::blocking::Transfer` previously returned +a slice of the output data. +This slice is the same as the output buffer which is passed to the method, though, thus redundant and potentially confusing. +The `transfer()` method now returns `Result<(), Self::Error>`. +If you were using this return value, adapting the code should be straight forward by simply using the reception buffer which is passed. +See an example: +```rust +let tx_data = [1, 2, 3, 4]; +let mut rx_data = [0; 4]; +let data = spi.transfer(&tx_data, &mut rx_data)?; +println!("{:?}", data); +// There is no need to do `let data = `, since we already have the data in `rx_data`. +// Do this instead: +spi.transfer(&tx_data, &mut rx_data)?; +println!("{:?}", rx_data); +``` + +## Error type bounds + +All associated error types are now required to implement `core::fmt::Debug`. +Usually it is enough to add a `#[derive(Debug)]` clause to your error types. For example: + +```diff ++ #[derive(Debug)] +pub enum MyError { + InvalidInputData, + // ... +} +``` + ## `nb` dependency The `Result` type and `block!` macro from the [`nb`] crate are now reexported in `embeddeh_hal::nb`. @@ -116,6 +153,13 @@ The `rng` module and its traits have been removed in favor of the [`rand_core`] [`rand_core`]: https://crates.io/crates/rand_core +## Removed blanket implementations + +There were several blanket implementations of blocking traits using the non-blocking +traits as a base. +Due to their relative simplicity and some technical concerns, these have been removed. +Implementing them yourself is now necessary. This should be straight-forward. + ## Features The `unproven` feature has been removed and the traits have been marked as proven. From 1a0d60caad613c2e26eec4d51592973db1dda8f1 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 17 Aug 2022 21:21:36 +0200 Subject: [PATCH 100/201] Add overview and removed traits section + Update to current state --- MIGRATING-0.2-1.0.md | 200 ++++++++++++++++++++++++++++++++----------- 1 file changed, 151 insertions(+), 49 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index dce695fc0..4664a0469 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -4,28 +4,123 @@ - [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100) - [Table of contents](#table-of-contents) + - [Overview and reasoning](#overview-and-reasoning) - [Trait organization](#trait-organization) + - [Trait unification](#trait-unification) + - [Removed traits](#removed-traits) + - [Unconstrained associated types](#unconstrained-associated-types) + - [Impractical traits](#impractical-traits) + - [Delay traits](#delay-traits) + - [Bus/device separation](#busdevice-separation) - [Fallibility](#fallibility) - - [Method renaming](#method-renaming) - [SPI transfer return type](#spi-transfer-return-type) - [Error type bounds](#error-type-bounds) - - [`nb` dependency](#nb-dependency) - [Prelude](#prelude) - [`rng` module](#rng-module) - [Removed blanket implementations](#removed-blanket-implementations) - [Features](#features) + - [Companion crates](#companion-crates) - [Use-case-specific help](#use-case-specific-help) - [For driver authors](#for-driver-authors) - [I2C traits](#i2c-traits) - [SPI traits](#spi-traits) - [For HAL authors](#for-hal-authors) +## Overview and reasoning + +There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and 1.0.0. +We understand the significance of `embedded-hal` in the Rust embedded +ecosystem and thus intend to release a version that stays compatible for a long time. + +In this version, among many other changes, we have addressed several big topics that have emerged over the years: +- [Associated type compatibiilty](#removed-traits) +- [Trait fragmentation](#trait-organization) +- [Bus/device separation](#bus-device-separation) +- [Fallibility](#fallibility) +- [Execution model support](#trait-organization) + ## Trait organization -All traits have been organized in modules for each feature, each of these containing sub-modules depending on their execution model. That includes `blocking` and `nb` for -non-blocking. In the future when we add asynchronous traits, we envision adding a `futures` (or similarly-named) module. +All traits have been organized in modules for each feature. For example `embedded_hal::spi` and `embedded_hal::i2c`. +We only foresee having blocking traits in `embedded-hal`. We have put the traits for different execution models +into separate crates. Notably `embedded-hal-async` and `embedded-hal-nb`. See [companion crates](#companion-crates). +This allows for a separate and more tailored evolution. + + + +Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. + +### Trait unification + +Previously, there were multiple traits for the same feature. In order to avoid fragmentation and ensure +interoperability for generic code, these have now been united. + +For example, most generic code should simply use the `SpiDevice` trait instead of +choosing from `Transactional`, `Transfer`, `Write` and `WriteIter`. + +For HAL implementations and some specialized use-cases there are still a few traits to implement for SPI +but the number has been reduced. + +Please see more about this separation [below](#bus-device-separation). + +## Removed traits + +These traits have been removed in the 1.0.0 release: + +- [`adc::OneShot`][adc] +- [`adc::Channel`][adc] +- [`capture::Capture`][capture] +- `delay::DelayMs` (replaced by `DelayUs`) +- [`digital::IoPin`][iopin] +- [`pwm::Pwm`][pwm] +- [`pwm::PwmPin`][pwm] +- [`qei::Qei`][qei] +- [`timer::Cancel`][timer] +- [`timer::CountDown`][timer] +- [`timer::Periodic`][timer] +- [`watchdog::Disable`][watchdog] +- [`watchdog::Enable`][watchdog] +- [`watchdog::Watchdog`][watchdog] + +Please find a general [roadmap with further guidance here][roadmap-rm-traits] about +how to get these traits back in a future release. +If you need them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. + +[roadmap-rm-traits]: https://github.com/rust-embedded/embedded-hal/issues/357 +[adc]: https://github.com/rust-embedded/embedded-hal/issues/377 +[iopin]: https://github.com/rust-embedded/embedded-hal/issues/397 +[capture]: https://github.com/rust-embedded/embedded-hal/issues/361 +[pwm]: https://github.com/rust-embedded/embedded-hal/issues/358 +[qei]: https://github.com/rust-embedded/embedded-hal/issues/362 +[timer]: https://github.com/rust-embedded/embedded-hal/issues/359 +[watchdog]: https://github.com/rust-embedded/embedded-hal/issues/360 + +### Unconstrained associated types + +Traits defined in `embedded-hal` pursue creating an interface for interoperability between generic code (be it generic user code, generic application code, generic device drivers, etc.). +When a trait has an unconstrained associated type, it is not possible to write generic code around it. Each side (implementer and user) need to specify which type the associated type will be. If the types match, the both parts can work together, however, this is not truly generic code. + +For example, if somebody creates a device driver that receives a `CountDown` struct, it needs to specify what its `Time` type should be. If they choose a type coming from `fugit`, somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides `CountDown` with `Time` types defined in `embedded-time`. It is also not possible for the user to implement `CountDown` for `Time` types defined by `fugit` in a straight-forward way due to the orphan rule. +In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one. + +At the moment no solution for this has been found so we have decided to remove such traits hoping that a solution may be found +and we can add them back in a future 1.x release. + +### Impractical traits + +The [`digital::IoPin` trait][iopin] and the [`adc` traits][adc] have been deemed impractical for use and have thus been removed. +Please feel free to comment on the appropriate issue if you need any of these crates and propose a solution. + +### Delay traits + +The `DelayMs` trait has been removed. The functionality provided by this trait should now be provided by the `DelayUs` trait, +which also features a convenience `delay_ms()` method, so changes should be minimal. -Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules. +This allowed us to reduce the API surface while still keeping the main functionality. We intend to add a generic `Delay` trait +in the future, once the time representation issue has been resolved. + +## Bus/device separation + ## Fallibility @@ -34,19 +129,19 @@ However, HAL implementations can also provide infallible versions of the methods For example, an implementation similar to the one below would allow to use the GPIO pins as `OutputPin`s in any generic driver or implementation-agnostic code (by importing the `OutputPin` trait), -as well as using the infallible methods in non-generic code, thus avoiding the need to use `unwrap()` -the results in many cases and resulting in more succinct code. +as well as using the infallible methods in non-generic code. +This avoids the need to use `unwrap()` the results in many cases and results in more succinct code. It should be noted that given this implementation, importing the `OutputPin` trait can result in ambiguous calls, so please remove the trait imports if you do not need them. ```rust use core::convert::Infallible; -use embedded_hal::blocking::digital::OutputPin; +use embedded_hal::digital::blocking::OutputPin; -struct GpioPin; +struct HalImplGpioPin; -impl OutputPin for GpioPin { +impl OutputPin for HalImplGpioPin { type Error = Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { @@ -60,7 +155,7 @@ impl OutputPin for GpioPin { } } -impl GpioPin { +impl HalImplGpioPin { fn set_high(&mut self) { // ... } @@ -71,24 +166,15 @@ impl GpioPin { } ``` -## Method renaming - -The methods in `SPI`, `I2C` and `Serial` traits for both `blocking` and `nb` execution models have been renamed -to `write()`, `read()` and `flush()` for consistency. - -In order to avoid method call ambiguity, only the traits from the corresponding execution model should be imported -into the relevant scope. This is the reason why we have removed the prelude. - -For more on this, see [Prelude](#prelude). - ## SPI transfer return type -The `transfer()` method in the trait `spi::blocking::Transfer` previously returned -a slice of the output data. +Previously the `transfer()` method in SPI returned a slice of the output data. This slice is the same as the output buffer which is passed to the method, though, thus redundant and potentially confusing. The `transfer()` method now returns `Result<(), Self::Error>`. If you were using this return value, adapting the code should be straight forward by simply using the reception buffer which is passed. + See an example: + ```rust let tx_data = [1, 2, 3, 4]; let mut rx_data = [0; 4]; @@ -113,38 +199,52 @@ pub enum MyError { } ``` -## `nb` dependency +Additionally, for the I2C, SPI and Serial communication interfaces we have added a dedicated mechanism +which allows for two crucial requirements: +1. Generic code like drivers can interpret and act on errors if they want to. +2. HAL implementations can have arbitrarily-precise error types. -The `Result` type and `block!` macro from the [`nb`] crate are now reexported in `embeddeh_hal::nb`. -This ensures there are no version mismatches. -You should remove the `nb` crate dependency in your `Cargo.toml` in any version and use the reexported items. +This works in the following way: -In your `Cargo.toml`: -```diff -- nb = "1" -``` +For each interface, `embedded-hal` defines an `ErrorKind` `enum` type with all sensible error variants as well +as an `Error` trait featuring a method that converts the type into that `ErrorKind`. -In your code: -```diff -- use nb; -+ use embedded_hal::nb; -``` -You can also drop `#[macro_use]` if you are using Rust edition 2018. +`embedded-hal` still allows for implementation-defined error types associated to each trait, but requires these to +implement the appropriate `Error` trait, thus providing a mapping to a defined set of error variants. -Alternatively (needs Rust edition 2018): -```diff -- use nb::{Result, block}; -+ use embedded_hal::nb::{Result, block}; +With this mechanism, HAL implementations can continue to define their own error types which can carry as much +information as they want. On the other hand it is now possible for generic code to inspect those errors +and act on common errors like I2Cs NACK. + +Furthermore, implementation-specific code can access the original error type and retrieve any information contained. + +An example of a driver which looks for I2C NACK errors and returns its own `DeviceBusy` or `Comm` error +wrapping the original one could be as follows: + +```rust +const address = 0x1D; + +fn set_some_parameter(&mut self) -> Result<(), Self::Error> { + const data = [0, 1]; + match self.i2c.write(address, &data) { + Err(e) => match e.kind() { + ErrorKind::NoAcknowledge(_) => Err(Self::Error::DeviceBusy(e)), + _ => Err(Self::Error::Comm(e)) // wrap and return any other error + }, + Ok(_) => Ok(()) + } +} ``` ## Prelude The prelude has been removed because it could make method calls ambiguous, since the method names are now -the same across execution models. +the same across traits. To overcome this, please import the traits you wish to use individually. -If you run into ambiguous method calls, you can disambiguate using fully-qualified syntax (the error message + +If you run into ambiguous method calls, you can disambiguate using the fully-qualified syntax (the error message from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code -to limit the scope of the trait imports and thus avoid ambiguity. +to limit the scope of the trait imports and thus avoid the ambiguity. Please note that it is also possible to import traits *inside a function*. ## `rng` module @@ -157,8 +257,11 @@ The `rng` module and its traits have been removed in favor of the [`rand_core`] There were several blanket implementations of blocking traits using the non-blocking traits as a base. -Due to their relative simplicity and some technical concerns, these have been removed. -Implementing them yourself is now necessary. This should be straight-forward. + +Since the non-blocking traits have been extracted into the separate crate `embedded-hal-nb`, +these have been removed. + + ## Features @@ -170,18 +273,17 @@ long time to stabilize. Instead, we would like to push experimentation OUT of the `embedded-hal` crate, allowing people to experiment externally, and merge when some kind of feasibility had been proven. +## Companion crates + ## Use-case-specific help ### For driver authors ### I2C traits -Nothing changed. #### SPI traits -For the blocking traits nothing changed. -For the non-blocking traits, TODO ### For HAL authors From c090f4dfa3fc479b683f871cc8fdd34648e0eea0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 25 Dec 2023 00:34:23 +0100 Subject: [PATCH 101/201] Expand migration guide, update for the latest changes. --- MIGRATING-0.2-1.0.md | 231 +++++++++++++++++++++++++++++-------------- 1 file changed, 156 insertions(+), 75 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 4664a0469..e980e046d 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -2,29 +2,23 @@ ## Table of contents -- [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100) - - [Table of contents](#table-of-contents) - - [Overview and reasoning](#overview-and-reasoning) - - [Trait organization](#trait-organization) - - [Trait unification](#trait-unification) - - [Removed traits](#removed-traits) - - [Unconstrained associated types](#unconstrained-associated-types) - - [Impractical traits](#impractical-traits) - - [Delay traits](#delay-traits) - - [Bus/device separation](#busdevice-separation) - - [Fallibility](#fallibility) - - [SPI transfer return type](#spi-transfer-return-type) - - [Error type bounds](#error-type-bounds) - - [Prelude](#prelude) - - [`rng` module](#rng-module) - - [Removed blanket implementations](#removed-blanket-implementations) - - [Features](#features) - - [Companion crates](#companion-crates) - - [Use-case-specific help](#use-case-specific-help) - - [For driver authors](#for-driver-authors) - - [I2C traits](#i2c-traits) - - [SPI traits](#spi-traits) - - [For HAL authors](#for-hal-authors) +- [Overview and reasoning](#overview-and-reasoning) +- [Trait organization](#trait-organization) +- [Trait unification](#trait-unification) +- [Removed traits](#removed-traits) + - [Unconstrained associated types](#unconstrained-associated-types) + - [Impractical traits](#impractical-traits) + - [Serial traits](#serial-traits) + - [RNG traits](#rng-traits) + - [CAN traits](#can-traits) +- [SPI Bus/device separation](#spi-busdevice-separation) +- [Fallibility](#fallibility) +- [SPI transfer return type](#spi-transfer-return-type) +- [Error type bounds](#error-type-bounds) +- [Prelude](#prelude) +- [Removed blanket implementations](#removed-blanket-implementations) +- [Cargo Features](#cargo-features) +- [Companion crates](#companion-crates) ## Overview and reasoning @@ -32,7 +26,21 @@ There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and We understand the significance of `embedded-hal` in the Rust embedded ecosystem and thus intend to release a version that stays compatible for a long time. -In this version, among many other changes, we have addressed several big topics that have emerged over the years: +The main difference betewen `embedded-hal` 0.2 and 1.0 is the project is now focused +on a single goal: traits for writing drivers that work on any HAL. + +In `embedded-hal` 0.2, the traits had dual goals: +- Standardize the API of HAL crates, so HAL crate authors get guidance on how to design their APIs and + end users writing code directly against one HAL get a familiar API. +- Allowing writing generic drivers using the traits, so they work on top of any HAL crate. + +For `embedded-hal` 1.0, we decided to drop the first goal, targeting only the second. The reasons are: + +- Standardizing HAL APIs is difficult, because hardware out there has wildly different sets of capabilities. Modeling all capabilities required many different variants of the traits, and required "customization points" like associated types, significantly increasing complexity. +- There is a tension between both goals. "Customization points" like associated types make the traits hard to use in generic HAL-independent drivers. +- The second goal delivers much more value. Being able to use any driver together with any HAL crate, out of the box, and across the entire Rust Embedded ecosystem, is just plain awesome. + +This refocusing on drivers is the root cause of many of the changes between `embedded-hal` 0.2 and 1.0: - [Associated type compatibiilty](#removed-traits) - [Trait fragmentation](#trait-organization) - [Bus/device separation](#bus-device-separation) @@ -41,39 +49,39 @@ In this version, among many other changes, we have addressed several big topics ## Trait organization -All traits have been organized in modules for each feature. For example `embedded_hal::spi` and `embedded_hal::i2c`. +All traits have been organized in modules for each peripheral. For example `embedded_hal::spi` and `embedded_hal::i2c`. We only foresee having blocking traits in `embedded-hal`. We have put the traits for different execution models into separate crates. Notably `embedded-hal-async` and `embedded-hal-nb`. See [companion crates](#companion-crates). This allows for a separate and more tailored evolution. - - -Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. +Execution-model-independent definitions have been moved into the peripheral's module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. -### Trait unification +## Trait unification -Previously, there were multiple traits for the same feature. In order to avoid fragmentation and ensure -interoperability for generic code, these have now been united. +Previously, there were multiple traits for the same peripheral, for different sets of capabilities. The reasoning +was different hardware supports a different set of features, so by making the traits granular each HAL implementation +can implement only the features supported by the hardware. -For example, most generic code should simply use the `SpiDevice` trait instead of -choosing from `Transactional`, `Transfer`, `Write` and `WriteIter`. +However, this has proven to be troublesome for generic drivers, in cases where a driver expects to use one +trait, but the HAL crate implements only other traits. To avoid this fragmentation and ensure +interoperability for generic code, these have now been unified. -For HAL implementations and some specialized use-cases there are still a few traits to implement for SPI -but the number has been reduced. +- I2C: `Read`, `Write`, `WriteIter`, `WriteIterRead`, `WriteRead`, `Transactional`, `TransactionalIter` have now been unified into a single `I2c` trait. +- SPI: `Write` `WriteIter`, `Transfer`, `Transactional` have been unified into `SpiBus`. +- GPIO: `ToggleableOutputPin` has been merged into `StatefulOutputPin`. +- Delays: `DelayMs`, `DelayUs` has been unified into `DelayNs` (and precision extended to nanoseconds). -Please see more about this separation [below](#bus-device-separation). +HAL implementation crates should implement the full functionality of the traits. If a feature is not supported natively by the hardware, it should be polyfilled/emulated in software. In no case should "not supported" errors be returned. This ensures maximum compatibility. ## Removed traits -These traits have been removed in the 1.0.0 release: +These traits have been removed in the 1.0.0 release, with no replacement for now: - [`adc::OneShot`][adc] - [`adc::Channel`][adc] - [`capture::Capture`][capture] -- `delay::DelayMs` (replaced by `DelayUs`) - [`digital::IoPin`][iopin] - [`pwm::Pwm`][pwm] -- [`pwm::PwmPin`][pwm] - [`qei::Qei`][qei] - [`timer::Cancel`][timer] - [`timer::CountDown`][timer] @@ -83,8 +91,11 @@ These traits have been removed in the 1.0.0 release: - [`watchdog::Watchdog`][watchdog] Please find a general [roadmap with further guidance here][roadmap-rm-traits] about -how to get these traits back in a future release. -If you need them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. +whether and how to get these traits back in a future release + +If you are a generic driver author and need one of them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. + +HAL implementation crates are encouraged to provide their own APIs for functionality for the removed traits, and not implement any traits. This will allow the APIs to more closely match the hardware capabilities, and allow users to continue to use them. [roadmap-rm-traits]: https://github.com/rust-embedded/embedded-hal/issues/357 [adc]: https://github.com/rust-embedded/embedded-hal/issues/377 @@ -98,10 +109,10 @@ If you need them, we would like to hear from you. Please add your use case to th ### Unconstrained associated types Traits defined in `embedded-hal` pursue creating an interface for interoperability between generic code (be it generic user code, generic application code, generic device drivers, etc.). -When a trait has an unconstrained associated type, it is not possible to write generic code around it. Each side (implementer and user) need to specify which type the associated type will be. If the types match, the both parts can work together, however, this is not truly generic code. +When a trait has an unconstrained associated type (for example `type Time;`), it is not possible to write generic code around it. Each side (implementer and user) need to specify which type the associated type will be. If the types match, the both parts can work together, however, this is not truly generic code. For example, if somebody creates a device driver that receives a `CountDown` struct, it needs to specify what its `Time` type should be. If they choose a type coming from `fugit`, somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides `CountDown` with `Time` types defined in `embedded-time`. It is also not possible for the user to implement `CountDown` for `Time` types defined by `fugit` in a straight-forward way due to the orphan rule. -In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one. +In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one. This means all these traits don't fulfill the "allow writing generic drivers" goal. At the moment no solution for this has been found so we have decided to remove such traits hoping that a solution may be found and we can add them back in a future 1.x release. @@ -109,18 +120,99 @@ and we can add them back in a future 1.x release. ### Impractical traits The [`digital::IoPin` trait][iopin] and the [`adc` traits][adc] have been deemed impractical for use and have thus been removed. -Please feel free to comment on the appropriate issue if you need any of these crates and propose a solution. +Please feel free to comment on the appropriate issue if you need any of these traitsk and propose a solution. + +### Serial traits + +The `blocking::serial::Write` trait has been removed in favor of the [`embedded-io`] traits, also maintained within the `embedded-hal` repository. + +[`embedded-io`]: https://crates.io/crates/embedded-io + +### RNG traits + +The `rng` module and its traits have been removed in favor of the [`rand_core`] traits. -### Delay traits +[`rand_core`]: https://crates.io/crates/rand_core + +### CAN traits + +The `can` module and its traits have been removed in favor of the [`embedded-can`] traits. + +[`embedded-can`]: https://crates.io/crates/embedded-can + +## SPI Bus/device separation + +The SPI traits have been unified into a single `SpiBus` trait. However, to allow sharing an SPI bus, and hardware control of the CS pin, 1.0 adds the `SpiDevice` trait. + +The short summary is: +- `SpiBus` represents an entire SPI bus (with SCK, MOSI, MISO) pins +- `SpiDevice` represents a single device on an SPI bus, selected by a CS pin. -The `DelayMs` trait has been removed. The functionality provided by this trait should now be provided by the `DelayUs` trait, -which also features a convenience `delay_ms()` method, so changes should be minimal. +See the [SPI documentation](https://docs.rs/embedded-hal/1.0.0/embedded_hal/spi/index.html) for more details. -This allowed us to reduce the API surface while still keeping the main functionality. We intend to add a generic `Delay` trait -in the future, once the time representation issue has been resolved. +When upgrading code to `embedded-hal` 1.0, it is critical to implement/use the right trait depending on the underlying situation. -## Bus/device separation - +### For HAL implementation crates + +- If you previously implemented the SPI traits, and did *not* manage a CS pin automatically, you should now implement `SpiBus`, which is the equivalent in 1.0. +- Optionally, if the API *does* manage a CS pin automatically, you may implement `SpiDevice`. + - This is required if the underlying API requires it to manage the CS pin, like `spidev` on Linux. + +Do not implement `SpiBus` and `SpiDevice` on the same struct, since this is never correct. When there's no CS pin being controlled you must implement only `SpiBus`, and when there is, implement only `SpiDevice`. If you want to offer both APIs, implement them on separate structs so the user has to cohose one or the other. + +### For driver crates + +- If your device has SCK, MOSI, MISO, CS pins: use `SpiDevice`. + - Do NOT take the CS pin as a separate `OutputPin`, the `SpiDevice` will manage it for you. Taking the CS pin separately will make your driver not work on shared buses. +- If your device only has SCK, MOSI, MISO: use `SpiBus`. + - This means bus sharing won't be supported, but there's no way to share without a CS pin anyway. +- If you're using SPI to bitbang non-SPI protocols (for example, WS2812 smart LEDs), use `SpiBus`. + +### For end users + +You will most likely find the HAL crate you're using implements `SpiBus`, and the driver you want to use +requires `SpiDevice`. To convert from `SpiBus` to `SpiDevice`, wrap it with a [`embedded_hal_bus::spi::ExclusiveDevice`](https://docs.rs/embedded-hal-bus/0.1.0/embedded_hal_bus/spi/struct.ExclusiveDevice.html), together with the CS pin: + +```rust +use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay}; + +// Create the SPI from the HAL. This implements SpiBus, not SpiDevice! +let spi_bus = my_hal::spi::Spi::new(...); +// Create the CS. This must implement OutputPin. +let cs = my_hal::gpio::Output::new(...); + +// Combine the SPI bus and the CS pin into a SPI device. This now does implement SpiDevice! +let spi_device = ExclusiveDevice::new(spi_bus, cs, NoDelay); + +// Now you can create your driver with it! +let driver = my_driver::Driver::new(spi_device, ...); +``` + +If you want multiple drivers to share the same SPI bus, [`embedded_hal_bus::spi`](https://docs.rs/embedded-hal-bus/0.1.0/embedded_hal_bus/spi/index.html) +has a few options depending on the kind of mutex you want to use. This is now built-in to `embedded-hal`, using external crates like `shared-bus` is discouraged. + +For example, you can use `RefCellDevice` when you don't need drivers to be `Send`. + +```rust +use core::cell::RefCell; +use embedded_hal_bus::spi::{RefCellDevice, NoDelay}; + +// Create the SPI bus and CS pins. +let spi_bus = my_hal::spi::Spi::new(...); +let cs1 = my_hal::gpio::Output::new(...); +let cs2 = my_hal::gpio::Output::new(...); + +// Wrap the bus with a RefCell. +let spi_bus = RefCell::new(spi_bus); + +// Combine references to the SPI bus with a CS pin to get a SpiDevice for one device on the bus. +let device1 = RefCellDevice::new(&spi_bus, cs1, NoDelay); +let device2 = RefCellDevice::new(&spi_bus, cs2, NoDelay); + +// Now you can create drivers. They will transparently talk each to its own device, sharing the same bus. +let driver1 = my_driver::Driver::new(device1, ...); +let driver2 = my_driver::Driver::new(device2, ...); +``` ## Fallibility @@ -199,7 +291,7 @@ pub enum MyError { } ``` -Additionally, for the I2C, SPI and Serial communication interfaces we have added a dedicated mechanism +Additionally, for the I2C and SPI communication interfaces we have added a dedicated mechanism which allows for two crucial requirements: 1. Generic code like drivers can interpret and act on errors if they want to. 2. HAL implementations can have arbitrarily-precise error types. @@ -247,12 +339,6 @@ from the compiler should already tell you how it should look like in your case) to limit the scope of the trait imports and thus avoid the ambiguity. Please note that it is also possible to import traits *inside a function*. -## `rng` module - -The `rng` module and its traits have been removed in favor of the [`rand_core`] traits. - -[`rand_core`]: https://crates.io/crates/rand_core - ## Removed blanket implementations There were several blanket implementations of blocking traits using the non-blocking @@ -261,9 +347,7 @@ traits as a base. Since the non-blocking traits have been extracted into the separate crate `embedded-hal-nb`, these have been removed. - - -## Features +## Cargo features The `unproven` feature has been removed and the traits have been marked as proven. In the past, managing unproven features, and having "sort of breaking" changes has been a struggling point. @@ -275,19 +359,16 @@ experiment externally, and merge when some kind of feasibility had been proven. ## Companion crates -## Use-case-specific help - -### For driver authors - -### I2C traits - - -#### SPI traits - - -### For HAL authors - -TODO +The `embedded-hal` project now spans several crates, where some functionality has been moved out from the main `embedded-hal` crate to separate crates as detailed above. Here is the full listing of crates: +| Crate | crates.io | Docs | | +|-|-|-|-| +| [embedded-hal](./embedded-hal) | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) | Core traits, blocking version | +| [embedded-hal-async](./embedded-hal-async) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) | Core traits, async version | +| [embedded-hal-nb](./embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | +| [embedded-hal-bus](./embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | +| [embedded-can](./embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | +| [embedded-io](./embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | +| [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | +| [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | -[MeetingSummary]: https://hackmd.io/ck-xRXtMTmKYXdK5bEh82A From c5a1df0f3de205ac02c0ec5b15a9e5a303288f8f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 25 Dec 2023 00:34:50 +0100 Subject: [PATCH 102/201] README fixes. --- embedded-hal/README.md | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 391599ec5..79667661e 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -9,13 +9,6 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -**NOTE** This HAL is still in active development. Expect the traits presented here to be -tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 -release. - -**NOTE** If you want to use an alpha release of the 1.0.0 version, use an exact version -specifier in your `Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"`. - ## Companion crates The main `embedded-hal` crate contains only blocking traits, where the operation is done @@ -30,6 +23,14 @@ SPI and I2C buses. Additionally, more domain-specific traits are available in separate crates: - [`embedded-can`](https://docs.rs/embedded-can): Controller Area Network (CAN) +- [`embedded-io`](https://docs.rs/embedded-io): I/O byte streams (like `std::io`, but `no-std`-compatible). + +## Serial/UART traits + +There is no serial traits in `embedded-hal`. Instead, use [`embedded-io`](https://crates.io/crates/embedded-io). +A serial port is essentially a byte-oriented stream, and that's what `embedded-io` models. Sharing the traits +with all byte streams has some advantages. For example, it allows generic code providing a command-line interface +or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. ## Design goals @@ -77,13 +78,6 @@ If you are writing a platform-agnostic driver yourself you are highly encouraged embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) to your crate before publishing it! -## Serial/UART traits - -There is no serial traits in `embedded-hal`. Instead, use [`embedded-io`](https://crates.io/crates/embedded-io). -A serial port is essentially a byte-oriented stream, and that's what `embedded-io` models. Sharing the traits -with all byte streams has some advantages. For example, it allows generic code providing a command-line interface -or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. - ## Optional Cargo features - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. From 009ccf8cb33041cda99ede640d21994ffdc15634 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Wed, 27 Dec 2023 04:22:52 +0000 Subject: [PATCH 103/201] Fix a couple of typos in migration guide --- MIGRATING-0.2-1.0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index e980e046d..4401ca90b 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -120,7 +120,7 @@ and we can add them back in a future 1.x release. ### Impractical traits The [`digital::IoPin` trait][iopin] and the [`adc` traits][adc] have been deemed impractical for use and have thus been removed. -Please feel free to comment on the appropriate issue if you need any of these traitsk and propose a solution. +Please feel free to comment on the appropriate issue if you need any of these traits and propose a solution. ### Serial traits @@ -158,7 +158,7 @@ When upgrading code to `embedded-hal` 1.0, it is critical to implement/use the r - Optionally, if the API *does* manage a CS pin automatically, you may implement `SpiDevice`. - This is required if the underlying API requires it to manage the CS pin, like `spidev` on Linux. -Do not implement `SpiBus` and `SpiDevice` on the same struct, since this is never correct. When there's no CS pin being controlled you must implement only `SpiBus`, and when there is, implement only `SpiDevice`. If you want to offer both APIs, implement them on separate structs so the user has to cohose one or the other. +Do not implement `SpiBus` and `SpiDevice` on the same struct, since this is never correct. When there's no CS pin being controlled you must implement only `SpiBus`, and when there is, implement only `SpiDevice`. If you want to offer both APIs, implement them on separate structs so the user has to choose one or the other. ### For driver crates @@ -335,7 +335,7 @@ the same across traits. To overcome this, please import the traits you wish to use individually. If you run into ambiguous method calls, you can disambiguate using the fully-qualified syntax (the error message -from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code +from the compiler should already tell you how it should look in your case) or tweak your trait imports or code to limit the scope of the trait imports and thus avoid the ambiguity. Please note that it is also possible to import traits *inside a function*. From c9b953a48e4f5e548adc06bcf1aafb34a07893c9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 14:41:30 +0100 Subject: [PATCH 104/201] i2c: disallow returning before transfer is finished. Fixes #455. --- embedded-hal/src/i2c.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index c2a1efb3a..634d52248 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -38,6 +38,15 @@ //! you've received from a HAL and "split" it into multiple shared ones, to instantiate //! several drivers on the same bus. //! +//! # Flushing +//! +//! Implementations must flush the transfer, ensuring the bus has returned to an idle state before returning. +//! No pipelining is allowed. Users must be able to shut down the I2C peripheral immediately after a transfer +//! returns, without any risk of e.g. cutting short a stop condition. +//! +//! (Implementations must wait until the last ACK bit to report it as an error anyway. Therefore pipelining would only +//! yield very small time savings, not worth the complexity) +//! //! # For driver authors //! //! Drivers can select the adequate address length with `I2c` or `I2c` depending From 101c6f9225f80c3eda21a99668e53f90da3a379d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 14:51:38 +0100 Subject: [PATCH 105/201] bus/spi: add semver risk warning to `new_no_delay`. Fixes #552. --- embedded-hal-bus/src/spi/critical_section.rs | 10 ++++++++++ embedded-hal-bus/src/spi/exclusive.rs | 10 ++++++++++ embedded-hal-bus/src/spi/mutex.rs | 10 ++++++++++ embedded-hal-bus/src/spi/refcell.rs | 10 ++++++++++ 4 files changed, 40 insertions(+) diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 7c87eeaaa..838be4f5a 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -34,6 +34,16 @@ impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`CriticalSectionDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 3717844c1..f28a10af4 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -45,6 +45,16 @@ impl ExclusiveDevice { impl ExclusiveDevice { /// Create a new [`ExclusiveDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 3167c72d3..b06f3fd95 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -32,6 +32,16 @@ impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`MutexDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index da8602624..382591c34 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -31,6 +31,16 @@ impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`RefCellDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction From ff1f4695be29d7b3b80a79b6b5d41d493661299a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 15:06:17 +0100 Subject: [PATCH 106/201] spi: document `Operation::DelayNs` is not for CS delays. --- embedded-hal/src/spi.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index ca61269e7..508a4a9b1 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -160,6 +160,16 @@ //! why, because these methods can't return before receiving all the read data. However it's still technically possible //! for them to return before the bus is idle. For example, assuming SPI mode 0, the last bit is sampled on the first (rising) edge //! of SCK, at which point a method could return, but the second (falling) SCK edge still has to happen before the bus is idle. +//! +//! # CS-to-clock delays +//! +//! Many chips require a minimum delay between asserting CS and the first SCK edge, and the last SCK edge and deasserting CS. +//! Drivers should *NOT* use [`Operation::DelayNs`] for this, they should instead document that the user should configure the +//! delays when creating the `SpiDevice` instance, same as they have to configure the SPI frequency and mode. This has a few advantages: +//! +//! - Allows implementations that use hardware-managed CS to program the delay in hardware +//! - Allows the end user more flexibility. For example, they can choose to not configure any delay if their MCU is slow +//! enough to "naturally" do the delay (very common if the delay is in the order of nanoseconds). use core::fmt::Debug; From e1472112b215e88230a0e297e05556840103c38d Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Wed, 27 Dec 2023 19:45:00 +0000 Subject: [PATCH 107/201] Fix rustdoc link in doc comment of set_duty_cycle --- embedded-hal/src/pwm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 782c45fff..68a94f54f 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -74,7 +74,9 @@ pub trait SetDutyCycle: ErrorType { /// Set the duty cycle to `duty / max_duty`. /// /// The caller is responsible for ensuring that the duty cycle value is less than or equal to the maximum duty cycle value, - /// as reported by `get_max_duty`. + /// as reported by [`max_duty_cycle`]. + /// + /// [`max_duty_cycle`]: SetDutyCycle::max_duty_cycle fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error>; /// Set the duty cycle to 0%, or always inactive. From 83620fed38de805139e46c3c9d119970619f5eba Mon Sep 17 00:00:00 2001 From: GrantM11235 Date: Thu, 28 Dec 2023 23:09:33 -0600 Subject: [PATCH 108/201] Migration guide typo --- MIGRATING-0.2-1.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 4401ca90b..a2fa8c6f9 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -26,7 +26,7 @@ There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and We understand the significance of `embedded-hal` in the Rust embedded ecosystem and thus intend to release a version that stays compatible for a long time. -The main difference betewen `embedded-hal` 0.2 and 1.0 is the project is now focused +The main difference between `embedded-hal` 0.2 and 1.0 is the project is now focused on a single goal: traits for writing drivers that work on any HAL. In `embedded-hal` 0.2, the traits had dual goals: From 74ca5ae008c87b517f5eea67d4613204e693089d Mon Sep 17 00:00:00 2001 From: Ralf Date: Sat, 30 Dec 2023 19:54:24 +0100 Subject: [PATCH 109/201] Commit b6764ecd replaced closures by operations. Update documentation accordingly. --- embedded-hal-async/src/spi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 38d471d9a..69a46a468 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -53,7 +53,7 @@ pub trait SpiDevice: ErrorType { /// Do a transfer within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer(read, write))`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] #[inline] @@ -64,7 +64,7 @@ pub trait SpiDevice: ErrorType { /// Do an in-place transfer within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer_in_place(buf))`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] #[inline] From 871f473dcf98657c288120ca093cad8c7a8ac518 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 26 Dec 2023 17:09:20 +0100 Subject: [PATCH 110/201] Move migration guide to docs dir. --- MIGRATING-0.2-1.0.md => docs/migrating-from-0.2-to-1.0.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename MIGRATING-0.2-1.0.md => docs/migrating-from-0.2-to-1.0.md (100%) diff --git a/MIGRATING-0.2-1.0.md b/docs/migrating-from-0.2-to-1.0.md similarity index 100% rename from MIGRATING-0.2-1.0.md rename to docs/migrating-from-0.2-to-1.0.md From b7ee113478d8f88dbf9f08ba3b76d9bd7d138f97 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 26 Dec 2023 17:27:23 +0100 Subject: [PATCH 111/201] docs/migration: add advice to impl both versions, add e-h-compat --- docs/migrating-from-0.2-to-1.0.md | 84 +++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/docs/migrating-from-0.2-to-1.0.md b/docs/migrating-from-0.2-to-1.0.md index a2fa8c6f9..5c9709b48 100644 --- a/docs/migrating-from-0.2-to-1.0.md +++ b/docs/migrating-from-0.2-to-1.0.md @@ -19,6 +19,8 @@ - [Removed blanket implementations](#removed-blanket-implementations) - [Cargo Features](#cargo-features) - [Companion crates](#companion-crates) +- [Supporting both 0.2 and 1.0 in the same HAL](#supporting-both-02-and-10-in-the-same-hal) +- [`embedded-hal-compat`](#embedded-hal-compat) ## Overview and reasoning @@ -41,7 +43,7 @@ For `embedded-hal` 1.0, we decided to drop the first goal, targeting only the se - The second goal delivers much more value. Being able to use any driver together with any HAL crate, out of the box, and across the entire Rust Embedded ecosystem, is just plain awesome. This refocusing on drivers is the root cause of many of the changes between `embedded-hal` 0.2 and 1.0: -- [Associated type compatibiilty](#removed-traits) +- [Associated type compatibility](#removed-traits) - [Trait fragmentation](#trait-organization) - [Bus/device separation](#bus-device-separation) - [Fallibility](#fallibility) @@ -91,7 +93,7 @@ These traits have been removed in the 1.0.0 release, with no replacement for now - [`watchdog::Watchdog`][watchdog] Please find a general [roadmap with further guidance here][roadmap-rm-traits] about -whether and how to get these traits back in a future release +whether and how to get these traits back in a future release. If you are a generic driver author and need one of them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. @@ -359,16 +361,76 @@ experiment externally, and merge when some kind of feasibility had been proven. ## Companion crates -The `embedded-hal` project now spans several crates, where some functionality has been moved out from the main `embedded-hal` crate to separate crates as detailed above. Here is the full listing of crates: +The `embedded-hal` project now spans several crates, where some functionality has been moved out from the main `embedded-hal` crate to separate crates as detailed above. + +Different crates are released independently. The main `embedded-hal-*` trait crates have reached 1.0 maturity, others will become 1.0 as time passes. + +Here is the full listing of crates: | Crate | crates.io | Docs | | |-|-|-|-| -| [embedded-hal](./embedded-hal) | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) | Core traits, blocking version | -| [embedded-hal-async](./embedded-hal-async) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) | Core traits, async version | -| [embedded-hal-nb](./embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | -| [embedded-hal-bus](./embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | -| [embedded-can](./embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | -| [embedded-io](./embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | -| [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | -| [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | +| [embedded-hal](../embedded-hal) | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) | Core traits, blocking version | +| [embedded-hal-async](../embedded-hal-async) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) | Core traits, async version | +| [embedded-hal-nb](../embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | +| [embedded-hal-bus](../embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | +| [embedded-can](../embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | +| [embedded-io](../embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | +| [embedded-io-async](../embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | +| [embedded-io-adapters](../embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | + +## Supporting both 0.2 and 1.0 in the same HAL + +It is strongly recommended that HAL implementation crates provide implementations for both the `embedded-hal` v0.2 and v1.0 traits. +This allows users to use drivers using either version seamlessly. + +The way you do it is adding a dependency on both versions in `Cargo.toml` like this: + +```toml +[dependencies] +embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +``` + +This allows you to refer to the v0.2 traits under the `embedded_hal_02` name, and the v1.0 traits under +`embedded_hal_1`. Implement both versions on the same struct. For example, for an input pin: + +```rust +/// The HAL's input pin struct +struct Input {...} + +/// Implement the v0.2 traits on the struct. +impl embedded_hal_02::digital::v2::InputPin for Input { + type Error = Infallible; + + fn is_high(&self) -> Result { + ... + } + + fn is_low(&self) -> Result { + ... + } +} + +/// ... and implement the v1.0 traits on the *same* struct. +impl embedded_hal_1::digital::ErrorType for Input { + type Error = Infallible; +} + +impl embedded_hal_1::digital::InputPin for Input { + fn is_high(&mut self) -> Result { + ... + } + + fn is_low(&mut self) -> Result { + ... + } +} +``` + +## `embedded-hal-compat` + +For HAL implementation crates that haven't been updated yet, [embedded-hal-compat](https://github.com/ryankurte/embedded-hal-compat) +provides shims to support interoperability between `embedded-hal` v0.2 and v1.0. +This allows using a driver requiring v1.0 with a HAL crate implementing only v0.2 or vice-versa, (generally) without alteration. +See the [docs](https://docs.rs/embedded-hal-compat/) for examples. From bf488dfb20809b90d0d743fd57a962a5ee176b66 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 26 Dec 2023 17:28:12 +0100 Subject: [PATCH 112/201] docs: update readmes for v1.0 --- README.md | 19 ++++--------------- docs/version-policy.md | 26 -------------------------- embedded-hal-bus/README.md | 2 +- 3 files changed, 5 insertions(+), 42 deletions(-) delete mode 100644 docs/version-policy.md diff --git a/README.md b/README.md index 3d86509eb..da60c3fed 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). +> [!IMPORTANT] +> 📣 `embedded-hal` v1.0 is now released! Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/), the [API documentation](https://docs.rs/embedded-hal) and the [migration guide](docs/migrating-from-0.2-to-1.0.md). + ## Scope `embedded-hal` serves as a foundation for building an ecosystem of platform-agnostic drivers. @@ -38,24 +41,10 @@ The main `embedded-hal` project is not tied to a specific execution model like | [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | | [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | -## Releases - -At the moment we are working towards a `1.0.0` release (see [#177]). During this process we will -release alpha versions like `1.0.0-alpha.1` and `1.0.0-alpha.2`. -Alpha releases are **not guaranteed** to be compatible with each other. -They are provided as early previews for community testing and preparation for the final release. -If you use an alpha release, we recommend you choose an exact version specification in your -`Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.9"` - -See [this guide](docs/version-policy.md) for a way to implement both an `embedded-hal` `0.2.x` -version and an `-alpha` version side by side in a HAL. - -[#177]: https://github.com/rust-embedded/embedded-hal/issues/177 - ## Documents +- [Migrating from v0.2 to v1.0](docs/migrating-from-0.2-to-1.0.md). - [How-to: add a new trait](docs/how-to-add-a-new-trait.md) -- [Version policy](docs/version-policy.md) - [MSRV](docs/msrv.md) ## Implementations and drivers diff --git a/docs/version-policy.md b/docs/version-policy.md deleted file mode 100644 index 2f4f0e75e..000000000 --- a/docs/version-policy.md +++ /dev/null @@ -1,26 +0,0 @@ -# Version policy - -At the moment we are working towards a `1.0.0` release (see [#177]). During this process we will -release alpha versions like `1.0.0-alpha.1` and `1.0.0-alpha.2`. -Alpha releases are **not guaranteed** to be compatible with each other. -They are provided as early previews for community testing and preparation for the final release. -If you use an alpha release, we recommend you choose an exact version specification in your -`Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"` - -See below for a way to implement both an `embedded-hal` `0.2.x` version and an `-alpha` version -side by side in a HAL. - -[#177]: https://github.com/rust-embedded/embedded-hal/issues/177 - -## Supporting different (alpha and non-alpha) HALs - -[embedded-hal-compat](https://github.com/ryankurte/embedded-hal-compat) provides shims -to support interoperability between the latest `0.2.x` and `1.0.0-alpha.N` HALs, allowing one to use -incompatible HAL components (generally) without alteration. -See the [docs](https://docs.rs/embedded-hal-compat/) for examples. - -It is also possible for HAL implementations to support both the latest `0.2.x` and `1.0.0-alpha.N` versions -side by side, for an example see [LPC8xx HAL](https://github.com/lpc-rs/lpc8xx-hal). - -Note that `embedded-hal` `-alpha` versions are a moving target and _not guaranteed_ to be compatible. -Because of this we only aim to support the latest `-alpha`. diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 3cfae9448..9fc8aaae0 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru To support bus sharing, `embedded-hal` provides the `SpiBus` and `SpiDevice` traits. `SpiBus` represents an entire bus, while `SpiDevice` represents a device on that bus. For further details on these traits, please consult the -[`embedded-hal` documentation](https://docs.rs/embedded-hal/1.0.0-alpha.10/embedded_hal/spi/index.html). +[`embedded-hal` documentation](https://docs.rs/embedded-hal/latest/embedded_hal/spi/index.html). `embedded-hal` trait implementations for microcontrollers should implement the `SpiBus` trait. However, device drivers should use the `SpiDevice` traits, _not the `SpiBus` traits_ if at all possible From 5f89197a3e2152144790d1ebabc83330f3304a79 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 9 Jan 2024 22:11:33 +0100 Subject: [PATCH 113/201] docs/migration: document gpio change to `&mut self`. --- docs/migrating-from-0.2-to-1.0.md | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/docs/migrating-from-0.2-to-1.0.md b/docs/migrating-from-0.2-to-1.0.md index 5c9709b48..5fa5481b1 100644 --- a/docs/migrating-from-0.2-to-1.0.md +++ b/docs/migrating-from-0.2-to-1.0.md @@ -15,6 +15,7 @@ - [Fallibility](#fallibility) - [SPI transfer return type](#spi-transfer-return-type) - [Error type bounds](#error-type-bounds) +- [GPIO traits now require `&mut self`](#gpio-traits-now-require-mut-self) - [Prelude](#prelude) - [Removed blanket implementations](#removed-blanket-implementations) - [Cargo Features](#cargo-features) @@ -330,6 +331,95 @@ fn set_some_parameter(&mut self) -> Result<(), Self::Error> { } ``` +## GPIO traits now require `&mut self` + +Methods on `InputPin` and `State` now take `&mut self` instead of `&self`, to allow implementations to +have mutable state or access exclusive resources. + +**For HAL implementors**: You should not need to do any changes, since `&mut self` is strictly more permissive +for implementations. + +For ease of use, you might want to provide inherent methods that take `&self` if the hardware permits it. In this case, +you might need to do `&*self` to call them from the trait methods. Otherwise Rust will resolve the +method call to the trait method, causing infinite recursion. + +```rust +struct HalPin; + +impl HalPin { + fn is_high(&self) -> bool { + true + } + + fn is_low(&self) -> bool { + true + } +} + +impl InputPin for HalPin { + fn is_high(&mut self) -> Result { + // Needs `&*self` so that the inherent `is_high` is picked. + Ok((&*self).is_high()) + } + + fn is_low(&mut self) -> Result { + Ok((&*self).is_low()) + } +} +``` + +**For driver authors**: If your driver does not need sharing input pins, you should be able to upgrade without any changes. +If you do need to share input pins, the recommended solution is wrapping them with a `RefCell`. + +Note that if you need to share multiple objects, you should prefer using a single `RefCell` wherever possible to reduce RAM +usage. Make an "inner" struct with all the objects that need sharing, and wrap it in a single `RefCell`. Below is an example +skeleton of a keypad driver using row/column multiplexing, sharing multiple `InputPin`s and `OutputPin`s with a single `RefCell`: + +```rust +use core::cell::RefCell; + +use embedded_hal::digital::{ErrorType, InputPin, OutputPin}; + +pub struct Keypad { + inner: RefCell>, +} + +struct KeypadInner { + cols: [O; NCOLS], + rows: [I; NROWS], +} + +pub struct KeypadInput<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> { + inner: &'a RefCell>, + row: usize, + col: usize, +} + +impl<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> ErrorType for KeypadInput<'a, O, I, NCOLS, NROWS> { + type Error = core::convert::Infallible; +} + +impl<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> InputPin for KeypadInput<'a, O, I, NCOLS, NROWS> { + fn is_high(&mut self) -> Result { + Ok(!self.is_low()?) + } + + fn is_low(&mut self) -> Result { + let inner = &mut *self.inner.borrow_mut(); + let row = &mut inner.rows[self.row]; + let col = &mut inner.cols[self.col]; + + // using unwrap for demo purposes, you should propagate errors up instead. + col.set_low().unwrap(); + let out = row.is_low().unwrap(); + col.set_high().unwrap(); + + Ok(out) + } +} +``` + + ## Prelude The prelude has been removed because it could make method calls ambiguous, since the method names are now From 11c8b9e23f220ca8088673f3c7e3863d3cfa4d02 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 15:16:04 +0100 Subject: [PATCH 114/201] Release embedded-hal{,-async,-nb} v1.0.0, embedded-hal-bus v0.1.0 --- .github/workflows/test.yml | 2 +- embedded-hal-async/CHANGELOG.md | 9 +++++++-- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-bus/CHANGELOG.md | 7 ++++++- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-nb/CHANGELOG.md | 7 ++++++- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 7 ++++++- embedded-hal/Cargo.toml | 2 +- 9 files changed, 34 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e1d7c5664..924bbd821 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,7 @@ jobs: - uses: dtolnay/rust-toolchain@1.60 - run: > cargo test - -p embedded-hal:1.0.0-rc.3 + -p embedded-hal:1.0.0 -p embedded-hal-bus -p embedded-hal-nb -p embedded-io diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 314e0ebaa..40fc8b68a 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +No unreleased changes yet. + +## [v1.0.0] - 2023-12-28 + +- Updated `embedded-hal` to version `1.0.0`. ## [v1.0.0-rc.3] - 2023-12-14 @@ -86,7 +90,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0...HEAD +[v1.0.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.3...embedded-hal-async-v1.0.0 [v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...embedded-hal-async-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...embedded-hal-async-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index fe09580fe..4d2679336 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.3" +version = "1.0.0" rust-version = "1.75" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +embedded-hal = { version = "1.0.0", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 8155db960..04de7aa69 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v0.1.0] - 2023-12-28 + +- Updated `embedded-hal` to version `1.0.0`. + ## [v0.1.0-rc.3] - 2023-12-14 - Updated `embedded-hal` to version `1.0.0-rc.3`. @@ -52,7 +56,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...HEAD +[v0.1.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...embedded-hal-bus-v0.1.0 [v0.1.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...embedded-hal-bus-v0.1.0-rc.3 [v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 [v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 7f04bbded..5c89c9704 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-rc.3" +version = "0.1.0" [features] std = [] @@ -20,8 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } -embedded-hal-async = { version = "=1.0.0-rc.3", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "1.0.0", path = "../embedded-hal" } +embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 6ac3be4ed..53ff11796 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v1.0.0] - 2023-12-28 + +- Updated `embedded-hal` to version `1.0.0`. + ## [v1.0.0-rc.3] - 2023-12-14 - Updated `embedded-hal` to version `1.0.0-rc.3`. @@ -44,7 +48,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0...HEAD +[v1.0.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.3...embedded-hal-nb-v1.0.0 [v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...embedded-hal-nb-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.1...embedded-hal-nb-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...embedded-hal-nb-v1.0.0-rc.1 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index d0c006e76..d4575ead0 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-rc.3" +version = "1.0.0" edition = "2021" rust-version = "1.56" @@ -13,7 +13,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +embedded-hal = { version = "1.0.0", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 9d3ac625b..fcbcc41bf 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes yet. + +## [v1.0.0] - 2023-12-28 + - gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. ## [v1.0.0-rc.3] - 2023-12-14 @@ -314,7 +318,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0...HEAD +[v1.0.0]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.3...v1.0.0 [v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index b91f45f44..f67b2527b 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.3" +version = "1.0.0" [features] defmt-03 = ["dep:defmt-03"] From 676ccd8a5d732fba1b507c303923b7c5e63bbfe7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 9 Jan 2024 23:49:56 +0100 Subject: [PATCH 115/201] migration: autoderef workaround still works with just `*self`. --- docs/migrating-from-0.2-to-1.0.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/migrating-from-0.2-to-1.0.md b/docs/migrating-from-0.2-to-1.0.md index 5fa5481b1..b35a7a2d4 100644 --- a/docs/migrating-from-0.2-to-1.0.md +++ b/docs/migrating-from-0.2-to-1.0.md @@ -340,7 +340,7 @@ have mutable state or access exclusive resources. for implementations. For ease of use, you might want to provide inherent methods that take `&self` if the hardware permits it. In this case, -you might need to do `&*self` to call them from the trait methods. Otherwise Rust will resolve the +you might need to do `*self` to call them from the trait methods. Otherwise Rust will resolve the method call to the trait method, causing infinite recursion. ```rust @@ -358,12 +358,12 @@ impl HalPin { impl InputPin for HalPin { fn is_high(&mut self) -> Result { - // Needs `&*self` so that the inherent `is_high` is picked. - Ok((&*self).is_high()) + // Needs `*self` so that the inherent `is_high` is picked. + Ok((*self).is_high()) } fn is_low(&mut self) -> Result { - Ok((&*self).is_low()) + Ok((*self).is_low()) } } ``` From 983a60bc8e6d798cb579f8c9b14149fed4608373 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 9 Jan 2024 23:50:13 +0100 Subject: [PATCH 116/201] Link to announcement blog post and migration guide in changelogs. --- embedded-hal-async/CHANGELOG.md | 2 ++ embedded-hal-nb/CHANGELOG.md | 2 ++ embedded-hal/CHANGELOG.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 40fc8b68a..cfd5ad051 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -11,6 +11,8 @@ No unreleased changes yet. ## [v1.0.0] - 2023-12-28 +Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. + - Updated `embedded-hal` to version `1.0.0`. ## [v1.0.0-rc.3] - 2023-12-14 diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 53ff11796..7aaf47903 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -11,6 +11,8 @@ No unreleased changes ## [v1.0.0] - 2023-12-28 +CheckCheck out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. + - Updated `embedded-hal` to version `1.0.0`. ## [v1.0.0-rc.3] - 2023-12-14 diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index fcbcc41bf..4a1af0e3f 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -11,6 +11,8 @@ No unreleased changes yet. ## [v1.0.0] - 2023-12-28 +Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. + - gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. ## [v1.0.0-rc.3] - 2023-12-14 From ebc756e2e4ec0107f2877c1f032de59dd929cb67 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 10 Jan 2024 08:56:14 +0100 Subject: [PATCH 117/201] Fix typo --- embedded-hal-nb/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 7aaf47903..f31190acb 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -11,7 +11,7 @@ No unreleased changes ## [v1.0.0] - 2023-12-28 -CheckCheck out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. +Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. - Updated `embedded-hal` to version `1.0.0`. From a5ad7bf4f1c048a6750a74abc82395459db04a29 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 17 Jan 2024 01:58:15 +0100 Subject: [PATCH 118/201] Re-export `digital` error types in `embedded-hal-async`. --- embedded-hal-async/src/digital.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 4fb3843cb..5efe3d180 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -15,9 +15,10 @@ //! .expect("failed to await input pin") //! } //! ``` +pub use embedded_hal::digital::{Error, ErrorKind, ErrorType}; /// Asynchronously wait for GPIO pin state. -pub trait Wait: embedded_hal::digital::ErrorType { +pub trait Wait: ErrorType { /// Wait until the pin is high. If it is already high, return immediately. /// /// # Note for implementers From db179e98faa91e1a4df7c84cf519cd741d4630f7 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 17 Jan 2024 02:02:57 +0100 Subject: [PATCH 119/201] Merge `i2c` re-exports. --- embedded-hal-async/src/i2c.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 1a8f07a48..445cd9e6b 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -16,9 +16,9 @@ //! Since 7-bit addressing is the mode of the majority of I2C devices, //! `SevenBitAddress` has been set as default mode and thus can be omitted if desired. -pub use embedded_hal::i2c::Operation; pub use embedded_hal::i2c::{ - AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, SevenBitAddress, TenBitAddress, + AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, Operation, SevenBitAddress, + TenBitAddress, }; /// Async I2c. From aebcf0f785ef7ce667e37768edfe6f06e22d6fab Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Sun, 4 Feb 2024 20:43:58 +0000 Subject: [PATCH 120/201] Implement Display and std::error::Error for DeviceError if possible. --- embedded-hal-bus/README.md | 3 ++- embedded-hal-bus/src/spi/mod.rs | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 9fc8aaae0..ce71eec92 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -30,7 +30,8 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Optional Cargo features -- **`std`**: enable shared bus implementations using `std::sync::Mutex`. +- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement + `std::error::Error` for `DeviceError`. - **`async`**: enable `embedded-hal-async` support. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 8b51242d8..d408bd92f 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -1,6 +1,6 @@ //! `SpiDevice` implementations. -use core::fmt::Debug; +use core::fmt::{self, Debug, Display, Formatter}; use embedded_hal::spi::{Error, ErrorKind}; mod exclusive; @@ -29,6 +29,18 @@ pub enum DeviceError { Cs(CS), } +impl Display for DeviceError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Spi(bus) => write!(f, "SPI bus error: {}", bus), + Self::Cs(cs) => write!(f, "SPI CS error: {}", cs), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DeviceError {} + impl Error for DeviceError where BUS: Error + Debug, From 8f0e5493e7bffc116674dc03faf827807dbc2b53 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 16 Apr 2024 20:33:32 +0000 Subject: [PATCH 121/201] Use rust 1.75 in msrv-1-75 CI job --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 924bbd821..12f48a133 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,5 +56,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@1.75 - run: cargo test --workspace --all-features From 22a04ceeb2e16342c4c55c2aca285f70115b1c28 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 16 Apr 2024 20:36:38 +0000 Subject: [PATCH 122/201] Use stable toolchain for tests --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12f48a133..65a228d62 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,21 +15,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace test-all-features: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace --all-features build-nostd: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@stable with: target: thumbv7m-none-eabi - run: > From fdc6f3e71df697292e2504dc7fa063a694d628d0 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 16 Apr 2024 18:46:18 +0000 Subject: [PATCH 123/201] Add license file to the top-level directory. These files are referenced from README.md, so they should be present. The same files are already available in the individual crates, so another possibility would be linking to one of the subdirectories. I think it's preferable to have them at the top-level for better visibility. Fixes #588 --- LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++++ 2 files changed, 226 insertions(+) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..52cb453f2 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017-2018 Jorge Aparicio + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From 67eef7fad8994a3d39ae9519a143c92a5b530f75 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 21:22:20 +0200 Subject: [PATCH 124/201] Fix "imported redundantly" warning. --- embedded-hal/src/digital.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index afcf03963..201cd7637 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -1,6 +1,6 @@ //! Digital I/O. -use core::{convert::From, ops::Not}; +use core::ops::Not; #[cfg(feature = "defmt-03")] use crate::defmt; From e00ffa01c269254006aa0770250c033f2e025d51 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 10:37:30 +0100 Subject: [PATCH 125/201] Adding AtomicDevice for I2C bus sharing --- embedded-hal-bus/CHANGELOG.md | 3 +- embedded-hal-bus/Cargo.toml | 1 + embedded-hal-bus/src/i2c/atomic.rs | 170 +++++++++++++++++++++++++++++ embedded-hal-bus/src/i2c/mod.rs | 2 + embedded-hal-bus/src/spi/atomic.rs | 129 ++++++++++++++++++++++ embedded-hal-bus/src/spi/mod.rs | 2 + 6 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 embedded-hal-bus/src/i2c/atomic.rs create mode 100644 embedded-hal-bus/src/spi/atomic.rs diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 04de7aa69..55013fc08 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +### Added +- Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. ## [v0.1.0] - 2023-12-28 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 5c89c9704..cec1945a8 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -24,6 +24,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } +portable-atomic = {version = "1", default-features = false} [package.metadata.docs.rs] features = ["std", "async"] diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs new file mode 100644 index 000000000..c6905c886 --- /dev/null +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -0,0 +1,170 @@ +use core::cell::UnsafeCell; +use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; + +/// `UnsafeCell`-based shared bus [`I2c`] implementation. +/// +/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to [`crate::i2c::RefCellDevice`] instances, but they are `Send`. +/// so it only allows sharing across multiple threads (interrupt priority levels). When attempting +/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules, such as the RTIC framework. +/// +/// # Examples +/// +/// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor +/// with address `0x20`; [`AtomicDevice`] can be used to give access to both of these sensors +/// from a single `i2c` instance. +/// +/// ``` +/// use embedded_hal_bus::i2c; +/// use core::cell::UnsafeCell; +/// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; +/// # pub struct Sensor { +/// # i2c: I2C, +/// # address: u8, +/// # } +/// # impl Sensor { +/// # pub fn new(i2c: I2C, address: u8) -> Self { +/// # Self { i2c, address } +/// # } +/// # } +/// # type PressureSensor = Sensor; +/// # type TemperatureSensor = Sensor; +/// # pub struct I2c0; +/// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] +/// # pub enum Error { } +/// # impl hali2c::Error for Error { +/// # fn kind(&self) -> hali2c::ErrorKind { +/// # ErrorKind::Other +/// # } +/// # } +/// # impl hali2c::ErrorType for I2c0 { +/// # type Error = Error; +/// # } +/// # impl I2c for I2c0 { +/// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { +/// # Ok(()) +/// # } +/// # } +/// # struct Hal; +/// # impl Hal { +/// # fn i2c(&self) -> I2c0 { +/// # I2c0 +/// # } +/// # } +/// # let hal = Hal; +/// +/// let i2c = hal.i2c(); +/// let i2c_unsafe_cell = UnsafeCell::new(i2c); +/// let mut temperature_sensor = TemperatureSensor::new( +/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// 0x20, +/// ); +/// let mut pressure_sensor = PressureSensor::new( +/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// 0x42, +/// ); +/// ``` +pub struct AtomicDevice<'a, T> { + bus: &'a UnsafeCell, + busy: portable_atomic::AtomicBool, +} + +#[derive(Debug, Copy, Clone)] +/// Wrapper type for errors originating from the atomically-checked I2C bus manager. +pub enum AtomicError { + /// This error is returned if the I2C bus was already in use when an operation was attempted, + /// which indicates that the driver requirements are not being met with regard to + /// synchronization. + Busy, + + /// An I2C-related error occurred, and the internal error should be inspected. + Other(T), +} + +impl Error for AtomicError { + fn kind(&self) -> ErrorKind { + match self { + AtomicError::Other(e) => e.kind(), + _ => ErrorKind::Other, + } + } +} + +unsafe impl<'a, T> Send for AtomicDevice<'a, T> {} + +impl<'a, T> AtomicDevice<'a, T> +where + T: I2c, +{ + /// Create a new `AtomicDevice`. + #[inline] + pub fn new(bus: &'a UnsafeCell) -> Self { + Self { + bus, + busy: portable_atomic::AtomicBool::from(false), + } + } + + fn lock(&self, f: F) -> Result> + where + F: FnOnce(&mut T) -> Result::Error>, + { + self.busy + .compare_exchange( + false, + true, + core::sync::atomic::Ordering::SeqCst, + core::sync::atomic::Ordering::SeqCst, + ) + .map_err(|_| AtomicError::::Busy)?; + + let result = f(unsafe { &mut *self.bus.get() }); + + self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + + result.map_err(AtomicError::Other) + } +} + +impl<'a, T> ErrorType for AtomicDevice<'a, T> +where + T: I2c, +{ + type Error = AtomicError; +} + +impl<'a, T> I2c for AtomicDevice<'a, T> +where + T: I2c, +{ + #[inline] + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.lock(|bus| bus.read(address, read)) + } + + #[inline] + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.lock(|bus| bus.write(address, write)) + } + + #[inline] + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + self.lock(|bus| bus.write_read(address, write, read)) + } + + #[inline] + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.lock(|bus| bus.transaction(address, operations)) + } +} diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 8eaf2882f..5295492f1 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,3 +8,5 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; +mod atomic; +pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs new file mode 100644 index 000000000..c5db18f09 --- /dev/null +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -0,0 +1,129 @@ +use core::cell::UnsafeCell; +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{Error, ErrorKind, ErrorType, Operation, SpiBus, SpiDevice}; + +use super::DeviceError; +use crate::spi::shared::transaction; + +/// `UnsafeCell`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, and, unlike [`crate::spi::RefCellDevice`], instances are `Send`, +/// so it only allows sharing across multiple threads (interrupt priority level). When attempting +/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules, such as the RTIC framework. +/// +pub struct AtomicDevice<'a, BUS, CS, D> { + bus: &'a UnsafeCell, + cs: CS, + delay: D, + busy: portable_atomic::AtomicBool, +} + +#[derive(Debug, Copy, Clone)] +/// Wrapper type for errors originating from the atomically-checked SPI bus manager. +pub enum AtomicError { + /// This error is returned if the SPI bus was already in use when an operation was attempted, + /// which indicates that the driver requirements are not being met with regard to + /// synchronization. + Busy, + + /// An SPI-related error occurred, and the internal error should be inspected. + Other(T), +} + +impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { + /// Create a new [`AtomicDevice`]. + #[inline] + pub fn new(bus: &'a UnsafeCell, cs: CS, delay: D) -> Self { + Self { + bus, + cs, + delay, + busy: portable_atomic::AtomicBool::from(false), + } + } +} + +impl<'a, BUS, CS> AtomicDevice<'a, BUS, CS, super::NoDelay> +where + BUS: ErrorType, + CS: OutputPin, +{ + /// Create a new [`AtomicDevice`] without support for in-transaction delays. + /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type [`Operation::DelayNs`]. + #[inline] + pub fn new_no_delay(bus: &'a UnsafeCell, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + busy: portable_atomic::AtomicBool::from(false), + } + } +} + +unsafe impl<'a, BUS, CS, D> Send for AtomicDevice<'a, BUS, CS, D> {} + +impl Error for AtomicError { + fn kind(&self) -> ErrorKind { + match self { + AtomicError::Other(e) => e.kind(), + _ => ErrorKind::Other, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for AtomicDevice<'a, BUS, CS, D> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = AtomicError>; +} + +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for AtomicDevice<'a, BUS, CS, D> +where + BUS: SpiBus, + CS: OutputPin, + D: DelayNs, +{ + #[inline] + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.busy + .compare_exchange( + false, + true, + core::sync::atomic::Ordering::SeqCst, + core::sync::atomic::Ordering::SeqCst, + ) + .map_err(|_| AtomicError::Busy)?; + + let bus = unsafe { &mut *self.bus.get() }; + + let result = transaction(operations, bus, &mut self.delay, &mut self.cs); + + self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + + result.map_err(AtomicError::Other) + } +} diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d408bd92f..d55fa5eaf 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,8 +11,10 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; +mod atomic; mod critical_section; mod shared; +pub use atomic::*; pub use self::critical_section::*; From 7dbaf6d4799459bed6fece61ef2b0bd167b7c904 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 21:23:26 +0200 Subject: [PATCH 126/201] bus: make AtomicDevice use a per-bus "busy" flag, not per-device. Needed for soundness. --- embedded-hal-bus/src/i2c/atomic.rs | 30 +++++++++++++++--------------- embedded-hal-bus/src/lib.rs | 1 + embedded-hal-bus/src/spi/atomic.rs | 28 +++++++++++----------------- embedded-hal-bus/src/util.rs | 25 +++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 embedded-hal-bus/src/util.rs diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index c6905c886..18264a7dd 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -1,6 +1,7 @@ -use core::cell::UnsafeCell; use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; +use crate::util::AtomicCell; + /// `UnsafeCell`-based shared bus [`I2c`] implementation. /// /// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to [`crate::i2c::RefCellDevice`] instances, but they are `Send`. @@ -18,7 +19,7 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// /// ``` /// use embedded_hal_bus::i2c; -/// use core::cell::UnsafeCell; +/// use embedded_hal_bus::util::AtomicCell; /// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; /// # pub struct Sensor { /// # i2c: I2C, @@ -56,19 +57,18 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// # let hal = Hal; /// /// let i2c = hal.i2c(); -/// let i2c_unsafe_cell = UnsafeCell::new(i2c); +/// let i2c_cell = AtomicCell::new(i2c); /// let mut temperature_sensor = TemperatureSensor::new( -/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// i2c::AtomicDevice::new(&i2c_cell), /// 0x20, /// ); /// let mut pressure_sensor = PressureSensor::new( -/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// i2c::AtomicDevice::new(&i2c_cell), /// 0x42, /// ); /// ``` pub struct AtomicDevice<'a, T> { - bus: &'a UnsafeCell, - busy: portable_atomic::AtomicBool, + bus: &'a AtomicCell, } #[derive(Debug, Copy, Clone)] @@ -100,18 +100,16 @@ where { /// Create a new `AtomicDevice`. #[inline] - pub fn new(bus: &'a UnsafeCell) -> Self { - Self { - bus, - busy: portable_atomic::AtomicBool::from(false), - } + pub fn new(bus: &'a AtomicCell) -> Self { + Self { bus } } fn lock(&self, f: F) -> Result> where F: FnOnce(&mut T) -> Result::Error>, { - self.busy + self.bus + .busy .compare_exchange( false, true, @@ -120,9 +118,11 @@ where ) .map_err(|_| AtomicError::::Busy)?; - let result = f(unsafe { &mut *self.bus.get() }); + let result = f(unsafe { &mut *self.bus.bus.get() }); - self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + self.bus + .busy + .store(false, core::sync::atomic::Ordering::SeqCst); result.map_err(AtomicError::Other) } diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index e7e75ec76..396043a31 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -18,3 +18,4 @@ use defmt_03 as defmt; pub mod i2c; pub mod spi; +pub mod util; diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index c5db18f09..08666dfed 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -1,10 +1,10 @@ -use core::cell::UnsafeCell; use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{Error, ErrorKind, ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; use crate::spi::shared::transaction; +use crate::util::AtomicCell; /// `UnsafeCell`-based shared bus [`SpiDevice`] implementation. /// @@ -19,10 +19,9 @@ use crate::spi::shared::transaction; /// rules, such as the RTIC framework. /// pub struct AtomicDevice<'a, BUS, CS, D> { - bus: &'a UnsafeCell, + bus: &'a AtomicCell, cs: CS, delay: D, - busy: portable_atomic::AtomicBool, } #[derive(Debug, Copy, Clone)] @@ -40,13 +39,8 @@ pub enum AtomicError { impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { /// Create a new [`AtomicDevice`]. #[inline] - pub fn new(bus: &'a UnsafeCell, cs: CS, delay: D) -> Self { - Self { - bus, - cs, - delay, - busy: portable_atomic::AtomicBool::from(false), - } + pub fn new(bus: &'a AtomicCell, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } @@ -72,18 +66,15 @@ where /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a UnsafeCell, cs: CS) -> Self { + pub fn new_no_delay(bus: &'a AtomicCell, cs: CS) -> Self { Self { bus, cs, delay: super::NoDelay, - busy: portable_atomic::AtomicBool::from(false), } } } -unsafe impl<'a, BUS, CS, D> Send for AtomicDevice<'a, BUS, CS, D> {} - impl Error for AtomicError { fn kind(&self) -> ErrorKind { match self { @@ -109,7 +100,8 @@ where { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.busy + self.bus + .busy .compare_exchange( false, true, @@ -118,11 +110,13 @@ where ) .map_err(|_| AtomicError::Busy)?; - let bus = unsafe { &mut *self.bus.get() }; + let bus = unsafe { &mut *self.bus.bus.get() }; let result = transaction(operations, bus, &mut self.delay, &mut self.cs); - self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + self.bus + .busy + .store(false, core::sync::atomic::Ordering::SeqCst); result.map_err(AtomicError::Other) } diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs new file mode 100644 index 000000000..739f4b1b3 --- /dev/null +++ b/embedded-hal-bus/src/util.rs @@ -0,0 +1,25 @@ +//! Utilities shared by all bus types. + +use core::cell::UnsafeCell; + +/// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). +/// +/// To use `AtomicDevice`, you must wrap the bus with this struct, and then +/// construct multiple `AtomicDevice` instances with references to it. +pub struct AtomicCell { + pub(crate) bus: UnsafeCell, + pub(crate) busy: portable_atomic::AtomicBool, +} + +unsafe impl Send for AtomicCell {} +unsafe impl Sync for AtomicCell {} + +impl AtomicCell { + /// Create a new `AtomicCell` + pub fn new(bus: BUS) -> Self { + Self { + bus: UnsafeCell::new(bus), + busy: portable_atomic::AtomicBool::from(false), + } + } +} From e4b8c779425f1008f8dd44c5786aee90c5a9e7fb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 21:40:25 +0200 Subject: [PATCH 127/201] bus: expand docs for AtomicDevice a bit. --- embedded-hal-bus/src/i2c/atomic.rs | 19 ++++++++++++++----- embedded-hal-bus/src/spi/atomic.rs | 22 +++++++++++++++------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 18264a7dd..793e827b1 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -2,14 +2,23 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; use crate::util::AtomicCell; -/// `UnsafeCell`-based shared bus [`I2c`] implementation. +/// Atomics-based shared bus [`I2c`] implementation. /// -/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to [`crate::i2c::RefCellDevice`] instances, but they are `Send`. -/// so it only allows sharing across multiple threads (interrupt priority levels). When attempting -/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// Sharing is implemented with a [`AtomicDevice`], which consists of an `UnsafeCell` and an `AtomicBool` "locked" flag. +/// This means it has low overhead, like [`RefCellDevice`](crate::i2c::RefCellDevice). Aditionally, it is `Send`, +/// which allows sharing a single bus across multiple threads (interrupt priority level), like [`CriticalSectionDevice`](crate::i2c::CriticalSectionDevice), +/// while not using critical sections and therefore impacting real-time performance less. +/// +/// The downside is using a simple `AtomicBool` for locking doesn't prevent two threads from trying to lock it at once. +/// For example, the main thread can be doing an I2C transaction, and an interrupt fires and tries to do another. In this +/// case, a `Busy` error is returned that must be handled somehow, usually dropping the data or trying again later. +/// +/// Note that retrying in a loop on `Busy` errors usually leads to deadlocks. In the above example, it'll prevent the +/// interrupt handler from returning, which will starve the main thread and prevent it from releasing the lock. If +/// this is an issue for your use case, you most likely should use [`CriticalSectionDevice`](crate::i2c::CriticalSectionDevice) instead. /// /// This primitive is particularly well-suited for applications that have external arbitration -/// rules, such as the RTIC framework. +/// rules that prevent `Busy` errors in the first place, such as the RTIC framework. /// /// # Examples /// diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index 08666dfed..e97ed8ae0 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -6,18 +6,26 @@ use super::DeviceError; use crate::spi::shared::transaction; use crate::util::AtomicCell; -/// `UnsafeCell`-based shared bus [`SpiDevice`] implementation. +/// Atomics-based shared bus [`SpiDevice`] implementation. /// /// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// -/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, and, unlike [`crate::spi::RefCellDevice`], instances are `Send`, -/// so it only allows sharing across multiple threads (interrupt priority level). When attempting -/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// Sharing is implemented with a [`AtomicDevice`], which consists of an `UnsafeCell` and an `AtomicBool` "locked" flag. +/// This means it has low overhead, like [`RefCellDevice`](crate::spi::RefCellDevice). Aditionally, it is `Send`, +/// which allows sharing a single bus across multiple threads (interrupt priority level), like [`CriticalSectionDevice`](crate::spi::CriticalSectionDevice), +/// while not using critical sections and therefore impacting real-time performance less. /// -/// This primitive is particularly well-suited for applications that have external arbitration -/// rules, such as the RTIC framework. +/// The downside is using a simple `AtomicBool` for locking doesn't prevent two threads from trying to lock it at once. +/// For example, the main thread can be doing a SPI transaction, and an interrupt fires and tries to do another. In this +/// case, a `Busy` error is returned that must be handled somehow, usually dropping the data or trying again later. +/// +/// Note that retrying in a loop on `Busy` errors usually leads to deadlocks. In the above example, it'll prevent the +/// interrupt handler from returning, which will starve the main thread and prevent it from releasing the lock. If +/// this is an issue for your use case, you most likely should use [`CriticalSectionDevice`](crate::spi::CriticalSectionDevice) instead. /// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules that prevent `Busy` errors in the first place, such as the RTIC framework. pub struct AtomicDevice<'a, BUS, CS, D> { bus: &'a AtomicCell, cs: CS, @@ -25,7 +33,7 @@ pub struct AtomicDevice<'a, BUS, CS, D> { } #[derive(Debug, Copy, Clone)] -/// Wrapper type for errors originating from the atomically-checked SPI bus manager. +/// Wrapper type for errors returned by [`AtomicDevice`]. pub enum AtomicError { /// This error is returned if the SPI bus was already in use when an operation was attempted, /// which indicates that the driver requirements are not being met with regard to From bae4c27884e99046082d465cf2911e155efe6027 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 20:55:07 +0200 Subject: [PATCH 128/201] bus: make constructors set CS high. This avoids a footgun where a HAL makes OutputPins low by default (or the user sets them to low and doesn't notice). --- embedded-hal-bus/CHANGELOG.md | 2 +- embedded-hal-bus/src/spi/atomic.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/critical_section.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/exclusive.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/mutex.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/refcell.rs | 24 ++++++++++++++++---- 6 files changed, 96 insertions(+), 26 deletions(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 55013fc08..16408934a 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,8 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -### Added - Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. +- SPI shared bus constructors now set `CS` high, to prevent sharing issues if it was low. ## [v0.1.0] - 2023-12-28 diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index e97ed8ae0..7d18e28c5 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -46,9 +46,16 @@ pub enum AtomicError { impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { /// Create a new [`AtomicDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a AtomicCell, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a AtomicCell, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } @@ -59,6 +66,9 @@ where { /// Create a new [`AtomicDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -74,12 +84,16 @@ where /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a AtomicCell, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a AtomicCell, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 838be4f5a..4c3a46eb2 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -25,15 +25,25 @@ pub struct CriticalSectionDevice<'a, BUS, CS, D> { impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { /// Create a new [`CriticalSectionDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a Mutex>, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`CriticalSectionDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -49,12 +59,16 @@ impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a Mutex>, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index f28a10af4..1599ae7ae 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -24,9 +24,16 @@ pub struct ExclusiveDevice { impl ExclusiveDevice { /// Create a new [`ExclusiveDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: BUS, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: BUS, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } /// Returns a reference to the underlying bus object. @@ -45,6 +52,9 @@ impl ExclusiveDevice { impl ExclusiveDevice { /// Create a new [`ExclusiveDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -60,12 +70,16 @@ impl ExclusiveDevice { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: BUS, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: BUS, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index b06f3fd95..83fe85d88 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -23,15 +23,25 @@ pub struct MutexDevice<'a, BUS, CS, D> { impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { /// Create a new [`MutexDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a Mutex, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`MutexDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -47,12 +57,16 @@ impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a Mutex, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 382591c34..35bea03a2 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -22,15 +22,25 @@ pub struct RefCellDevice<'a, BUS, CS, D> { impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { /// Create a new [`RefCellDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a RefCell, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`RefCellDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -46,12 +56,16 @@ impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a RefCell, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } From add71a3824e4fe6ab7e00249929b22864201ffc1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 22:14:23 +0200 Subject: [PATCH 129/201] Release embedded-hal-bus v0.2.0 --- embedded-hal-bus/CHANGELOG.md | 7 ++++++- embedded-hal-bus/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 16408934a..d92532d3f 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +(Add unreleased changes here) + +## [v0.2.0] - 2024-04-23 + - Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. - SPI shared bus constructors now set `CS` high, to prevent sharing issues if it was low. @@ -57,7 +61,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.2.0...HEAD +[v0.2.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...embedded-hal-bus-v0.2.0 [v0.1.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...embedded-hal-bus-v0.1.0 [v0.1.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...embedded-hal-bus-v0.1.0-rc.3 [v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index cec1945a8..2c54bfbd0 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0" +version = "0.2.0" [features] std = [] From 1c42f823f47893b5d5e55a8f27f9325f43fe94a0 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 4 Jun 2024 20:47:24 +0000 Subject: [PATCH 130/201] Update workflow to actions/checkout@v4 This fixes a warning about Node 16 being deprecated --- .github/workflows/clippy.yml | 2 +- .github/workflows/rustdoc.yml | 2 +- .github/workflows/rustfmt.yml | 2 +- .github/workflows/test.yml | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index dcea25c5e..58f813bee 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -10,7 +10,7 @@ jobs: clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: # embedded-hal-async needs nightly. diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index c4b46425e..105d4e58c 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -10,7 +10,7 @@ jobs: clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: nightly-2023-10-14 diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 3d9036e69..6a3522c47 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -11,7 +11,7 @@ jobs: fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: nightly diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65a228d62..46fb03b26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,21 +14,21 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace test-all-features: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace --all-features build-nostd: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: target: thumbv7m-none-eabi @@ -41,7 +41,7 @@ jobs: msrv-1-60: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.60 - run: > cargo test @@ -55,6 +55,6 @@ jobs: msrv-1-75: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.75 - run: cargo test --workspace --all-features From e229494bbf36173d0abde91055b435a4f2c80329 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:37:28 +1000 Subject: [PATCH 131/201] Feature gate embedded-hal-bus atomic impls --- embedded-hal-bus/Cargo.toml | 3 ++- embedded-hal-bus/src/i2c/mod.rs | 2 ++ embedded-hal-bus/src/spi/mod.rs | 2 ++ embedded-hal-bus/src/util.rs | 6 +++++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 2c54bfbd0..3460062e9 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -16,6 +16,7 @@ version = "0.2.0" [features] std = [] +atomic-device = [] async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] @@ -27,5 +28,5 @@ defmt-03 = { package = "defmt", version = "0.3", optional = true } portable-atomic = {version = "1", default-features = false} [package.metadata.docs.rs] -features = ["std", "async"] +features = ["std", "async", "atomic-device"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 5295492f1..5e2c14020 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,5 +8,7 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; +#[cfg(feature = "atomic-device")] mod atomic; +#[cfg(feature = "atomic-device")] pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d55fa5eaf..23cd824d4 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,9 +11,11 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; +#[cfg(feature = "atomic-device")] mod atomic; mod critical_section; mod shared; +#[cfg(feature = "atomic-device")] pub use atomic::*; pub use self::critical_section::*; diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index 739f4b1b3..70dac74e2 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -1,7 +1,9 @@ //! Utilities shared by all bus types. +#[allow(unused_imports)] use core::cell::UnsafeCell; +#[cfg(feature = "atomic-device")] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// /// To use `AtomicDevice`, you must wrap the bus with this struct, and then @@ -10,10 +12,12 @@ pub struct AtomicCell { pub(crate) bus: UnsafeCell, pub(crate) busy: portable_atomic::AtomicBool, } - +#[cfg(feature = "atomic-device")] unsafe impl Send for AtomicCell {} +#[cfg(feature = "atomic-device")] unsafe impl Sync for AtomicCell {} +#[cfg(feature = "atomic-device")] impl AtomicCell { /// Create a new `AtomicCell` pub fn new(bus: BUS) -> Self { From f0eec219bb1ff9a5642e12b8e92dcc98c4d1640c Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:38:28 +1000 Subject: [PATCH 132/201] Add features for selecting portable-atomic features --- embedded-hal-bus/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 3460062e9..44b9a0287 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -19,6 +19,8 @@ std = [] atomic-device = [] async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] +portable-atomic-critical-section = ["atomic-device", "portable-atomic/critical-section"] +portable-atomic-unsafe-assume-single-core = ["atomic-device", "portable-atomic/unsafe-assume-single-core"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } From 797dc9559040df979aab69dc876b1c22f5499fc5 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:47:41 +1000 Subject: [PATCH 133/201] Document embedded-hal-bus features in README.md and Cargo.toml --- embedded-hal-bus/Cargo.toml | 13 +++++++++++++ embedded-hal-bus/README.md | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 44b9a0287..07bff536f 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -15,11 +15,24 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0" [features] +# Enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError` std = [] +# Enable shared bus implementations that require Atomic CAS operations atomic-device = [] +# Enable `embedded-hal-async` support. async = ["dep:embedded-hal-async"] +# Derive `defmt::Format` from `defmt` 0.3 for enums and structs. See https://github.com/knurling-rs/defmt for more info defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] +# Enable critical-section feature in portable-atomic. +# +# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. +# This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. +# A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) portable-atomic-critical-section = ["atomic-device", "portable-atomic/critical-section"] +# Enable unsafe-assume-single-core feature of portable-atomic. +# +# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. +# This feature is only safe on single core systems portable-atomic-unsafe-assume-single-core = ["atomic-device", "portable-atomic/unsafe-assume-single-core"] [dependencies] diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index ce71eec92..4922c7f5f 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -30,10 +30,20 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Optional Cargo features -- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement - `std::error::Error` for `DeviceError`. - **`async`**: enable `embedded-hal-async` support. +- **`atomic-device`**: enable shared bus implementations that require Atomic CAS operations. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. +- **`portable-atomic-critical-section`**: Enable critical-section feature in portable-atomic. + + `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. + This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. + A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) +- **`portable-atomic-unsafe-assume-single-core`**: Enable unsafe-assume-single-core feature of portable-atomic. + + `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. + This feature is only safe on single core systems +- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement + `std::error::Error` for `DeviceError`. ## Minimum Supported Rust Version (MSRV) From d346553c0cc8a0ad6fde4551af8d62a733633770 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 22:03:05 +1000 Subject: [PATCH 134/201] Also enable Atomic* when target_has_atomic=8 --- embedded-hal-bus/src/i2c/mod.rs | 4 ++-- embedded-hal-bus/src/spi/mod.rs | 4 ++-- embedded-hal-bus/src/util.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 5e2c14020..ef8f7ae9c 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,7 +8,7 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] mod atomic; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 23cd824d4..a7ad6b989 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,11 +11,11 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] mod atomic; mod critical_section; mod shared; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] pub use atomic::*; pub use self::critical_section::*; diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index 70dac74e2..f0cb8f16d 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -3,7 +3,7 @@ #[allow(unused_imports)] use core::cell::UnsafeCell; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// /// To use `AtomicDevice`, you must wrap the bus with this struct, and then @@ -12,12 +12,12 @@ pub struct AtomicCell { pub(crate) bus: UnsafeCell, pub(crate) busy: portable_atomic::AtomicBool, } -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] unsafe impl Send for AtomicCell {} -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] unsafe impl Sync for AtomicCell {} -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] impl AtomicCell { /// Create a new `AtomicCell` pub fn new(bus: BUS) -> Self { From 3c41607d948c2b2e6b52ef314f954e945f63d330 Mon Sep 17 00:00:00 2001 From: Liam Kinne Date: Thu, 4 Jul 2024 20:27:05 +1000 Subject: [PATCH 135/201] add feature and Format derive --- embedded-can/Cargo.toml | 4 ++++ embedded-can/README.md | 4 ++++ embedded-can/src/id.rs | 3 +++ embedded-can/src/lib.rs | 1 + 4 files changed, 12 insertions(+) diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 6279fa80d..07ef7cc53 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -14,3 +14,7 @@ repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] nb = "1" +defmt = { version = "0.3", optional = true } + +[features] +defmt-03 = ["dep:defmt"] diff --git a/embedded-can/README.md b/embedded-can/README.md index d0b7d05d9..1beaf39cd 100644 --- a/embedded-can/README.md +++ b/embedded-can/README.md @@ -13,6 +13,10 @@ This project is developed and maintained by the [HAL team](https://github.com/ru [API reference]: https://docs.rs/embedded-can +## Optional features + +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index 2774b6c3d..b0d6d4d5f 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -2,6 +2,7 @@ /// Standard 11-bit CAN Identifier (`0..=0x7FF`). #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct StandardId(u16); impl StandardId { @@ -44,6 +45,7 @@ impl StandardId { /// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct ExtendedId(u32); impl ExtendedId { @@ -93,6 +95,7 @@ impl ExtendedId { /// A CAN Identifier (standard or extended). #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Id { /// Standard 11-bit Identifier (`0..=0x7FF`). Standard(StandardId), diff --git a/embedded-can/src/lib.rs b/embedded-can/src/lib.rs index 2de2b6ebc..da561f58c 100644 --- a/embedded-can/src/lib.rs +++ b/embedded-can/src/lib.rs @@ -73,6 +73,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common CAN errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// The peripheral receive buffer was overrun. From 86c39b3b4663c401a051a115ddeebaa121de0647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 12 Jul 2024 15:06:34 +0200 Subject: [PATCH 136/201] e-io-adapters: add ToFmt: impl fmt::Write --- embedded-io-adapters/CHANGELOG.md | 2 +- embedded-io-adapters/src/fmt.rs | 42 +++++++++++++++++++++++++++++++ embedded-io-adapters/src/lib.rs | 2 ++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 embedded-io-adapters/src/fmt.rs diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index b540b67b4..f427e0aa1 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -Add unreleased changes here +- Added `ToFmt` adapter for `core::fmt::Write`. ## 0.6.1 - 2023-11-28 diff --git a/embedded-io-adapters/src/fmt.rs b/embedded-io-adapters/src/fmt.rs new file mode 100644 index 000000000..7a057668d --- /dev/null +++ b/embedded-io-adapters/src/fmt.rs @@ -0,0 +1,42 @@ +//! Adapters to the `core::fmt::Write`. + +/// Adapter to the `core::fmt::Write` trait. +#[derive(Clone, Default, PartialEq, Debug)] +pub struct ToFmt { + inner: T, +} + +impl ToFmt { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl ToFmt { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl core::fmt::Write for ToFmt { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.inner.write_all(s.as_bytes()).or(Err(core::fmt::Error)) + } + + // Use fmt::Write default impls for + // * write_fmt(): better here than e-io::Write::write_fmt + // since we don't need to bother with saving the Error + // * write_char(): would be the same +} diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index f9d0f1a5c..936546e66 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -15,6 +15,8 @@ feature(async_fn_in_trait, impl_trait_projections) )] +pub mod fmt; + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub mod std; From a9c1ebd2f4f393fca2ebaea356bdae9dcdd8b2ea Mon Sep 17 00:00:00 2001 From: James Munns Date: Sat, 13 Jul 2024 19:21:19 +0200 Subject: [PATCH 137/201] Update minimum `embedded-io` version The 0.6.0 version of `embedded-io` does not have `SliceWriteError` in the public API, leading to this error if the 0.6.0 version is selected: ``` error[E0432]: unresolved import `embedded_io::SliceWriteError` --> /Users/james/.cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-io-async-0.6.1/src/impls/slice_mut.rs:2:5 | 2 | use embedded_io::SliceWriteError; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `SliceWriteError` in the root For more information about this error, try `rustc --explain E0432`. error: could not compile `embedded-io-async` (lib) due to 1 previous error warning: build failed, waiting for other jobs to finish... ``` This means that embedded-io-async requires *at least* embedded-io 0.6. --- embedded-io-async/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 6cd60c9c7..1cf2e0aad 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -18,7 +18,7 @@ alloc = ["embedded-io/alloc"] defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] [dependencies] -embedded-io = { version = "0.6", path = "../embedded-io" } +embedded-io = { version = "0.6.1", path = "../embedded-io" } defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] From a5f6aada222531c842d70e6358ee831861d86148 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Jul 2024 11:57:04 +0200 Subject: [PATCH 138/201] Remove nightly checks. Async fn in traits has been stable for 7 months, it's very unlikely someone is using such old nightlies. Fixes #618 --- embedded-hal-async/build.rs | 18 ------------------ embedded-hal-async/src/lib.rs | 6 ------ embedded-hal-bus/build.rs | 18 ------------------ embedded-hal-bus/src/lib.rs | 9 --------- embedded-io-adapters/build.rs | 18 ------------------ embedded-io-adapters/src/lib.rs | 12 ------------ embedded-io-async/build.rs | 18 ------------------ embedded-io-async/src/lib.rs | 6 ------ 8 files changed, 105 deletions(-) delete mode 100644 embedded-hal-async/build.rs delete mode 100644 embedded-hal-bus/build.rs delete mode 100644 embedded-io-adapters/build.rs delete mode 100644 embedded-io-async/build.rs diff --git a/embedded-hal-async/build.rs b/embedded-hal-async/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-hal-async/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 44901deca..cbd74f5d4 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -1,12 +1,6 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr(nightly, allow(stable_features, unknown_lints))] -#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] pub mod delay; diff --git a/embedded-hal-bus/build.rs b/embedded-hal-bus/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-hal-bus/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 396043a31..dfeca16e1 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -2,15 +2,6 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr(all(feature = "async", nightly), allow(stable_features))] -#![cfg_attr( - all(feature = "async", nightly), - feature(async_fn_in_trait, impl_trait_projections) -)] // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. #[cfg(feature = "defmt-03")] diff --git a/embedded-io-adapters/build.rs b/embedded-io-adapters/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-io-adapters/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index f9d0f1a5c..9d6628836 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -2,18 +2,6 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr( - all(any(feature = "tokio-1", feature = "futures-03"), nightly), - allow(stable_features) -)] -#![cfg_attr( - all(any(feature = "tokio-1", feature = "futures-03"), nightly), - feature(async_fn_in_trait, impl_trait_projections) -)] #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-io-async/build.rs b/embedded-io-async/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-io-async/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 1171d42a0..7c0ac2d55 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -2,12 +2,6 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr(nightly, allow(stable_features, unknown_lints))] -#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] #[cfg(feature = "alloc")] From 91e254d97c7d45b180b52fc57c254b862085fe6e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Jul 2024 12:02:24 +0200 Subject: [PATCH 139/201] Use stable in CI for clippy, rustfmt. Update nightly for rustdoc. --- .github/workflows/clippy.yml | 5 +---- .github/workflows/rustdoc.yml | 2 +- .github/workflows/rustfmt.yml | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 58f813bee..422eab62f 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -11,10 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master + - uses: dtolnay/rust-toolchain@stable with: - # embedded-hal-async needs nightly. - # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) - toolchain: nightly-2023-10-14 components: clippy - run: cargo clippy --all-features -- --deny=warnings diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index 105d4e58c..7829e62b6 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -13,6 +13,6 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2023-10-14 + toolchain: nightly-2024-07-26 # tokio/net required to workaround https://github.com/tokio-rs/tokio/issues/6165 - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features --features tokio/net diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 6a3522c47..129eb2285 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -12,8 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master + - uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly components: rustfmt - run: cargo fmt --check From f593acb2b0d1d57ca983a50053e6d725e8fb7f21 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Jul 2024 12:07:55 +0200 Subject: [PATCH 140/201] Fixes for 1.80 clippy. --- embedded-hal-nb/src/spi.rs | 6 ++--- embedded-hal/README.md | 34 +++++++++++++------------- embedded-io-adapters/src/futures_03.rs | 5 ++++ embedded-io-adapters/src/tokio_1.rs | 5 ++++ embedded-io/README.md | 4 +-- 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/embedded-hal-nb/src/spi.rs b/embedded-hal-nb/src/spi.rs index 06689db4c..75553c78b 100644 --- a/embedded-hal-nb/src/spi.rs +++ b/embedded-hal-nb/src/spi.rs @@ -13,13 +13,13 @@ pub use embedded_hal::spi::{ /// - Due to how full duplex SPI works each `read` call must be preceded by a `write` call. /// /// - `read` calls only return the data received with the last `write` call. -/// Previously received data is discarded +/// Previously received data is discarded /// /// - Data is only guaranteed to be clocked out when the `read` call succeeds. -/// The slave select line shouldn't be released before that. +/// The slave select line shouldn't be released before that. /// /// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different -/// `Word` types to allow operation in both modes. +/// `Word` types to allow operation in both modes. pub trait FullDuplex: ErrorType { /// Reads the word stored in the shift register /// diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 79667661e..e47182b71 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -37,37 +37,37 @@ or a console to operate either on hardware serial ports or on virtual ones like The HAL - Must *erase* device specific details. Neither register, register blocks, nor magic values should -appear in the API. + appear in the API. - Must be generic *within* a device and *across* devices. The API to use a serial interface must -be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a -device or the UART0 peripheral of another device. + be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a + device or the UART0 peripheral of another device. - Where possible must *not* be tied to a specific asynchronous model. The API should be usable -in blocking mode, with the `futures` model, with an async/await model or with a callback model. -(cf. the [`nb`](https://docs.rs/nb) crate) + in blocking mode, with the `futures` model, with an async/await model or with a callback model. + (cf. the [`nb`](https://docs.rs/nb) crate) - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that -want higher level abstraction should *prefer to use this HAL* rather than *re-implement* -register manipulation code. + want higher level abstraction should *prefer to use this HAL* rather than *re-implement* + register manipulation code. - Serve as a foundation for building an ecosystem of platform-agnostic drivers. Here driver -means a library crate that lets a target platform interface an external device like a digital -sensor or a wireless transceiver. The advantage of this system is that by writing the driver as -a generic library on top of `embedded-hal` driver authors can support any number of target -platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The -advantage for application developers is that by adopting `embedded-hal` they can unlock all -these drivers for their platform. + means a library crate that lets a target platform interface an external device like a digital + sensor or a wireless transceiver. The advantage of this system is that by writing the driver as + a generic library on top of `embedded-hal` driver authors can support any number of target + platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The + advantage for application developers is that by adopting `embedded-hal` they can unlock all + these drivers for their platform. - Trait methods must be fallible so that they can be used in any possible situation. -Nevertheless, HAL implementations can additionally provide infallible versions of the same methods -if they can never fail in their platform. This way, generic code can use the fallible abstractions -provided here but platform-specific code can avoid fallibility-related boilerplate if possible. + Nevertheless, HAL implementations can additionally provide infallible versions of the same methods + if they can never fail in their platform. This way, generic code can use the fallible abstractions + provided here but platform-specific code can avoid fallibility-related boilerplate if possible. ## Out of scope - Initialization and configuration stuff like "ensure this serial interface and that SPI -interface are not using the same pins". The HAL will focus on *doing I/O*. + interface are not using the same pins". The HAL will focus on *doing I/O*. ## Platform agnostic drivers diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index a40370a24..d1127a8c6 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -1,5 +1,10 @@ //! Adapters to/from `futures::io` traits. +// MSRV is 1.60 if you don't enable async, 1.80 if you do. +// Cargo.toml has 1.60, which makes Clippy complain that `poll_fn` was introduced +// in 1.64. So, just silence it for this file. +#![allow(clippy::incompatible_msrv)] + use core::future::poll_fn; use core::pin::Pin; diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 418284b93..0dd4d265b 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -1,5 +1,10 @@ //! Adapters to/from `tokio::io` traits. +// MSRV is 1.60 if you don't enable async, 1.80 if you do. +// Cargo.toml has 1.60, which makes Clippy complain that `poll_fn` was introduced +// in 1.64. So, just silence it for this file. +#![allow(clippy::incompatible_msrv)] + use core::future::poll_fn; use core::pin::Pin; use core::task::Poll; diff --git a/embedded-io/README.md b/embedded-io/README.md index d647ffe9d..04ea97771 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -15,9 +15,9 @@ targets. ## Differences with `std::io` - `Error` is an associated type. This allows each implementor to return its own error type, -while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). + while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). - In `std::io`, the `Read`/`Write` traits might be blocking or non-blocking (i.e. returning `WouldBlock` errors) depending on the file descriptor's mode, which is only known at run-time. This allows passing a non-blocking stream to code that expects a blocking -stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. + stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. ## Optional Cargo features From be43383966f562ef42cc9de3126f90f7a51fff68 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 5 Jul 2024 15:46:51 +0200 Subject: [PATCH 141/201] Add a Rc>-based implementation of SpiDevice --- embedded-hal-bus/Cargo.toml | 4 +- embedded-hal-bus/src/spi/mod.rs | 5 ++ embedded-hal-bus/src/spi/rc.rs | 90 +++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 embedded-hal-bus/src/spi/rc.rs diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 2c54bfbd0..6ab831fbe 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -15,9 +15,11 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0" [features] -std = [] +std = ["alloc"] async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] +# Enables additional utilities requiring a global allocator. +alloc = [] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d55fa5eaf..b654a54c6 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -16,6 +16,11 @@ mod critical_section; mod shared; pub use atomic::*; +#[cfg(feature = "alloc")] +mod rc; +#[cfg(feature = "alloc")] +pub use rc::*; + pub use self::critical_section::*; #[cfg(feature = "defmt-03")] diff --git a/embedded-hal-bus/src/spi/rc.rs b/embedded-hal-bus/src/spi/rc.rs new file mode 100644 index 000000000..1d58db37d --- /dev/null +++ b/embedded-hal-bus/src/spi/rc.rs @@ -0,0 +1,90 @@ +extern crate alloc; +use alloc::rc::Rc; + +use core::cell::RefCell; +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; + +use super::DeviceError; +use crate::spi::shared::transaction; + +/// Implementation of [`SpiDevice`] around a bus shared with `Rc>`. +/// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice), requiring allocation. +/// +/// A single [`SpiBus`] is shared via [`RefCell`], and its ownership is handled by [`Rc`]. +/// Both of these mechanisms only allow sharing within a single thread (or interrupt priority level). +/// For this reason, this does not implement [`Send`]. +/// +/// When this structure is dropped, the reference count of the `Bus` will be decremented, +/// and the bus driver will be cleaned up when that count reaches zero. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +pub struct RcDevice { + bus: Rc>, + cs: Cs, + delay: Delay, +} + +impl RcDevice { + /// Creates a new [`RcDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. + /// It is recommended to have already set that pin high the moment it has been configured as an output, to avoid glitches. + /// + /// This function does not increment the reference count: + /// you will need to call `Rc::clone(&bus)` if you only have a `&RefCell`. + #[inline] + pub fn new(bus: Rc>, mut cs: Cs, delay: Delay) -> Result + where + Cs: OutputPin, + { + cs.set_high()?; + + Ok(Self { bus, cs, delay }) + } +} + +impl RcDevice { + /// Creates a new [`RcDevice`] without support for in-transaction delays. + /// + /// **Warning**: It's advised to prefer [`RcDevice::new`], + /// as the contract of [`SpiDevice`] requests support for in-transaction delays. + /// + /// Refer to [`RefCellDevice::new_no_delay`](super::RefCellDevice::new_no_delay) for more information. + #[inline] + pub fn new_no_delay(bus: Rc>, mut cs: Cs) -> Result + where + Cs: OutputPin, + { + cs.set_high()?; + + Ok(Self { + bus, + cs, + delay: super::NoDelay, + }) + } +} + +impl ErrorType for RcDevice +where + Bus: ErrorType, + Cs: OutputPin, +{ + type Error = DeviceError; +} + +impl SpiDevice for RcDevice +where + Word: Copy + 'static, + Bus: SpiBus, + Cs: OutputPin, + Delay: DelayNs, +{ + #[inline] + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + transaction(operations, bus, &mut self.delay, &mut self.cs) + } +} From aaa29c0ec44e1a65fa5b4a34d1deb3a21d0ab3e6 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 5 Jul 2024 17:01:28 +0200 Subject: [PATCH 142/201] Fix small mistake in the documentation of RcDevice::new --- embedded-hal-bus/src/spi/rc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded-hal-bus/src/spi/rc.rs b/embedded-hal-bus/src/spi/rc.rs index 1d58db37d..5a1c558bd 100644 --- a/embedded-hal-bus/src/spi/rc.rs +++ b/embedded-hal-bus/src/spi/rc.rs @@ -16,8 +16,8 @@ use crate::spi::shared::transaction; /// Both of these mechanisms only allow sharing within a single thread (or interrupt priority level). /// For this reason, this does not implement [`Send`]. /// -/// When this structure is dropped, the reference count of the `Bus` will be decremented, -/// and the bus driver will be cleaned up when that count reaches zero. +/// When this structure is dropped, the reference count of the `Bus` instance will be decremented, +/// and it will be cleaned up once the reference count reaches zero. #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] pub struct RcDevice { bus: Rc>, @@ -32,7 +32,7 @@ impl RcDevice { /// It is recommended to have already set that pin high the moment it has been configured as an output, to avoid glitches. /// /// This function does not increment the reference count: - /// you will need to call `Rc::clone(&bus)` if you only have a `&RefCell`. + /// you will need to call `Rc::clone(&bus)` if you only have a `&Rc>`. #[inline] pub fn new(bus: Rc>, mut cs: Cs, delay: Delay) -> Result where From 9c609615afe25e9c7ee05fee7225da53ed309e84 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 5 Jul 2024 17:15:54 +0200 Subject: [PATCH 143/201] Add Rc>-based implementation of shared I2c buses --- embedded-hal-bus/Cargo.toml | 2 + embedded-hal-bus/src/i2c/mod.rs | 5 +++ embedded-hal-bus/src/i2c/rc.rs | 75 +++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 embedded-hal-bus/src/i2c/rc.rs diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 6ab831fbe..836f050b2 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -20,6 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] # Enables additional utilities requiring a global allocator. alloc = [] +# TODO: remove this +default = ["alloc"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 5295492f1..6420d06c7 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -10,3 +10,8 @@ mod critical_section; pub use self::critical_section::*; mod atomic; pub use atomic::*; + +#[cfg(feature = "alloc")] +mod rc; +#[cfg(feature = "alloc")] +pub use rc::*; diff --git a/embedded-hal-bus/src/i2c/rc.rs b/embedded-hal-bus/src/i2c/rc.rs new file mode 100644 index 000000000..2f1621687 --- /dev/null +++ b/embedded-hal-bus/src/i2c/rc.rs @@ -0,0 +1,75 @@ +extern crate alloc; +use alloc::rc::Rc; + +use core::cell::RefCell; +use embedded_hal::i2c::{ErrorType, I2c}; + +/// `Rc>`-based shared bus [`I2c`] implementation. +/// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice). +/// +/// Sharing is implemented with a [`RefCell`] and ownership is managed by [`Rc`]. +/// Like [`RefCellDevice`](super::RefCellDevice), `RcDevice` instances are not [`Send`], +/// so they can only be shared within a single thread (interrupt priority level). +/// +/// When this `RcDevice` is dropped, the reference count of the I2C bus will be decremented. +/// Once that reference count hits zero, it will be cleaned up. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +pub struct RcDevice { + bus: Rc>, +} + +impl RcDevice { + /// Creates a new `RcDevice`. + /// + /// This function does not increment the reference count for the bus: + /// you will need to call `Rc::clone(&bus)` if you only have a `&Rc>`. + #[inline] + pub fn new(bus: Rc>) -> Self { + Self { bus } + } +} + +impl ErrorType for RcDevice +where + Bus: ErrorType, +{ + type Error = Bus::Error; +} + +impl I2c for RcDevice +where + Bus: I2c, +{ + #[inline] + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.read(address, read) + } + + #[inline] + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.write(address, write) + } + + #[inline] + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.write_read(address, write, read) + } + + #[inline] + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.transaction(address, operations) + } +} From 1c02ef9f8644d388cafc46cd391dab6e89e8506b Mon Sep 17 00:00:00 2001 From: Shad Amethyst Date: Fri, 26 Jul 2024 11:45:03 +0200 Subject: [PATCH 144/201] Remove the alloc feature from default for testing Co-authored-by: Diego Barrios Romero --- embedded-hal-bus/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 836f050b2..6ab831fbe 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -20,8 +20,6 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] # Enables additional utilities requiring a global allocator. alloc = [] -# TODO: remove this -default = ["alloc"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } From 21e82bb21d7b02b299299c0bdae493ac27d71c56 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 26 Jul 2024 11:52:08 +0200 Subject: [PATCH 145/201] Document changes and document the `alloc` feature --- embedded-hal-bus/CHANGELOG.md | 3 ++- embedded-hal-bus/README.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index d92532d3f..45d0f6cc8 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -(Add unreleased changes here) +- Added the `alloc` feature. +- Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`. ## [v0.2.0] - 2024-04-23 diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index ce71eec92..2089cb8a6 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -34,6 +34,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins `std::error::Error` for `DeviceError`. - **`async`**: enable `embedded-hal-async` support. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. +- **`alloc`**: enable implementations using `alloc` (for instance, `spi::RcDevice`, which makes use of `alloc::rc::Rc`) ## Minimum Supported Rust Version (MSRV) From 3ddc31a81087b92effcc00cee5f0a0003f73fbd4 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:03:20 +1000 Subject: [PATCH 146/201] Remove features that enable atomic-polyfill flags --- embedded-hal-bus/Cargo.toml | 20 +++++++------------- embedded-hal-bus/README.md | 14 +++++--------- embedded-hal-bus/src/i2c/mod.rs | 4 ++-- embedded-hal-bus/src/spi/mod.rs | 4 ++-- embedded-hal-bus/src/util.rs | 8 ++++---- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 07bff536f..1ef572e9e 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -17,23 +17,17 @@ version = "0.2.0" [features] # Enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError` std = [] -# Enable shared bus implementations that require Atomic CAS operations -atomic-device = [] +# Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS +# +# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware +# that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with +# a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. +# See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. +portable-atomic = [] # Enable `embedded-hal-async` support. async = ["dep:embedded-hal-async"] # Derive `defmt::Format` from `defmt` 0.3 for enums and structs. See https://github.com/knurling-rs/defmt for more info defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] -# Enable critical-section feature in portable-atomic. -# -# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. -# This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. -# A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) -portable-atomic-critical-section = ["atomic-device", "portable-atomic/critical-section"] -# Enable unsafe-assume-single-core feature of portable-atomic. -# -# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. -# This feature is only safe on single core systems -portable-atomic-unsafe-assume-single-core = ["atomic-device", "portable-atomic/unsafe-assume-single-core"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 4922c7f5f..c2d9a77cf 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -31,17 +31,13 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Optional Cargo features - **`async`**: enable `embedded-hal-async` support. -- **`atomic-device`**: enable shared bus implementations that require Atomic CAS operations. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. -- **`portable-atomic-critical-section`**: Enable critical-section feature in portable-atomic. +- **`portable-atomic`**: Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS - `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. - This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. - A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) -- **`portable-atomic-unsafe-assume-single-core`**: Enable unsafe-assume-single-core feature of portable-atomic. - - `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. - This feature is only safe on single core systems + `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware + that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with + a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. + See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. - **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError`. diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index ef8f7ae9c..0353ea710 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,7 +8,7 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] mod atomic; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index a7ad6b989..c14c8fdca 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,11 +11,11 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] mod atomic; mod critical_section; mod shared; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] pub use atomic::*; pub use self::critical_section::*; diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index f0cb8f16d..91add2324 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -3,7 +3,7 @@ #[allow(unused_imports)] use core::cell::UnsafeCell; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// /// To use `AtomicDevice`, you must wrap the bus with this struct, and then @@ -12,12 +12,12 @@ pub struct AtomicCell { pub(crate) bus: UnsafeCell, pub(crate) busy: portable_atomic::AtomicBool, } -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Send for AtomicCell {} -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Sync for AtomicCell {} -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] impl AtomicCell { /// Create a new `AtomicCell` pub fn new(bus: BUS) -> Self { From 33f5255ce8fe88f33755078c0386eb46d2ee0a08 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:18:37 +1000 Subject: [PATCH 147/201] Fix broken URL in README.md --- embedded-hal-bus/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 027612642..2e6d9db0d 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -38,7 +38,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. - See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. + See for more info. - **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError`. From 54fa71e32ae3334088b9f8c9e59dfca5fe4e8e53 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:20:52 +1000 Subject: [PATCH 148/201] Add feature require-cas to portable-atomic --- embedded-hal-bus/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 61f59f3ec..8898d961f 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -36,7 +36,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } -portable-atomic = {version = "1", default-features = false} +portable-atomic = {version = "1", default-features = false, features = ["require-cas"]} [package.metadata.docs.rs] features = ["std", "async", "atomic-device"] From a6be9a76bb09aba26337fb0c42990f4ea4537f43 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:24:46 +1000 Subject: [PATCH 149/201] Set minimum portable-atomic version to allow 'require-cas' --- embedded-hal-bus/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 8898d961f..333aef89b 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -36,7 +36,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } -portable-atomic = {version = "1", default-features = false, features = ["require-cas"]} +portable-atomic = {version = "1.3", default-features = false, features = ["require-cas"]} [package.metadata.docs.rs] features = ["std", "async", "atomic-device"] From e7cbd52fb889de617566495dc405abac7de7c52d Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:27:29 +1000 Subject: [PATCH 150/201] Remove atomic-device feature from docs.rs metadata --- embedded-hal-bus/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 333aef89b..0b3d444c7 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -39,5 +39,5 @@ defmt-03 = { package = "defmt", version = "0.3", optional = true } portable-atomic = {version = "1.3", default-features = false, features = ["require-cas"]} [package.metadata.docs.rs] -features = ["std", "async", "atomic-device"] +features = ["std", "async"] rustdoc-args = ["--cfg", "docsrs"] From cc868a31cd9999bd86467463e62cae8eeeb4e9c3 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 28 Jul 2024 10:13:26 +1000 Subject: [PATCH 151/201] Make portable-atomic optional --- embedded-hal-bus/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 0b3d444c7..d0ebecf7d 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -23,7 +23,7 @@ std = ["alloc"] # that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with # a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. # See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. -portable-atomic = [] +portable-atomic = ["dep:portable-atomic"] # Enable `embedded-hal-async` support. async = ["dep:embedded-hal-async"] # Derive `defmt::Format` from `defmt` 0.3 for enums and structs. See https://github.com/knurling-rs/defmt for more info @@ -36,7 +36,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } -portable-atomic = {version = "1.3", default-features = false, features = ["require-cas"]} +portable-atomic = {version = "1.3", default-features = false, optional = true, features = ["require-cas"]} [package.metadata.docs.rs] features = ["std", "async"] From 2acbd7773cb30b3c8c5e78720a9fbc6b304954d8 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 28 Jul 2024 10:19:09 +1000 Subject: [PATCH 152/201] Use AtomicBool from core if not using portable-atomic --- embedded-hal-bus/src/util.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index 91add2324..bb16577c4 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -3,6 +3,11 @@ #[allow(unused_imports)] use core::cell::UnsafeCell; +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::AtomicBool; +#[cfg(feature = "portable-atomic")] +use portable_atomic::AtomicBool; + #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// @@ -10,7 +15,7 @@ use core::cell::UnsafeCell; /// construct multiple `AtomicDevice` instances with references to it. pub struct AtomicCell { pub(crate) bus: UnsafeCell, - pub(crate) busy: portable_atomic::AtomicBool, + pub(crate) busy: AtomicBool, } #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Send for AtomicCell {} @@ -23,7 +28,7 @@ impl AtomicCell { pub fn new(bus: BUS) -> Self { Self { bus: UnsafeCell::new(bus), - busy: portable_atomic::AtomicBool::from(false), + busy: AtomicBool::from(false), } } } From 64d822764dddae3cf27a71f694d6c7f810af5ba4 Mon Sep 17 00:00:00 2001 From: pupu Date: Tue, 6 Aug 2024 00:37:08 +0800 Subject: [PATCH 153/201] Clarify I2C transaction contract for NACK behavior Update the I2C trait documentation in both embedded-hal and embedded-hal-async to clarify the NACK (No Acknowledge) behavior during read operations. The previous wording suggested that a NACK was only sent for the last byte of the final read operation in a transaction. This has been corrected to specify that a NACK should be sent at the end of each read operation, whether it's followed by a stop condition or a repeated start condition. This change ensures correct implementation of the I2C protocol across different scenarios and prevents potential communication errors. Changes: - Updated comment in embedded-hal/src/i2c.rs - Updated comment in embedded-hal-async/src/i2c.rs --- embedded-hal-async/src/i2c.rs | 2 +- embedded-hal/src/i2c.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 445cd9e6b..86014eec0 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -112,7 +112,7 @@ pub trait I2c: ErrorType { /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. + /// - At the end of each read operation (before SP or SR), the master does not send an acknowledge for the last byte. /// /// - `ST` = start condition /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 634d52248..36052afaf 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -392,7 +392,7 @@ pub trait I2c: ErrorType { /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. + /// - At the end of each read operation (before SP or SR), the master does not send an acknowledge for the last byte. /// /// - `ST` = start condition /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing From 9b1e75480bb133a10f454903e38d3fcd9abe7f54 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Aug 2024 07:32:28 +0000 Subject: [PATCH 154/201] Clarify Read trait blocking behavior --- embedded-io/src/lib.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 9f95e636a..62028c222 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -300,16 +300,23 @@ impl std::error::Error for WriteFmtError {} pub trait Read: ErrorType { /// Read some bytes from this source into the specified buffer, returning how many bytes were read. /// - /// If no bytes are currently available to read, this function blocks until at least one byte is available. + /// If no bytes are currently available to read: + /// - The method blocks until at least one byte becomes available; + /// - Once at least one (or more) bytes become available, a non-zero amount of those is read to the + /// beginning of `buf`, and the amount is returned, *without waiting or blocking any further for + /// more bytes to become available*. /// - /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount - /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the - /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately - /// available. + /// If bytes are available to read: + /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, + /// *without blocking and waiting for more bytes to become available*; + /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to + /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. /// - /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF - /// will always be so in the future, for example a reader can stop being at EOF if another process appends - /// more bytes to the underlying file. + /// This blocking behavior is important for the cases where `Read` represents the "read" leg of a pipe-like + /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty + /// buffer - does expect _some_ data (one or more bytes) - but _not necessarily `buf.len()` or more bytes_ - + /// to become available, before the peer represented by `Read` would stop sending bytes due to + /// application-specific reasons (as in the peer waiting for a response to the data it had sent so far). /// /// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error. /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. From 45fcad0cd5c329a90df7f066aaffb6741a414498 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Aug 2024 13:01:06 +0300 Subject: [PATCH 155/201] Update embedded-io/src/lib.rs Co-authored-by: James Munns --- embedded-io/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 62028c222..fcf465f42 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -302,7 +302,7 @@ pub trait Read: ErrorType { /// /// If no bytes are currently available to read: /// - The method blocks until at least one byte becomes available; - /// - Once at least one (or more) bytes become available, a non-zero amount of those is read to the + /// - Once at least one (or more) bytes become available, a non-zero amount of those is copied to the /// beginning of `buf`, and the amount is returned, *without waiting or blocking any further for /// more bytes to become available*. /// From 92dbaf2d03dbf8d6910cee4fc3b867c1bcf3746a Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Aug 2024 11:48:15 +0000 Subject: [PATCH 156/201] Re-add an unintentionally removed paragraph --- embedded-io/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index fcf465f42..a9abf9f42 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -318,6 +318,10 @@ pub trait Read: ErrorType { /// to become available, before the peer represented by `Read` would stop sending bytes due to /// application-specific reasons (as in the peer waiting for a response to the data it had sent so far). /// + /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. + /// /// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error. /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. fn read(&mut self, buf: &mut [u8]) -> Result; From f3558ac6f10a2f9d6403237f86edea210a9bfc0b Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 26 Aug 2024 07:32:57 +0000 Subject: [PATCH 157/201] Add identical changes to the async Read variant --- embedded-io-async/src/lib.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 7c0ac2d55..0fb8a2576 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -19,12 +19,23 @@ pub use embedded_io::{ pub trait Read: ErrorType { /// Read some bytes from this source into the specified buffer, returning how many bytes were read. /// - /// If no bytes are currently available to read, this function waits until at least one byte is available. - /// - /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount - /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the - /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately - /// available. + /// If no bytes are currently available to read: + /// - The method waits until at least one byte becomes available; + /// - Once at least one (or more) bytes become available, a non-zero amount of those is copied to the + /// beginning of `buf`, and the amount is returned, *without waiting any further for more bytes to + /// become available*. + /// + /// If bytes are available to read: + /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, + /// *without waiting for more bytes to become available*; + /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to + /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. + /// + /// This waiting behavior is important for the cases where `Read` represents the "read" leg of a pipe-like + /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty + /// buffer - does expect _some_ data (one or more bytes) - but _not necessarily `buf.len()` or more bytes_ - + /// to become available, before the peer represented by `Read` would stop sending bytes due to + /// application-specific reasons (as in the peer waiting for a response to the data it had sent so far). /// /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF /// will always be so in the future, for example a reader can stop being at EOF if another process appends From d7454184366fdbefb590bdee0cb7a8a21ce4e886 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sun, 8 Sep 2024 07:51:22 +0000 Subject: [PATCH 158/201] Address review feedback --- embedded-io-async/src/lib.rs | 6 ++++-- embedded-io/src/lib.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 0fb8a2576..eef50ebcd 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -28,8 +28,10 @@ pub trait Read: ErrorType { /// If bytes are available to read: /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, /// *without waiting for more bytes to become available*; - /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to - /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. + /// + /// Note that once some bytes are available to read, it is *not* guaranteed that all available bytes are returned. + /// It is possible for the implementation to read an amount of bytes less than `buf.len()` while there are more + /// bytes immediately available. /// /// This waiting behavior is important for the cases where `Read` represents the "read" leg of a pipe-like /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index a9abf9f42..f050abb1d 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -309,8 +309,10 @@ pub trait Read: ErrorType { /// If bytes are available to read: /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, /// *without blocking and waiting for more bytes to become available*; - /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to - /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. + /// + /// Note that once some bytes are available to read, it is *not* guaranteed that all available bytes are returned. + /// It is possible for the implementation to read an amount of bytes less than `buf.len()` while there are more + /// bytes immediately available. /// /// This blocking behavior is important for the cases where `Read` represents the "read" leg of a pipe-like /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty From 81ee92a99080c602334a3149d508f797857ac1de Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Mon, 9 Sep 2024 13:42:44 +0200 Subject: [PATCH 159/201] CI: fix label of rustdoc job --- .github/workflows/rustdoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index 7829e62b6..1a99f8d92 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -7,7 +7,7 @@ on: name: Rustdoc check jobs: - clippy: + rustdoc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 91cf7e18bdff43a2a70345d07fafeaaf627c5ee4 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Mon, 9 Sep 2024 13:26:01 +0200 Subject: [PATCH 160/201] implement `core::error::Error` this trait has been stabilised in Rust 1.81.0. the existing custom `Error` types cannot be removed / replaced as that'd be a breaking change. for the same reason it's not possible for them to require `core::error::Error` being implemented. however, we can already implement the new trait for all cases where the custom trait has been implemented so far. existing `std` feature-gated implementations of `std::error::Error` have also been moved to `core::error::Error` and the feature gate removed. this raises the MSRV to 1.81.0 for most crates, but based on the MSRV policy this should not be an issue. --- .github/workflows/test.yml | 18 ++---------------- README.md | 2 +- embedded-can/CHANGELOG.md | 3 ++- embedded-can/Cargo.toml | 2 +- embedded-can/src/lib.rs | 2 ++ embedded-hal-bus/CHANGELOG.md | 2 ++ embedded-hal-bus/Cargo.toml | 4 ++-- embedded-hal-bus/README.md | 3 +-- embedded-hal-bus/src/spi/mod.rs | 3 +-- embedded-hal-nb/CHANGELOG.md | 3 ++- embedded-hal-nb/Cargo.toml | 2 +- embedded-hal-nb/src/serial.rs | 2 ++ embedded-hal/CHANGELOG.md | 3 ++- embedded-hal/Cargo.toml | 2 +- embedded-hal/src/digital.rs | 2 ++ embedded-hal/src/i2c.rs | 2 ++ embedded-hal/src/pwm.rs | 2 ++ embedded-hal/src/spi.rs | 2 ++ embedded-io-async/README.md | 2 +- embedded-io/CHANGELOG.md | 6 ++++++ embedded-io/Cargo.toml | 2 +- embedded-io/README.md | 2 +- embedded-io/src/impls/slice_mut.rs | 4 +--- embedded-io/src/lib.rs | 16 ++++++++++------ 24 files changed, 50 insertions(+), 41 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46fb03b26..586d42513 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,23 +38,9 @@ jobs: --target thumbv7m-none-eabi --features async,defmt-03 - msrv-1-60: + msrv-1-81: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.60 - - run: > - cargo test - -p embedded-hal:1.0.0 - -p embedded-hal-bus - -p embedded-hal-nb - -p embedded-io - -p embedded-io-adapters - -p embedded-can - - msrv-1-75: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.75 + - uses: dtolnay/rust-toolchain@1.81 - run: cargo test --workspace --all-features diff --git a/README.md b/README.md index da60c3fed..63c347628 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ on crates.io. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.81 and up. It *might* compile with older versions but that may change in any new patch release. See [here](docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-can/CHANGELOG.md b/embedded-can/CHANGELOG.md index 302c85a09..f8719d632 100644 --- a/embedded-can/CHANGELOG.md +++ b/embedded-can/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -... +- Added `core::error::Error` implementations for every custom `impl Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v0.4.1] - 2022-09-28 diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 07ef7cc53..105f286b0 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-can" version = "0.4.1" edition = "2021" -rust-version = "1.56" +rust-version = "1.81" description = "HAL traits for Controller Area Network (CAN) devices." categories = ["embedded", "hardware-support", "no-std"] diff --git a/embedded-can/src/lib.rs b/embedded-can/src/lib.rs index da561f58c..bb010b965 100644 --- a/embedded-can/src/lib.rs +++ b/embedded-can/src/lib.rs @@ -110,6 +110,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 45d0f6cc8..d9009d9c3 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added the `alloc` feature. - Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`. +- Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v0.2.0] - 2024-04-23 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index d0ebecf7d..605f01b85 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -6,7 +6,7 @@ categories = ["embedded", "hardware-support", "no-std"] description = "Bus/Device connection mechanisms for embedded-hal, a Hardware Abstraction Layer (HAL) for embedded systems" documentation = "https://docs.rs/embedded-hal-bus" edition = "2021" -rust-version = "1.60" +rust-version = "1.81" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal-bus" @@ -15,7 +15,7 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0" [features] -# Enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError` +# Enable shared bus implementations using `std::sync::Mutex` std = ["alloc"] # Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS # diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 2e6d9db0d..6f767aae7 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -39,8 +39,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. See for more info. -- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement - `std::error::Error` for `DeviceError`. +- **`std`**: enable shared bus implementations using `std::sync::Mutex`. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 5a8356e28..c7a793f90 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -47,8 +47,7 @@ impl Display for DeviceError { } } -#[cfg(feature = "std")] -impl std::error::Error for DeviceError {} +impl core::error::Error for DeviceError {} impl Error for DeviceError where diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index f31190acb..63314c7cf 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +- Added `core::error::Error` implementations for every custom `impl Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v1.0.0] - 2023-12-28 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index d4575ead0..43a6cd7be 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-hal-nb" version = "1.0.0" edition = "2021" -rust-version = "1.56" +rust-version = "1.81" categories = ["embedded", "hardware-support", "no-std"] description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index 18d1265c2..2b0d504ef 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -45,6 +45,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 4a1af0e3f..e919e25ad 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes yet. +- Added `core::error::Error` implementations for every custom `impl Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v1.0.0] - 2023-12-28 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index f67b2527b..011f3cc9d 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" -rust-version = "1.60" +rust-version = "1.81" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index 201cd7637..a38040c0c 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -41,6 +41,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 36052afaf..3d042cd36 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -232,6 +232,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 68a94f54f..4d4ff1508 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -40,6 +40,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 508a4a9b1..55fc7e9fb 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -275,6 +275,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md index 744f6e6f2..8c5043593 100644 --- a/embedded-io-async/README.md +++ b/embedded-io-async/README.md @@ -12,7 +12,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Optional Cargo features -- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`std`**: Adds `From` impls to convert to/from `std::io` structs. - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 286d1b707..1c8ea414c 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Added `core::error::Error` implementations for every custom `impl Error` +- Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` +- Increased MSRV to 1.81 due to `core::error::Error` + ## 0.6.1 - 2023-10-22 - Make `SliceWriteError` publicly available. diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index e92714f7a..dc878f7b6 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-io" version = "0.6.1" edition = "2021" -rust-version = "1.60" +rust-version = "1.81" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" diff --git a/embedded-io/README.md b/embedded-io/README.md index 04ea97771..6c228d8b4 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -21,7 +21,7 @@ targets. ## Optional Cargo features -- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`std`**: Adds `From` impls to convert to/from `std::io` structs. - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index 4608ee45b..67606561b 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -19,9 +19,7 @@ impl core::fmt::Display for SliceWriteError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for SliceWriteError {} +impl core::error::Error for SliceWriteError {} /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting /// its data. diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f050abb1d..ae5ff4853 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -193,6 +193,14 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Error for std::io::Error { @@ -255,9 +263,7 @@ impl fmt::Display for ReadExactError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for ReadExactError {} +impl core::error::Error for ReadExactError {} /// Errors that could be returned by `Write` on `&mut [u8]`. #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -290,9 +296,7 @@ impl fmt::Display for WriteFmtError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for WriteFmtError {} +impl core::error::Error for WriteFmtError {} /// Blocking reader. /// From d3f300cdd3e8c5ced313fea1ac112951db45da33 Mon Sep 17 00:00:00 2001 From: Jacob Birkett Date: Mon, 7 Oct 2024 13:23:22 -0700 Subject: [PATCH 161/201] i2c: AddressMode: add bound Copy --- embedded-hal/src/i2c.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 3d042cd36..8b99197ef 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -276,7 +276,7 @@ impl ErrorType for &mut T { /// Address mode (7-bit / 10-bit). /// /// Note: This trait is sealed and should not be implemented outside of this crate. -pub trait AddressMode: private::Sealed + 'static {} +pub trait AddressMode: Copy + 'static + private::Sealed {} /// 7-bit address mode type. /// From 6a7d02687db8dbfd16b55f29ade3e48bbbedbcde Mon Sep 17 00:00:00 2001 From: Tamme Dittrich Date: Thu, 14 Nov 2024 10:33:26 +0100 Subject: [PATCH 162/201] Update readme to reflect MSRV specified in Cargo.toml It looks like the readme was missed when updating the MSRV to include `core::error::Error`. --- embedded-can/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-can/README.md b/embedded-can/README.md index 1beaf39cd..96afb5ec6 100644 --- a/embedded-can/README.md +++ b/embedded-can/README.md @@ -19,7 +19,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.81 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. From 9cf0629c997dac0748329af88cd10a5e9fc4ba9f Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Tue, 19 Nov 2024 14:31:18 +0000 Subject: [PATCH 163/201] Don't recommend eh0.2 --- docs/migrating-from-0.2-to-1.0.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/migrating-from-0.2-to-1.0.md b/docs/migrating-from-0.2-to-1.0.md index b35a7a2d4..5ad2b879c 100644 --- a/docs/migrating-from-0.2-to-1.0.md +++ b/docs/migrating-from-0.2-to-1.0.md @@ -470,14 +470,15 @@ Here is the full listing of crates: ## Supporting both 0.2 and 1.0 in the same HAL -It is strongly recommended that HAL implementation crates provide implementations for both the `embedded-hal` v0.2 and v1.0 traits. -This allows users to use drivers using either version seamlessly. +It is not recommended to support 0.2 version of the HAL. Many of the traits are not fit for purpose which is already discussed in this document. + +If you wish to support both the 0.2 and 1.0 release it is recommended to support embedded-hal version 0.2 behind a feature flag named `embedded-hal-02`. The way you do it is adding a dependency on both versions in `Cargo.toml` like this: ```toml [dependencies] -embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } +embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"], optional = true } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } ``` @@ -489,6 +490,7 @@ This allows you to refer to the v0.2 traits under the `embedded_hal_02` name, an struct Input {...} /// Implement the v0.2 traits on the struct. +#[cfg(feature = "embedded-hal-02")] impl embedded_hal_02::digital::v2::InputPin for Input { type Error = Infallible; From 9a1921ab8703dafc0994d8db03ca886c006533cc Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Fri, 22 Nov 2024 19:01:07 +0000 Subject: [PATCH 164/201] Fix several clippy warnings in current stable / beta --- embedded-hal-bus/src/i2c/atomic.rs | 6 +++--- embedded-hal-bus/src/i2c/critical_section.rs | 4 ++-- embedded-hal-bus/src/i2c/refcell.rs | 4 ++-- embedded-hal-bus/src/spi/atomic.rs | 4 ++-- embedded-hal-bus/src/spi/critical_section.rs | 4 ++-- embedded-hal-bus/src/spi/refcell.rs | 4 ++-- embedded-hal-nb/src/serial.rs | 1 - 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 793e827b1..8443e4618 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -101,7 +101,7 @@ impl Error for AtomicError { } } -unsafe impl<'a, T> Send for AtomicDevice<'a, T> {} +unsafe impl Send for AtomicDevice<'_, T> {} impl<'a, T> AtomicDevice<'a, T> where @@ -137,14 +137,14 @@ where } } -impl<'a, T> ErrorType for AtomicDevice<'a, T> +impl ErrorType for AtomicDevice<'_, T> where T: I2c, { type Error = AtomicError; } -impl<'a, T> I2c for AtomicDevice<'a, T> +impl I2c for AtomicDevice<'_, T> where T: I2c, { diff --git a/embedded-hal-bus/src/i2c/critical_section.rs b/embedded-hal-bus/src/i2c/critical_section.rs index 6830f284f..be0bfb8ce 100644 --- a/embedded-hal-bus/src/i2c/critical_section.rs +++ b/embedded-hal-bus/src/i2c/critical_section.rs @@ -21,14 +21,14 @@ impl<'a, T> CriticalSectionDevice<'a, T> { } } -impl<'a, T> ErrorType for CriticalSectionDevice<'a, T> +impl ErrorType for CriticalSectionDevice<'_, T> where T: I2c, { type Error = T::Error; } -impl<'a, T> I2c for CriticalSectionDevice<'a, T> +impl I2c for CriticalSectionDevice<'_, T> where T: I2c, { diff --git a/embedded-hal-bus/src/i2c/refcell.rs b/embedded-hal-bus/src/i2c/refcell.rs index 1519a751b..445b6b020 100644 --- a/embedded-hal-bus/src/i2c/refcell.rs +++ b/embedded-hal-bus/src/i2c/refcell.rs @@ -75,14 +75,14 @@ impl<'a, T> RefCellDevice<'a, T> { } } -impl<'a, T> ErrorType for RefCellDevice<'a, T> +impl ErrorType for RefCellDevice<'_, T> where T: I2c, { type Error = T::Error; } -impl<'a, T> I2c for RefCellDevice<'a, T> +impl I2c for RefCellDevice<'_, T> where T: I2c, { diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index 7d18e28c5..10dc043b7 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -106,7 +106,7 @@ impl Error for AtomicError { } } -impl<'a, BUS, CS, D> ErrorType for AtomicDevice<'a, BUS, CS, D> +impl ErrorType for AtomicDevice<'_, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -114,7 +114,7 @@ where type Error = AtomicError>; } -impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for AtomicDevice<'a, BUS, CS, D> +impl SpiDevice for AtomicDevice<'_, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 4c3a46eb2..3ab2d29cc 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -72,7 +72,7 @@ impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { } } -impl<'a, BUS, CS, D> ErrorType for CriticalSectionDevice<'a, BUS, CS, D> +impl ErrorType for CriticalSectionDevice<'_, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -80,7 +80,7 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for CriticalSectionDevice<'a, BUS, CS, D> +impl SpiDevice for CriticalSectionDevice<'_, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 35bea03a2..9f0374e34 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -69,7 +69,7 @@ impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { } } -impl<'a, BUS, CS, D> ErrorType for RefCellDevice<'a, BUS, CS, D> +impl ErrorType for RefCellDevice<'_, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -77,7 +77,7 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for RefCellDevice<'a, BUS, CS, D> +impl SpiDevice for RefCellDevice<'_, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index 2b0d504ef..981422702 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -118,7 +118,6 @@ impl + ?Sized, Word: Copy> Write for &mut T { /// Implementation of `core::fmt::Write` for the HAL's `serial::Write`. /// /// TODO write example of usage - impl core::fmt::Write for dyn Write + '_ where Word: Copy + From, From 6982e47d075e9859fd6635402c2f42a1141609c9 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 21 Jan 2025 20:18:42 +0100 Subject: [PATCH 165/201] Update version for release --- embedded-hal-bus/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 605f01b85..7f404e3d6 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.2.0" +version = "0.3.0" [features] # Enable shared bus implementations using `std::sync::Mutex` From df0880458fb942856d6fde14a80530f8db3eff4c Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 21 Jan 2025 20:23:08 +0100 Subject: [PATCH 166/201] Fix clippy errors --- embedded-hal-bus/src/i2c/mutex.rs | 4 ++-- embedded-hal-bus/src/spi/mutex.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs index ef4a9f510..1fc1b4383 100644 --- a/embedded-hal-bus/src/i2c/mutex.rs +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -19,14 +19,14 @@ impl<'a, T> MutexDevice<'a, T> { } } -impl<'a, T> ErrorType for MutexDevice<'a, T> +impl ErrorType for MutexDevice<'_, T> where T: I2c, { type Error = T::Error; } -impl<'a, T> I2c for MutexDevice<'a, T> +impl I2c for MutexDevice<'_, T> where T: I2c, { diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 83fe85d88..e3afc71e5 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -70,7 +70,7 @@ impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { } } -impl<'a, BUS, CS, D> ErrorType for MutexDevice<'a, BUS, CS, D> +impl ErrorType for MutexDevice<'_, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -78,7 +78,7 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for MutexDevice<'a, BUS, CS, D> +impl SpiDevice for MutexDevice<'_, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, From 0c481ea805b31078d5cc7b40a8c6fd753a9e16b5 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 21 Jan 2025 20:32:26 +0100 Subject: [PATCH 167/201] Update changelog --- embedded-hal-bus/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index d9009d9c3..6f1666331 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Your change here! + +## [v0.3.0] - 2025-01-21 + - Added the `alloc` feature. - Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`. - Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` @@ -64,7 +68,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.2.0...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.3.0...HEAD +[v0.3.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.2.0...embedded-hal-bus-v0.3.0 [v0.2.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...embedded-hal-bus-v0.2.0 [v0.1.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...embedded-hal-bus-v0.1.0 [v0.1.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...embedded-hal-bus-v0.1.0-rc.3 From 10f15095e62b9659b8082a118c2e20d031463f2a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 21 Jan 2025 22:45:16 +0100 Subject: [PATCH 168/201] bus: add `portable-atomic` feature to changelog. --- embedded-hal-bus/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 6f1666331..a3680bc24 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [v0.3.0] - 2025-01-21 +- `AtomicDevice` now requires enabling feature `portable-atomic` on `thumbv6m-none-eabi` and other targets that don't have atomic CAS. - Added the `alloc` feature. - Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`. - Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` From 3a5d90e962d537ae9407d0f1471cfbe6d261814c Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Sat, 25 Jan 2025 18:56:12 +0000 Subject: [PATCH 169/201] Show features required for AtomicDevice in docs --- embedded-hal-bus/src/spi/atomic.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index 10dc043b7..53b780e9b 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -26,6 +26,10 @@ use crate::util::AtomicCell; /// /// This primitive is particularly well-suited for applications that have external arbitration /// rules that prevent `Busy` errors in the first place, such as the RTIC framework. +#[cfg_attr( + docsrs, + doc(cfg(any(feature = "portable-atomic", target_has_atomic = "8"))) +)] pub struct AtomicDevice<'a, BUS, CS, D> { bus: &'a AtomicCell, cs: CS, @@ -34,6 +38,10 @@ pub struct AtomicDevice<'a, BUS, CS, D> { #[derive(Debug, Copy, Clone)] /// Wrapper type for errors returned by [`AtomicDevice`]. +#[cfg_attr( + docsrs, + doc(cfg(any(feature = "portable-atomic", target_has_atomic = "8"))) +)] pub enum AtomicError { /// This error is returned if the SPI bus was already in use when an operation was attempted, /// which indicates that the driver requirements are not being met with regard to From ea672e027984a5a9aaac93f9d5704b238338c65a Mon Sep 17 00:00:00 2001 From: i509VCB Date: Tue, 11 Feb 2025 15:50:08 -0600 Subject: [PATCH 170/201] Add async versions of OutputPin, StatefulOutputPin and InputPin --- embedded-hal-async/src/digital.rs | 136 +++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 5efe3d180..b7f1f4daa 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -1,5 +1,12 @@ //! Asynchronous digital I/O. //! +//! The [`OutputPin`], [`StatefulOutputPin`] and [`InputPin`] traits are `async` variants +//! of the [blocking traits](embedded_hal::digital). These traits are useful for when +//! digital I/O may block execution, such as access through an I/O expander or over some +//! other transport. +//! +//! The [`Wait`] trait allows asynchronously waiting for a change in pin level. +//! //! # Example //! //! ```rust @@ -15,7 +22,134 @@ //! .expect("failed to await input pin") //! } //! ``` -pub use embedded_hal::digital::{Error, ErrorKind, ErrorType}; +//! +//! # For HAL authors +//! +//! If the digital I/O is implemented using memory mapped I/O and acts immediately, then the async traits +//! (except for [`Wait`]) can be implemented by calling the blocking traits and wrapping the result in +//! [`Poll::Ready`](core::task::Poll::Ready). +pub use embedded_hal::digital::{Error, ErrorKind, ErrorType, PinState}; + +/// Asynchronous single digital push-pull output pin. +pub trait OutputPin: ErrorType { + /// Drives the pin low. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven low. + /// + /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external + /// electrical sources. + async fn set_low(&mut self) -> Result<(), Self::Error>; + + /// Drives the pin high. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven high. + /// + /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external + /// electrical sources. + async fn set_high(&mut self) -> Result<(), Self::Error>; + + /// Drives the pin high or low depending on the provided value. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven to the provided state. + /// + /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external + /// electrical sources. + #[inline] + async fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { + match state { + PinState::Low => self.set_low().await, + PinState::High => self.set_high().await, + } + } +} + +impl OutputPin for &mut T { + #[inline] + async fn set_low(&mut self) -> Result<(), Self::Error> { + T::set_low(self).await + } + + #[inline] + async fn set_high(&mut self) -> Result<(), Self::Error> { + T::set_high(self).await + } + + #[inline] + async fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { + T::set_state(self, state).await + } +} + +/// Asynchronous push-pull output pin that can read its output state. +pub trait StatefulOutputPin: OutputPin { + /// Is the pin in drive high mode? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's drive mode been read. + /// + /// *NOTE* this does *not* read the electrical state of the pin. + async fn is_set_high(&mut self) -> Result; + + /// Is the pin in drive low mode? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's drive mode been read. + /// + /// *NOTE* this does *not* read the electrical state of the pin. + async fn is_set_low(&mut self) -> Result; + + /// Toggle pin output. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been toggled. + async fn toggle(&mut self) -> Result<(), Self::Error> { + let was_low: bool = self.is_set_low().await?; + self.set_state(PinState::from(was_low)).await + } +} + +impl StatefulOutputPin for &mut T { + #[inline] + async fn is_set_high(&mut self) -> Result { + T::is_set_high(self).await + } + + #[inline] + async fn is_set_low(&mut self) -> Result { + T::is_set_low(self).await + } + + #[inline] + async fn toggle(&mut self) -> Result<(), Self::Error> { + T::toggle(self).await + } +} + +/// Asynchronous single digital input pin. +pub trait InputPin: ErrorType { + /// Is the input pin high? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's electrical state has been read. + /// + /// *NOTE* the input state of the pin may have changed before the future is polled. + async fn is_high(&mut self) -> Result; + + /// Is the input pin low? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's electrical state has been read. + /// + /// *NOTE* the input state of the pin may have changed before the future is polled. + async fn is_low(&mut self) -> Result; +} + +impl InputPin for &mut T { + #[inline] + async fn is_high(&mut self) -> Result { + T::is_high(self).await + } + + #[inline] + async fn is_low(&mut self) -> Result { + T::is_low(self).await + } +} /// Asynchronously wait for GPIO pin state. pub trait Wait: ErrorType { From df1a2a9fadce23d3b9b875ea0a24c692ebe66aa2 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Tue, 11 Feb 2025 15:50:52 -0600 Subject: [PATCH 171/201] Update module documentation to mention traits in embedded_hal::digital are blocking --- embedded-hal/src/digital.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index a38040c0c..581f2ca38 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -1,4 +1,4 @@ -//! Digital I/O. +//! Blocking Digital I/O. use core::ops::Not; From c408c2a58591689a52ba8fa4cc589bf6298278d8 Mon Sep 17 00:00:00 2001 From: taozui472 Date: Mon, 24 Feb 2025 17:34:15 +0800 Subject: [PATCH 172/201] update copyright year --- embedded-can/LICENSE-MIT | 2 +- embedded-hal-async/LICENSE-MIT | 2 +- embedded-hal-bus/LICENSE-MIT | 2 +- embedded-hal-nb/LICENSE-MIT | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-can/LICENSE-MIT b/embedded-can/LICENSE-MIT index 0aee0d5a0..a9285b234 100644 --- a/embedded-can/LICENSE-MIT +++ b/embedded-can/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 The Rust embedded HAL team and contributors. +Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/embedded-hal-async/LICENSE-MIT b/embedded-hal-async/LICENSE-MIT index 0aee0d5a0..a9285b234 100644 --- a/embedded-hal-async/LICENSE-MIT +++ b/embedded-hal-async/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 The Rust embedded HAL team and contributors. +Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/embedded-hal-bus/LICENSE-MIT b/embedded-hal-bus/LICENSE-MIT index 0aee0d5a0..a9285b234 100644 --- a/embedded-hal-bus/LICENSE-MIT +++ b/embedded-hal-bus/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 The Rust embedded HAL team and contributors. +Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/embedded-hal-nb/LICENSE-MIT b/embedded-hal-nb/LICENSE-MIT index 0aee0d5a0..a9285b234 100644 --- a/embedded-hal-nb/LICENSE-MIT +++ b/embedded-hal-nb/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 The Rust embedded HAL team and contributors. +Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From bc736f2d9c81989abadd3ce5f69816ec510a714e Mon Sep 17 00:00:00 2001 From: Mathias Pius Date: Mon, 14 Apr 2025 12:45:23 +0200 Subject: [PATCH 173/201] Impl ReadReady and WriteReady for u8 slices --- embedded-io/CHANGELOG.md | 1 + embedded-io/src/impls/slice_mut.rs | 9 ++++++++- embedded-io/src/impls/slice_ref.rs | 9 ++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 1c8ea414c..f7c2e0e67 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `core::error::Error` implementations for every custom `impl Error` - Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` - Increased MSRV to 1.81 due to `core::error::Error` +- Implemented `ReadReady` for `&[u8]` and `WriteReady` for `&mut [u8]` ## 0.6.1 - 2023-10-22 diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index 67606561b..cee991ea6 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -1,4 +1,4 @@ -use crate::{Error, ErrorKind, ErrorType, SliceWriteError, Write}; +use crate::{Error, ErrorKind, ErrorType, SliceWriteError, Write, WriteReady}; use core::mem; impl Error for SliceWriteError { @@ -47,3 +47,10 @@ impl Write for &mut [u8] { Ok(()) } } + +impl WriteReady for &mut [u8] { + #[inline] + fn write_ready(&mut self) -> Result { + Ok(true) + } +} diff --git a/embedded-io/src/impls/slice_ref.rs b/embedded-io/src/impls/slice_ref.rs index 6332d70dd..0270aca27 100644 --- a/embedded-io/src/impls/slice_ref.rs +++ b/embedded-io/src/impls/slice_ref.rs @@ -1,4 +1,4 @@ -use crate::{BufRead, ErrorType, Read}; +use crate::{BufRead, ErrorType, Read, ReadReady}; impl ErrorType for &[u8] { type Error = core::convert::Infallible; @@ -39,3 +39,10 @@ impl BufRead for &[u8] { *self = &self[amt..]; } } + +impl ReadReady for &[u8] { + #[inline] + fn read_ready(&mut self) -> Result { + Ok(true) + } +} From 46a52222e2d009dcf95a04642e7e6970ee9ddf9d Mon Sep 17 00:00:00 2001 From: Mathias Pius Date: Mon, 14 Apr 2025 13:33:35 +0200 Subject: [PATCH 174/201] Fix clippy error --- embedded-hal-nb/src/serial.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index 981422702..6b4d79032 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -127,7 +127,7 @@ where let _ = s .bytes() .map(|c| nb::block!(self.write(Word::from(c)))) - .last(); + .next_back(); Ok(()) } } From 45f6a64d5ef3bd5bd390811165255f1cbe602f36 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 14 Apr 2025 14:37:09 +0200 Subject: [PATCH 175/201] nb: do not swallow errors in fmt::Write. --- embedded-hal-nb/src/serial.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index 6b4d79032..e51085cf8 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -124,10 +124,9 @@ where { #[inline] fn write_str(&mut self, s: &str) -> core::fmt::Result { - let _ = s - .bytes() - .map(|c| nb::block!(self.write(Word::from(c)))) - .next_back(); + for c in s.bytes() { + nb::block!(self.write(Word::from(c))).map_err(|_| core::fmt::Error)?; + } Ok(()) } } From 93de810a1794fc122ebfdea88caecaec767743eb Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 22 Jul 2025 11:11:46 +0200 Subject: [PATCH 176/201] clippy fixes --- embedded-hal-bus/src/spi/mod.rs | 4 ++-- embedded-io/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index c7a793f90..659726288 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -41,8 +41,8 @@ pub enum DeviceError { impl Display for DeviceError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Self::Spi(bus) => write!(f, "SPI bus error: {}", bus), - Self::Cs(cs) => write!(f, "SPI CS error: {}", cs), + Self::Spi(bus) => write!(f, "SPI bus error: {bus}"), + Self::Cs(cs) => write!(f, "SPI CS error: {cs}"), } } } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index ae5ff4853..1412f7e67 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -197,7 +197,7 @@ impl core::error::Error for ErrorKind {} impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } From d02f6db11021c8889bb22cd8ec60f0dc47eabb38 Mon Sep 17 00:00:00 2001 From: chrysn Date: Sun, 3 Aug 2025 10:11:39 +0200 Subject: [PATCH 177/201] io: Require core::error::Error for the Error trait --- embedded-io/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 1412f7e67..5f4e48e3c 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -176,7 +176,7 @@ impl From for ErrorKind { /// /// This trait allows generic code to do limited inspecting of errors, /// to react differently to different kinds. -pub trait Error: fmt::Debug { +pub trait Error: fmt::Debug + core::error::Error { /// Get the kind of this error. fn kind(&self) -> ErrorKind; } From 8a24ac0cd931fdb805e3e03e140277fee2f75850 Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 6 Aug 2025 20:49:01 +0200 Subject: [PATCH 178/201] embedded-io-adapters: Clarify scope in README --- embedded-io-adapters/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md index a069d1fac..8480d37e3 100644 --- a/embedded-io-adapters/README.md +++ b/embedded-io-adapters/README.md @@ -27,6 +27,9 @@ For `embedded-io-async`: - [`futures` 0.3](https://crates.io/crates/futures) traits. Needs the `futures-03` feature. - [`tokio` 1.x](https://crates.io/crates/tokio) traits. Needs the `tokio-1` feature. +As a rule of thumb, this crate will generally provide adapters for crates that are more stable and general-purpose than embedded-io, +provided those crates implement the corresponding std traits. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* From 3f2a48c8e6e38b23cbbc64a51ddfeeae48443186 Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 6 Aug 2025 21:07:58 +0200 Subject: [PATCH 179/201] Release embedded-io-adapters 0.6.2 --- embedded-io-adapters/CHANGELOG.md | 2 +- embedded-io-adapters/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index f427e0aa1..00763a22e 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.6.2 – 2025-08-06 - Added `ToFmt` adapter for `core::fmt::Write`. diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 89721f1bb..ea88ee659 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.6.1" +version = "0.6.2" edition = "2021" rust-version = "1.60" description = "Adapters between the `embedded-io` traits and other I/O traits" From 1d69c2a5a297041495f7bf1f9ad181b47bfebef4 Mon Sep 17 00:00:00 2001 From: chrysn Date: Sun, 3 Aug 2025 10:17:31 +0200 Subject: [PATCH 180/201] io, io-async: Update defmt to 1.0, rename feature from "defmt_03" to "defmt" Co-authored-by: Ralph Ursprung <39383228+rursprung@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- embedded-io-async/Cargo.toml | 4 ++-- embedded-io-async/README.md | 2 +- embedded-io/Cargo.toml | 4 ++-- embedded-io/src/lib.rs | 14 +++++--------- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 586d42513..5b5ee297d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,7 @@ jobs: cargo build --workspace --target thumbv7m-none-eabi - --features async,defmt-03 + --features async,defmt-03,embedded-io/defmt,embedded-io-async/defmt msrv-1-81: runs-on: ubuntu-latest diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 1cf2e0aad..3b878a825 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -15,11 +15,11 @@ categories = [ [features] std = ["alloc", "embedded-io/std"] alloc = ["embedded-io/alloc"] -defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] +defmt = ["dep:defmt", "embedded-io/defmt"] [dependencies] embedded-io = { version = "0.6.1", path = "../embedded-io" } -defmt-03 = { package = "defmt", version = "0.3", optional = true } +defmt = { package = "defmt", version = "1", optional = true } [package.metadata.docs.rs] features = ["std"] diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md index 8c5043593..69f1e700b 100644 --- a/embedded-io-async/README.md +++ b/embedded-io-async/README.md @@ -14,7 +14,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru - **`std`**: Adds `From` impls to convert to/from `std::io` structs. - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. -- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. +- **`defmt`**: Derive `defmt::Format` from `defmt` 1.0 for enums and structs. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index dc878f7b6..48b30cfb3 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -15,10 +15,10 @@ categories = [ [features] std = ["alloc"] alloc = [] -defmt-03 = ["dep:defmt-03"] +defmt = ["dep:defmt"] [dependencies] -defmt-03 = { package = "defmt", version = "0.3", optional = true } +defmt = { version = "1", optional = true } [package.metadata.docs.rs] features = ["std"] diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 5f4e48e3c..4f417f123 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -5,10 +5,6 @@ use core::fmt; -// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. -#[cfg(feature = "defmt-03")] -use defmt_03 as defmt; - #[cfg(feature = "alloc")] extern crate alloc; @@ -18,7 +14,7 @@ mod impls; /// /// This is the `embedded-io` equivalent of [`std::io::SeekFrom`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. Start(u64), @@ -62,7 +58,7 @@ impl From for SeekFrom { /// /// - `WouldBlock` is removed, since `embedded-io` traits are always blocking. See the [crate-level documentation](crate) for details. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// Unspecified error kind. @@ -229,7 +225,7 @@ impl ErrorType for &mut T { /// Error returned by [`Read::read_exact`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ReadExactError { /// An EOF error was encountered before reading the exact amount of requested bytes. UnexpectedEof, @@ -267,7 +263,7 @@ impl core::error::Error for ReadExactError {} /// Errors that could be returned by `Write` on `&mut [u8]`. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum SliceWriteError { /// The target slice was full and so could not receive any new data. @@ -276,7 +272,7 @@ pub enum SliceWriteError { /// Error returned by [`Write::write_fmt`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum WriteFmtError { /// An error was encountered while formatting. FmtError, From 727422e65356ea88230da66a334b6e006a3f88af Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 19 Aug 2025 13:36:27 +0200 Subject: [PATCH 181/201] io: Remove redundant requirement on Debug core::error::Error already depends on Debug, thus this is a compatible simplification. --- embedded-io/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 4f417f123..9e2e528a9 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -172,7 +172,7 @@ impl From for ErrorKind { /// /// This trait allows generic code to do limited inspecting of errors, /// to react differently to different kinds. -pub trait Error: fmt::Debug + core::error::Error { +pub trait Error: core::error::Error { /// Get the kind of this error. fn kind(&self) -> ErrorKind; } From b7b07f8f1dbb68e3656f43d423827941abcc20ac Mon Sep 17 00:00:00 2001 From: Nathaniel Thomas Date: Fri, 22 Aug 2025 12:31:16 -0500 Subject: [PATCH 182/201] Update atomic.rs docs: typo "aditionally" changed to "additionally" --- embedded-hal-bus/src/i2c/atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 8443e4618..b47b8c5e0 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -5,7 +5,7 @@ use crate::util::AtomicCell; /// Atomics-based shared bus [`I2c`] implementation. /// /// Sharing is implemented with a [`AtomicDevice`], which consists of an `UnsafeCell` and an `AtomicBool` "locked" flag. -/// This means it has low overhead, like [`RefCellDevice`](crate::i2c::RefCellDevice). Aditionally, it is `Send`, +/// This means it has low overhead, like [`RefCellDevice`](crate::i2c::RefCellDevice). Additionally, it is `Send`, /// which allows sharing a single bus across multiple threads (interrupt priority level), like [`CriticalSectionDevice`](crate::i2c::CriticalSectionDevice), /// while not using critical sections and therefore impacting real-time performance less. /// From dac86caf0e99e84af3d559cdb2a0b458d33580f1 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Tue, 26 Aug 2025 22:56:02 +0200 Subject: [PATCH 183/201] io: add `Seek::seek_relative` --- embedded-io/src/lib.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 9e2e528a9..e1b6fe4d7 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -469,23 +469,59 @@ pub trait Write: ErrorType { } } -/// Blocking seek within streams. +/// Blocking seek within streams.\ +/// +/// The `Seek` trait provides a cursor which can be moved within a stream of +/// bytes. +/// +/// The stream typically has a fixed size, allowing seeking relative to either +/// end or the current offset. /// /// This trait is the `embedded-io` equivalent of [`std::io::Seek`]. pub trait Seek: ErrorType { /// Seek to an offset, in bytes, in a stream. + /// A seek beyond the end of a stream is allowed, but behavior is defined + /// by the implementation. + /// + /// If the seek operation completed successfully, + /// this method returns the new position from the start of the stream. + /// That position can be used later with [`SeekFrom::Start`]. + /// + /// # Errors + /// + /// Seeking can fail, for example because it might involve flushing a buffer. + /// + /// Seeking to a negative offset is considered an error. fn seek(&mut self, pos: SeekFrom) -> Result; /// Rewind to the beginning of a stream. + /// + /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. + /// + /// # Errors + /// + /// Rewinding can fail, for example because it might involve flushing a buffer. fn rewind(&mut self) -> Result<(), Self::Error> { self.seek(SeekFrom::Start(0))?; Ok(()) } /// Returns the current seek position from the start of the stream. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(0))`. fn stream_position(&mut self) -> Result { self.seek(SeekFrom::Current(0)) } + + /// Seeks relative to the current position. + /// + /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but + /// doesn't return the new position which can allow some implementations + /// to perform more efficient seeks. + fn seek_relative(&mut self, offset: i64) -> Result<(), Self::Error> { + self.seek(SeekFrom::Current(offset))?; + Ok(()) + } } /// Get whether a reader is ready. From 12e427f54571b1284548b75dd3992d58a5ecfd4c Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Wed, 27 Aug 2025 15:57:13 +0200 Subject: [PATCH 184/201] io: `BufRead` super trait of `Read` --- embedded-io/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 9e2e528a9..1341d1aa5 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -355,7 +355,7 @@ pub trait Read: ErrorType { /// Blocking buffered reader. /// /// This trait is the `embedded-io` equivalent of [`std::io::BufRead`]. -pub trait BufRead: ErrorType { +pub trait BufRead: Read { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. /// /// If no bytes are currently available to read, this function blocks until at least one byte is available. From 9d3bcb94552077253bdbd16e6f64ddb9dbcc8afa Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Sun, 31 Aug 2025 16:51:13 +0200 Subject: [PATCH 185/201] io: make ReadReady, io_async::BufRead, and WriteReady super traits --- embedded-io-async/src/lib.rs | 2 +- embedded-io/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index eef50ebcd..cc3e35ec9 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -82,7 +82,7 @@ pub trait Read: ErrorType { /// Async buffered reader. /// /// This trait is the `embedded-io-async` equivalent of [`std::io::BufRead`]. -pub trait BufRead: ErrorType { +pub trait BufRead: Read { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. /// /// If no bytes are currently available to read, this function waits until at least one byte is available. diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 2115e695d..a0d5b1a18 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -528,7 +528,7 @@ pub trait Seek: ErrorType { /// /// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read /// only when it is ready. -pub trait ReadReady: ErrorType { +pub trait ReadReady: Read { /// Get whether the reader is ready for immediately reading. /// /// This usually means that there is either some bytes have been received and are buffered and ready to be read, @@ -542,7 +542,7 @@ pub trait ReadReady: ErrorType { /// /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. -pub trait WriteReady: ErrorType { +pub trait WriteReady: Write { /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. From f828cf7365d91129bc843e7330abce8e31c21a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 4 Sep 2025 11:15:01 +0200 Subject: [PATCH 186/201] feat: raise embedded-io-async's MSRV to 1.81 This is the same MSRV as embedded-io, which embedded-io-async depends on. --- embedded-io-async/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 3b878a825..081567bc1 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-io-async" version = "0.6.1" edition = "2021" -rust-version = "1.75" +rust-version = "1.81" description = "Async embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" From e72721f935b6d8fbe3c6859c42ff71f28625fc50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 4 Sep 2025 11:09:17 +0200 Subject: [PATCH 187/201] feat: implement `Read`, `BufRead`, and `Write` for `VecDeque` --- embedded-io-async/src/impls/mod.rs | 2 + embedded-io-async/src/impls/vec_deque.rs | 83 ++++++++++++++++++++++ embedded-io/src/impls/mod.rs | 2 + embedded-io/src/impls/vec_deque.rs | 90 ++++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 embedded-io-async/src/impls/vec_deque.rs create mode 100644 embedded-io/src/impls/vec_deque.rs diff --git a/embedded-io-async/src/impls/mod.rs b/embedded-io-async/src/impls/mod.rs index e79b9b8bf..83c0f33a0 100644 --- a/embedded-io-async/src/impls/mod.rs +++ b/embedded-io-async/src/impls/mod.rs @@ -5,3 +5,5 @@ mod slice_ref; mod boxx; #[cfg(feature = "alloc")] mod vec; +#[cfg(feature = "alloc")] +mod vec_deque; diff --git a/embedded-io-async/src/impls/vec_deque.rs b/embedded-io-async/src/impls/vec_deque.rs new file mode 100644 index 000000000..c9bbec302 --- /dev/null +++ b/embedded-io-async/src/impls/vec_deque.rs @@ -0,0 +1,83 @@ +//! Adapted from std. + +use alloc::collections::vec_deque::VecDeque; + +use crate::{BufRead, Read, ReadExactError, Write}; + +/// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Read for VecDeque { + /// Fill `buf` with the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `read` will be needed to read the entire content. + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + let (ref mut front, _) = self.as_slices(); + let n = Read::read(front, buf).await?; + self.drain(..n); + Ok(n) + } + + #[inline] + async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { + let (front, back) = self.as_slices(); + + // Use only the front buffer if it is big enough to fill `buf`, else use + // the back buffer too. + match buf.split_at_mut_checked(front.len()) { + None => buf.copy_from_slice(&front[..buf.len()]), + Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) { + Some((back, _)) => { + buf_front.copy_from_slice(front); + buf_back.copy_from_slice(back); + } + None => { + self.clear(); + return Err(ReadExactError::UnexpectedEof); + } + }, + } + + self.drain(..buf.len()); + Ok(()) + } +} + +/// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl BufRead for VecDeque { + /// Returns the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. + #[inline] + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + let (front, _) = self.as_slices(); + Ok(front) + } + + #[inline] + fn consume(&mut self, amt: usize) { + self.drain(..amt); + } +} + +/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for VecDeque { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + self.extend(buf); + Ok(buf.len()) + } + + #[inline] + async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + self.extend(buf); + Ok(()) + } + + #[inline] + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/embedded-io/src/impls/mod.rs b/embedded-io/src/impls/mod.rs index e79b9b8bf..83c0f33a0 100644 --- a/embedded-io/src/impls/mod.rs +++ b/embedded-io/src/impls/mod.rs @@ -5,3 +5,5 @@ mod slice_ref; mod boxx; #[cfg(feature = "alloc")] mod vec; +#[cfg(feature = "alloc")] +mod vec_deque; diff --git a/embedded-io/src/impls/vec_deque.rs b/embedded-io/src/impls/vec_deque.rs new file mode 100644 index 000000000..8af5a252a --- /dev/null +++ b/embedded-io/src/impls/vec_deque.rs @@ -0,0 +1,90 @@ +//! Adapted from std. + +use core::convert::Infallible; + +use alloc::collections::vec_deque::VecDeque; + +use crate::{BufRead, ErrorType, Read, ReadExactError, Write}; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl ErrorType for VecDeque { + type Error = Infallible; +} + +/// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Read for VecDeque { + /// Fill `buf` with the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `read` will be needed to read the entire content. + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + let (ref mut front, _) = self.as_slices(); + let n = Read::read(front, buf)?; + self.drain(..n); + Ok(n) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { + let (front, back) = self.as_slices(); + + // Use only the front buffer if it is big enough to fill `buf`, else use + // the back buffer too. + match buf.split_at_mut_checked(front.len()) { + None => buf.copy_from_slice(&front[..buf.len()]), + Some((buf_front, buf_back)) => match back.split_at_checked(buf_back.len()) { + Some((back, _)) => { + buf_front.copy_from_slice(front); + buf_back.copy_from_slice(back); + } + None => { + self.clear(); + return Err(ReadExactError::UnexpectedEof); + } + }, + } + + self.drain(..buf.len()); + Ok(()) + } +} + +/// BufRead is implemented for `VecDeque` by reading bytes from the front of the `VecDeque`. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl BufRead for VecDeque { + /// Returns the contents of the "front" slice as returned by + /// [`as_slices`][`VecDeque::as_slices`]. If the contained byte slices of the `VecDeque` are + /// discontiguous, multiple calls to `fill_buf` will be needed to read the entire content. + #[inline] + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + let (front, _) = self.as_slices(); + Ok(front) + } + + #[inline] + fn consume(&mut self, amt: usize) { + self.drain(..amt); + } +} + +/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for VecDeque { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.extend(buf); + Ok(buf.len()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + self.extend(buf); + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} From ad6a7d184999b7094039035975c563f2dd9412a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 4 Sep 2025 12:17:57 +0200 Subject: [PATCH 188/201] feat: implement `ReadRead`, `WriteReady` for `VecDeque` --- embedded-io/src/impls/vec_deque.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/embedded-io/src/impls/vec_deque.rs b/embedded-io/src/impls/vec_deque.rs index 8af5a252a..1e0d5b323 100644 --- a/embedded-io/src/impls/vec_deque.rs +++ b/embedded-io/src/impls/vec_deque.rs @@ -4,7 +4,7 @@ use core::convert::Infallible; use alloc::collections::vec_deque::VecDeque; -use crate::{BufRead, ErrorType, Read, ReadExactError, Write}; +use crate::{BufRead, ErrorType, Read, ReadExactError, ReadReady, Write, WriteReady}; #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] impl ErrorType for VecDeque { @@ -88,3 +88,19 @@ impl Write for VecDeque { Ok(()) } } + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl ReadReady for VecDeque { + #[inline] + fn read_ready(&mut self) -> Result { + Ok(true) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl WriteReady for VecDeque { + #[inline] + fn write_ready(&mut self) -> Result { + Ok(true) + } +} From 82e1c7ba59469fcbdd18d498698e0048fddeffc9 Mon Sep 17 00:00:00 2001 From: Oakchris1955 <80592203+Oakchris1955@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:37:18 +0300 Subject: [PATCH 189/201] fix: missing impls from and to std's io::ErrorKind for WriteZero --- embedded-io/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index a0d5b1a18..292621740 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -137,6 +137,7 @@ impl From for std::io::ErrorKind { ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, + ErrorKind::WriteZero => std::io::ErrorKind::WriteZero, _ => std::io::ErrorKind::Other, } } @@ -163,6 +164,7 @@ impl From for ErrorKind { std::io::ErrorKind::Interrupted => ErrorKind::Interrupted, std::io::ErrorKind::Unsupported => ErrorKind::Unsupported, std::io::ErrorKind::OutOfMemory => ErrorKind::OutOfMemory, + std::io::ErrorKind::WriteZero => ErrorKind::WriteZero, _ => ErrorKind::Other, } } From 4cfb197e600706767cb0555d053921403bc3f1b8 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Tue, 9 Sep 2025 13:50:40 +0200 Subject: [PATCH 190/201] io-adapter: forward calls to the std adapter --- embedded-io-adapters/src/std.rs | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index 90a2f8525..98a65429f 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -60,6 +60,18 @@ impl embedded_io::Write for FromStd { Err(e) => Err(e), } } + + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + self.inner.write_all(buf) + } + + fn write_fmt( + &mut self, + fmt: core::fmt::Arguments<'_>, + ) -> Result<(), embedded_io::WriteFmtError> { + Ok(self.inner.write_fmt(fmt)?) + } + fn flush(&mut self) -> Result<(), Self::Error> { self.inner.flush() } @@ -69,6 +81,14 @@ impl embedded_io::Seek for FromStd { fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { self.inner.seek(pos.into()) } + + fn rewind(&mut self) -> Result<(), Self::Error> { + self.inner.rewind() + } + + fn stream_position(&mut self) -> Result { + self.inner.stream_position() + } } /// Adapter to `std::io` traits. @@ -115,6 +135,22 @@ impl std::io::Write for ToStd { Err(e) => Err(to_std_error(e)), } } + + fn write_all(&mut self, buf: &[u8]) -> Result<(), std::io::Error> { + self.inner.write_all(buf).map_err(to_std_error) + } + + fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> Result<(), std::io::Error> { + match self.inner.write_fmt(fmt) { + Ok(()) => Ok(()), + Err(e @ embedded_io::WriteFmtError::FmtError) => Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("{e:?}"), + )), + Err(embedded_io::WriteFmtError::Other(e)) => Err(to_std_error(e)), + } + } + fn flush(&mut self) -> Result<(), std::io::Error> { self.inner.flush().map_err(to_std_error) } @@ -124,6 +160,14 @@ impl std::io::Seek for ToStd { fn seek(&mut self, pos: std::io::SeekFrom) -> Result { self.inner.seek(pos.into()).map_err(to_std_error) } + + fn rewind(&mut self) -> Result<(), std::io::Error> { + self.inner.rewind().map_err(to_std_error) + } + + fn stream_position(&mut self) -> Result { + self.inner.stream_position().map_err(to_std_error) + } } /// Convert a embedded-io error to a [`std::io::Error`] From b4c35b61366be8f419393af6b50e52b683c10337 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Tue, 9 Sep 2025 13:38:35 +0200 Subject: [PATCH 191/201] io: Fix `Read::read_exact` std adapter --- embedded-io-adapters/src/std.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index 90a2f8525..cfd96d9f6 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -40,6 +40,19 @@ impl embedded_io::Read for FromStd { fn read(&mut self, buf: &mut [u8]) -> Result { self.inner.read(buf) } + + fn read_exact( + &mut self, + buf: &mut [u8], + ) -> Result<(), embedded_io::ReadExactError> { + match self.inner.read_exact(buf) { + Ok(()) => Ok(()), + Err(error) if error.kind() == std::io::ErrorKind::UnexpectedEof => { + Err(embedded_io::ReadExactError::UnexpectedEof) + } + Err(error) => Err(error.into()), + } + } } impl embedded_io::BufRead for FromStd { @@ -105,6 +118,17 @@ impl std::io::Read for ToStd { fn read(&mut self, buf: &mut [u8]) -> Result { self.inner.read(buf).map_err(to_std_error) } + + fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { + match self.inner.read_exact(buf) { + Ok(()) => Ok(()), + Err(e @ embedded_io::ReadExactError::UnexpectedEof) => Err(std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + format!("{e:?}"), + )), + Err(embedded_io::ReadExactError::Other(e)) => Err(to_std_error(e)), + } + } } impl std::io::Write for ToStd { From 050e8827d365479dc89952be27a4dd4ef570e9a0 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Wed, 10 Sep 2025 11:00:25 +0200 Subject: [PATCH 192/201] io-adapters: forward seek_relative in std adapter and require Rust 1.81 --- embedded-io-adapters/Cargo.toml | 2 +- embedded-io-adapters/src/std.rs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index ea88ee659..8bb87679a 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-io-adapters" version = "0.6.2" edition = "2021" -rust-version = "1.60" +rust-version = "1.81" description = "Adapters between the `embedded-io` traits and other I/O traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index a76c005da..776bc8758 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -102,6 +102,10 @@ impl embedded_io::Seek for FromStd { fn stream_position(&mut self) -> Result { self.inner.stream_position() } + + fn seek_relative(&mut self, offset: i64) -> Result<(), Self::Error> { + self.inner.seek_relative(offset) + } } /// Adapter to `std::io` traits. @@ -167,10 +171,9 @@ impl std::io::Write for ToStd { fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> Result<(), std::io::Error> { match self.inner.write_fmt(fmt) { Ok(()) => Ok(()), - Err(e @ embedded_io::WriteFmtError::FmtError) => Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("{e:?}"), - )), + Err(e @ embedded_io::WriteFmtError::FmtError) => { + Err(std::io::Error::other(format!("{e:?}"))) + } Err(embedded_io::WriteFmtError::Other(e)) => Err(to_std_error(e)), } } @@ -192,6 +195,10 @@ impl std::io::Seek for ToStd { fn stream_position(&mut self) -> Result { self.inner.stream_position().map_err(to_std_error) } + + fn seek_relative(&mut self, offset: i64) -> std::io::Result<()> { + self.inner.seek_relative(offset).map_err(to_std_error) + } } /// Convert a embedded-io error to a [`std::io::Error`] From 83f17d49e9f2a5ee46d940cfb6d68d020721fd22 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Wed, 10 Sep 2025 13:23:58 +0200 Subject: [PATCH 193/201] io: forward calls in blanket impls --- embedded-io-async/src/lib.rs | 22 ++++++++++++++++++++++ embedded-io/src/lib.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index cc3e35ec9..1a8dbf7ed 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -175,13 +175,20 @@ impl Read for &mut T { async fn read(&mut self, buf: &mut [u8]) -> Result { T::read(self, buf).await } + + #[inline] + async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { + T::read_exact(self, buf).await + } } impl BufRead for &mut T { + #[inline] async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { T::fill_buf(self).await } + #[inline] fn consume(&mut self, amt: usize) { T::consume(self, amt); } @@ -197,6 +204,11 @@ impl Write for &mut T { async fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self).await } + + #[inline] + async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + T::write_all(self, buf).await + } } impl Seek for &mut T { @@ -204,4 +216,14 @@ impl Seek for &mut T { async fn seek(&mut self, pos: SeekFrom) -> Result { T::seek(self, pos).await } + + #[inline] + async fn rewind(&mut self) -> Result<(), Self::Error> { + T::rewind(self).await + } + + #[inline] + async fn stream_position(&mut self) -> Result { + T::stream_position(self).await + } } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 292621740..38505cd29 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -558,13 +558,20 @@ impl Read for &mut T { fn read(&mut self, buf: &mut [u8]) -> Result { T::read(self, buf) } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> { + T::read_exact(self, buf) + } } impl BufRead for &mut T { + #[inline] fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { T::fill_buf(self) } + #[inline] fn consume(&mut self, amt: usize) { T::consume(self, amt); } @@ -580,6 +587,11 @@ impl Write for &mut T { fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self) } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + T::write_all(self, buf) + } } impl Seek for &mut T { @@ -587,6 +599,21 @@ impl Seek for &mut T { fn seek(&mut self, pos: SeekFrom) -> Result { T::seek(self, pos) } + + #[inline] + fn rewind(&mut self) -> Result<(), Self::Error> { + T::rewind(self) + } + + #[inline] + fn stream_position(&mut self) -> Result { + T::stream_position(self) + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> Result<(), Self::Error> { + T::seek_relative(self, offset) + } } impl ReadReady for &mut T { From 43a1bbb57814046cb222be06aaae2eb5a00f7a67 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Tue, 16 Sep 2025 09:59:43 +0200 Subject: [PATCH 194/201] [io] Add specialized read_exact and write_all for slices --- embedded-io-async/src/impls/slice_mut.rs | 9 +++++++++ embedded-io-async/src/impls/slice_ref.rs | 11 +++++++++++ embedded-io-async/src/impls/vec.rs | 6 ++++++ embedded-io/src/impls/slice_mut.rs | 9 +++++++++ embedded-io/src/impls/slice_ref.rs | 9 +++++++++ embedded-io/src/impls/vec.rs | 6 ++++++ 6 files changed, 50 insertions(+) diff --git a/embedded-io-async/src/impls/slice_mut.rs b/embedded-io-async/src/impls/slice_mut.rs index bd64d1320..fbbed187d 100644 --- a/embedded-io-async/src/impls/slice_mut.rs +++ b/embedded-io-async/src/impls/slice_mut.rs @@ -24,4 +24,13 @@ impl Write for &mut [u8] { *self = b; Ok(amt) } + + #[inline] + async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + if self.len() < buf.len() { + return Err(SliceWriteError::Full); + } + self.write(buf).await?; + Ok(()) + } } diff --git a/embedded-io-async/src/impls/slice_ref.rs b/embedded-io-async/src/impls/slice_ref.rs index a6a4ba807..f49abbd62 100644 --- a/embedded-io-async/src/impls/slice_ref.rs +++ b/embedded-io-async/src/impls/slice_ref.rs @@ -22,6 +22,17 @@ impl Read for &[u8] { *self = b; Ok(amt) } + + async fn read_exact( + &mut self, + buf: &mut [u8], + ) -> Result<(), embedded_io::ReadExactError> { + if self.len() < buf.len() { + return Err(crate::ReadExactError::UnexpectedEof); + } + self.read(buf).await?; + Ok(()) + } } impl BufRead for &[u8] { diff --git a/embedded-io-async/src/impls/vec.rs b/embedded-io-async/src/impls/vec.rs index c24405e39..a9741f1e0 100644 --- a/embedded-io-async/src/impls/vec.rs +++ b/embedded-io-async/src/impls/vec.rs @@ -9,4 +9,10 @@ impl Write for Vec { self.extend_from_slice(buf); Ok(buf.len()) } + + #[inline] + async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + self.write(buf).await?; + Ok(()) + } } diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index cee991ea6..86390652a 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -42,6 +42,15 @@ impl Write for &mut [u8] { Ok(amt) } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + if self.len() < buf.len() { + return Err(SliceWriteError::Full); + } + self.write(buf)?; + Ok(()) + } + #[inline] fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) diff --git a/embedded-io/src/impls/slice_ref.rs b/embedded-io/src/impls/slice_ref.rs index 0270aca27..af928f9a8 100644 --- a/embedded-io/src/impls/slice_ref.rs +++ b/embedded-io/src/impls/slice_ref.rs @@ -26,6 +26,15 @@ impl Read for &[u8] { *self = b; Ok(amt) } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), crate::ReadExactError> { + if self.len() < buf.len() { + return Err(crate::ReadExactError::UnexpectedEof); + } + self.read(buf)?; + Ok(()) + } } impl BufRead for &[u8] { diff --git a/embedded-io/src/impls/vec.rs b/embedded-io/src/impls/vec.rs index 3b279c564..d8fbb638b 100644 --- a/embedded-io/src/impls/vec.rs +++ b/embedded-io/src/impls/vec.rs @@ -14,6 +14,12 @@ impl Write for Vec { Ok(buf.len()) } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + self.write(buf)?; + Ok(()) + } + #[inline] fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) From 9bb5dea4feed42ceac0bcc43251cf2a2fe3d653f Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Tue, 16 Sep 2025 10:14:32 +0200 Subject: [PATCH 195/201] [io] Forward calls in Box adapters --- embedded-io-async/src/impls/boxx.rs | 23 +++++++++++++++++++ embedded-io/src/impls/boxx.rs | 34 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/embedded-io-async/src/impls/boxx.rs b/embedded-io-async/src/impls/boxx.rs index d6186b0c7..b3c1bab56 100644 --- a/embedded-io-async/src/impls/boxx.rs +++ b/embedded-io-async/src/impls/boxx.rs @@ -7,6 +7,14 @@ impl Read for Box { async fn read(&mut self, buf: &mut [u8]) -> Result { T::read(self, buf).await } + + #[inline] + async fn read_exact( + &mut self, + buf: &mut [u8], + ) -> Result<(), crate::ReadExactError> { + T::read_exact(self, buf).await + } } #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] @@ -29,6 +37,11 @@ impl Write for Box { T::write(self, buf).await } + #[inline] + async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + T::write_all(self, buf).await + } + #[inline] async fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self).await @@ -41,4 +54,14 @@ impl Seek for Box { async fn seek(&mut self, pos: SeekFrom) -> Result { T::seek(self, pos).await } + + #[inline] + async fn rewind(&mut self) -> Result<(), Self::Error> { + T::rewind(self).await + } + + #[inline] + async fn stream_position(&mut self) -> Result { + T::stream_position(self).await + } } diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs index 037a6be0c..c7bb01a52 100644 --- a/embedded-io/src/impls/boxx.rs +++ b/embedded-io/src/impls/boxx.rs @@ -12,6 +12,11 @@ impl Read for Box { fn read(&mut self, buf: &mut [u8]) -> Result { T::read(self, buf) } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), crate::ReadExactError> { + T::read_exact(self, buf) + } } #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] @@ -20,6 +25,7 @@ impl BufRead for Box { T::fill_buf(self) } + #[inline] fn consume(&mut self, amt: usize) { T::consume(self, amt); } @@ -32,6 +38,19 @@ impl Write for Box { T::write(self, buf) } + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + T::write_all(self, buf) + } + + #[inline] + fn write_fmt( + &mut self, + fmt: core::fmt::Arguments<'_>, + ) -> Result<(), crate::WriteFmtError> { + T::write_fmt(self, fmt) + } + #[inline] fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self) @@ -44,6 +63,21 @@ impl Seek for Box { fn seek(&mut self, pos: crate::SeekFrom) -> Result { T::seek(self, pos) } + + #[inline] + fn rewind(&mut self) -> Result<(), Self::Error> { + T::rewind(self) + } + + #[inline] + fn stream_position(&mut self) -> Result { + T::stream_position(self) + } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> Result<(), Self::Error> { + T::seek_relative(self, offset) + } } #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] From 33327288b73eb88cdb177f12555738837e2929ec Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Tue, 23 Sep 2025 13:03:31 +0200 Subject: [PATCH 196/201] Update embedded-io README for defmt feature and MSRV As a preparation for the (hopefully) upcoming release. Looking forward to #679. --- embedded-io/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-io/README.md b/embedded-io/README.md index 6c228d8b4..2b09d9c05 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -23,11 +23,11 @@ targets. - **`std`**: Adds `From` impls to convert to/from `std::io` structs. - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. -- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. +- **`defmt`**: Derive `defmt::Format` from `defmt` 1.x for enums and structs. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.81 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. From d75daf977608325d19a5745a42705ba3391d0b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Thu, 4 Sep 2025 10:44:24 +0200 Subject: [PATCH 197/201] feat(async-io): make async `Write::flush` a required method --- embedded-io-async/src/impls/slice_mut.rs | 5 +++++ embedded-io-async/src/impls/vec.rs | 5 +++++ embedded-io-async/src/lib.rs | 4 +--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/embedded-io-async/src/impls/slice_mut.rs b/embedded-io-async/src/impls/slice_mut.rs index fbbed187d..a0800cd8d 100644 --- a/embedded-io-async/src/impls/slice_mut.rs +++ b/embedded-io-async/src/impls/slice_mut.rs @@ -25,6 +25,11 @@ impl Write for &mut [u8] { Ok(amt) } + #[inline] + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + #[inline] async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { if self.len() < buf.len() { diff --git a/embedded-io-async/src/impls/vec.rs b/embedded-io-async/src/impls/vec.rs index a9741f1e0..d8aba558b 100644 --- a/embedded-io-async/src/impls/vec.rs +++ b/embedded-io-async/src/impls/vec.rs @@ -10,6 +10,11 @@ impl Write for Vec { Ok(buf.len()) } + #[inline] + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + #[inline] async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { self.write(buf).await?; diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 1a8dbf7ed..a421e54cb 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -127,9 +127,7 @@ pub trait Write: ErrorType { async fn write(&mut self, buf: &[u8]) -> Result; /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. - async fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } + async fn flush(&mut self) -> Result<(), Self::Error>; /// Write an entire buffer into this writer. /// From 9123afb52890b9ec26010a3f919cd7d6328f3381 Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 12 Aug 2025 18:02:07 +0200 Subject: [PATCH 198/201] Release embedded-io{,*} 0.7 Contributes-To: https://github.com/rust-embedded/embedded-hal/issues/566 --- embedded-io-adapters/CHANGELOG.md | 5 +++++ embedded-io-adapters/Cargo.toml | 6 +++--- embedded-io-async/CHANGELOG.md | 12 +++++++++--- embedded-io-async/Cargo.toml | 4 ++-- embedded-io/CHANGELOG.md | 18 ++++++++++++------ embedded-io/Cargo.toml | 2 +- 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index 00763a22e..558fb9654 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.7.0 - 2025-09-30 + +- Update to embedded-io and embedded-io-async 0.7 +- Fix missing forwarding of provided method into std types + ## 0.6.2 – 2025-08-06 - Added `ToFmt` adapter for `core::fmt::Write`. diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 8bb87679a..6a2130a16 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.6.2" +version = "0.7.0" edition = "2021" rust-version = "1.81" description = "Adapters between the `embedded-io` traits and other I/O traits" @@ -18,8 +18,8 @@ tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std" futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"] [dependencies] -embedded-io = { version = "0.6", path = "../embedded-io" } -embedded-io-async = { version = "0.6.1", path = "../embedded-io-async", optional = true } +embedded-io = { version = "0.7", path = "../embedded-io" } +embedded-io-async = { version = "0.7", path = "../embedded-io-async", optional = true } futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md index 5f421e351..6fb5d519b 100644 --- a/embedded-io-async/CHANGELOG.md +++ b/embedded-io-async/CHANGELOG.md @@ -5,9 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased - -Add unreleased changes here +## 0.7.0 - 2025-09-30 + +- Make `Write::flush()` a required method, aligning with std and embedded-io +- Update to and align with embedded-io 0.7: + - Error type is updated to include core::Error + - Update `defmt` dependency to 1.0; rename feature from `defmt_03` to `defmt` + - Require `Read` and `Write` to be implemented for various Read and Write traits + - Fix missing method forwardings for blanket implementations + - Documentation updates ## 0.6.1 - 2023-11-28 diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 081567bc1..5148ebfa3 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-async" -version = "0.6.1" +version = "0.7.0" edition = "2021" rust-version = "1.81" description = "Async embedded IO traits" @@ -18,7 +18,7 @@ alloc = ["embedded-io/alloc"] defmt = ["dep:defmt", "embedded-io/defmt"] [dependencies] -embedded-io = { version = "0.6.1", path = "../embedded-io" } +embedded-io = { version = "0.7.0", path = "../embedded-io" } defmt = { package = "defmt", version = "1", optional = true } [package.metadata.docs.rs] diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index f7c2e0e67..2da30da3b 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,12 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased - -- Added `core::error::Error` implementations for every custom `impl Error` -- Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` -- Increased MSRV to 1.81 due to `core::error::Error` -- Implemented `ReadReady` for `&[u8]` and `WriteReady` for `&mut [u8]` +## 0.7.0 - 2025-09-30 + +- Add trait dependency on `core::error::Error` to this crate's `Error` trait + - Implement the trait on all provided implementations + - Migrate `std` feature-gated `std::error::Error` implementations to `core::error::Error` +- Updat `defmt` dependency to 1.0; rename feature from `defmt_03` to `defmt` +- Increase MSRV to 1.81 due to `core::error::Error` +- Implement `ReadReady` for `&[u8]` and `WriteReady` for `&mut [u8]` +- Require `Read` and `Write` to be implemented for various Read and Write traits +- Add provided `.seek_relative()` method to Seek +- Fix missing method forwardings for blanket implementations +- Specialize `.read_exact()` and `.write_all()` for slices ## 0.6.1 - 2023-10-22 diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index 48b30cfb3..ffbf4f396 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io" -version = "0.6.1" +version = "0.7.0" edition = "2021" rust-version = "1.81" description = "Embedded IO traits" From 4949c740a2afa842689f64baaee84d6c2071f2e9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Oct 2025 01:02:57 +0200 Subject: [PATCH 199/201] io: remove `Read`/`Write` supertrait for `ReadReady`/`WriteReady`. `ReadReady`/`WriteReady` are shared between blocking and async (the embedded-io-async crate just reexports them) so they shouldn't have the blocking `Read`/`Write` as supertraits. --- embedded-io/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 38505cd29..0918cdb59 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -530,7 +530,7 @@ pub trait Seek: ErrorType { /// /// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read /// only when it is ready. -pub trait ReadReady: Read { +pub trait ReadReady: ErrorType { /// Get whether the reader is ready for immediately reading. /// /// This usually means that there is either some bytes have been received and are buffered and ready to be read, @@ -544,7 +544,7 @@ pub trait ReadReady: Read { /// /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. -pub trait WriteReady: Write { +pub trait WriteReady: ErrorType { /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. From ed3297e54f6fe0c0403747f9f116488ac1eb73da Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 1 Oct 2025 01:08:35 +0200 Subject: [PATCH 200/201] Release embedded-io 0.7.1 --- embedded-io-async/CHANGELOG.md | 1 + embedded-io/CHANGELOG.md | 5 +++++ embedded-io/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md index 6fb5d519b..7738ccbb5 100644 --- a/embedded-io-async/CHANGELOG.md +++ b/embedded-io-async/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Require `Read` and `Write` to be implemented for various Read and Write traits - Fix missing method forwardings for blanket implementations - Documentation updates +- Implement `Read`, `ReadReady`, `BufRead`, `Write`, and `WriteReady` for `VecDeque` ## 0.6.1 - 2023-11-28 diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 2da30da3b..0fc0c11d0 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.7.1 - 2025-09-30 + +- Do not require `Read` and `Write` to be implemented for `ReadReady` and `WriteReady`. + ## 0.7.0 - 2025-09-30 - Add trait dependency on `core::error::Error` to this crate's `Error` trait @@ -17,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add provided `.seek_relative()` method to Seek - Fix missing method forwardings for blanket implementations - Specialize `.read_exact()` and `.write_all()` for slices +- Implement `Read`, `ReadReady`, `BufRead`, `Write`, and `WriteReady` for `VecDeque` ## 0.6.1 - 2023-10-22 diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index ffbf4f396..e02ecdc4c 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io" -version = "0.7.0" +version = "0.7.1" edition = "2021" rust-version = "1.81" description = "Embedded IO traits" From f9bfce551c53cd5ef72d7ec905a7f7ad67bbcc24 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Wed, 1 Oct 2025 16:44:24 +0200 Subject: [PATCH 201/201] embedded-can: bump defmt to v1, rename feature from defmt-03 to defmt --- embedded-can/CHANGELOG.md | 2 ++ embedded-can/Cargo.toml | 5 +---- embedded-can/src/id.rs | 6 +++--- embedded-can/src/lib.rs | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/embedded-can/CHANGELOG.md b/embedded-can/CHANGELOG.md index f8719d632..5e623528a 100644 --- a/embedded-can/CHANGELOG.md +++ b/embedded-can/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `core::error::Error` implementations for every custom `impl Error` - Increased MSRV to 1.81 due to `core::error::Error` +- Bumped `defmt` to v1 +- `defmt-03` feature is now named `defmt` ## [v0.4.1] - 2022-09-28 diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 105f286b0..7a638db08 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -14,7 +14,4 @@ repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] nb = "1" -defmt = { version = "0.3", optional = true } - -[features] -defmt-03 = ["dep:defmt"] +defmt = { version = "1", optional = true } diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index b0d6d4d5f..3f96474f8 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -2,7 +2,7 @@ /// Standard 11-bit CAN Identifier (`0..=0x7FF`). #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StandardId(u16); impl StandardId { @@ -45,7 +45,7 @@ impl StandardId { /// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ExtendedId(u32); impl ExtendedId { @@ -95,7 +95,7 @@ impl ExtendedId { /// A CAN Identifier (standard or extended). #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Id { /// Standard 11-bit Identifier (`0..=0x7FF`). Standard(StandardId), diff --git a/embedded-can/src/lib.rs b/embedded-can/src/lib.rs index bb010b965..86248f3c2 100644 --- a/embedded-can/src/lib.rs +++ b/embedded-can/src/lib.rs @@ -73,7 +73,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common CAN errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// The peripheral receive buffer was overrun.