diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 6ab831fb..d0ebecf7 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -15,8 +15,18 @@ 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 = ["alloc"] +# 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 = ["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 defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] # Enables additional utilities requiring a global allocator. alloc = [] @@ -26,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.3", default-features = false, optional = true, features = ["require-cas"]} [package.metadata.docs.rs] features = ["std", "async"] diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 2089cb8a..2e6d9db0 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -30,11 +30,17 @@ 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. - **`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`) +- **`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. 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`. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 6420d06c..5f322631 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,7 +8,9 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] mod atomic; +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] pub use atomic::*; #[cfg(feature = "alloc")] diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index b654a54c..5a8356e2 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(any(feature = "portable-atomic", target_has_atomic = "8"))] mod atomic; mod critical_section; mod shared; +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] pub use atomic::*; #[cfg(feature = "alloc")] diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index 739f4b1b..bb16577c 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -1,25 +1,34 @@ //! Utilities shared by all bus types. +#[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). /// /// 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, + pub(crate) busy: AtomicBool, } - +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Send for AtomicCell {} +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Sync for AtomicCell {} +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] impl AtomicCell { /// Create a new `AtomicCell` pub fn new(bus: BUS) -> Self { Self { bus: UnsafeCell::new(bus), - busy: portable_atomic::AtomicBool::from(false), + busy: AtomicBool::from(false), } } }