Skip to content

Commit 7dbaf6d

Browse files
committed
bus: make AtomicDevice use a per-bus "busy" flag, not per-device.
Needed for soundness.
1 parent e00ffa0 commit 7dbaf6d

File tree

4 files changed

+52
-32
lines changed

4 files changed

+52
-32
lines changed

embedded-hal-bus/src/i2c/atomic.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use core::cell::UnsafeCell;
21
use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c};
32

3+
use crate::util::AtomicCell;
4+
45
/// `UnsafeCell`-based shared bus [`I2c`] implementation.
56
///
67
/// 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};
1819
///
1920
/// ```
2021
/// use embedded_hal_bus::i2c;
21-
/// use core::cell::UnsafeCell;
22+
/// use embedded_hal_bus::util::AtomicCell;
2223
/// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind};
2324
/// # pub struct Sensor<I2C> {
2425
/// # i2c: I2C,
@@ -56,19 +57,18 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c};
5657
/// # let hal = Hal;
5758
///
5859
/// let i2c = hal.i2c();
59-
/// let i2c_unsafe_cell = UnsafeCell::new(i2c);
60+
/// let i2c_cell = AtomicCell::new(i2c);
6061
/// let mut temperature_sensor = TemperatureSensor::new(
61-
/// i2c::AtomicDevice::new(&i2c_unsafe_cell),
62+
/// i2c::AtomicDevice::new(&i2c_cell),
6263
/// 0x20,
6364
/// );
6465
/// let mut pressure_sensor = PressureSensor::new(
65-
/// i2c::AtomicDevice::new(&i2c_unsafe_cell),
66+
/// i2c::AtomicDevice::new(&i2c_cell),
6667
/// 0x42,
6768
/// );
6869
/// ```
6970
pub struct AtomicDevice<'a, T> {
70-
bus: &'a UnsafeCell<T>,
71-
busy: portable_atomic::AtomicBool,
71+
bus: &'a AtomicCell<T>,
7272
}
7373

7474
#[derive(Debug, Copy, Clone)]
@@ -100,18 +100,16 @@ where
100100
{
101101
/// Create a new `AtomicDevice`.
102102
#[inline]
103-
pub fn new(bus: &'a UnsafeCell<T>) -> Self {
104-
Self {
105-
bus,
106-
busy: portable_atomic::AtomicBool::from(false),
107-
}
103+
pub fn new(bus: &'a AtomicCell<T>) -> Self {
104+
Self { bus }
108105
}
109106

110107
fn lock<R, F>(&self, f: F) -> Result<R, AtomicError<T::Error>>
111108
where
112109
F: FnOnce(&mut T) -> Result<R, <T as ErrorType>::Error>,
113110
{
114-
self.busy
111+
self.bus
112+
.busy
115113
.compare_exchange(
116114
false,
117115
true,
@@ -120,9 +118,11 @@ where
120118
)
121119
.map_err(|_| AtomicError::<T::Error>::Busy)?;
122120

123-
let result = f(unsafe { &mut *self.bus.get() });
121+
let result = f(unsafe { &mut *self.bus.bus.get() });
124122

125-
self.busy.store(false, core::sync::atomic::Ordering::SeqCst);
123+
self.bus
124+
.busy
125+
.store(false, core::sync::atomic::Ordering::SeqCst);
126126

127127
result.map_err(AtomicError::Other)
128128
}

embedded-hal-bus/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ use defmt_03 as defmt;
1818

1919
pub mod i2c;
2020
pub mod spi;
21+
pub mod util;

embedded-hal-bus/src/spi/atomic.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use core::cell::UnsafeCell;
21
use embedded_hal::delay::DelayNs;
32
use embedded_hal::digital::OutputPin;
43
use embedded_hal::spi::{Error, ErrorKind, ErrorType, Operation, SpiBus, SpiDevice};
54

65
use super::DeviceError;
76
use crate::spi::shared::transaction;
7+
use crate::util::AtomicCell;
88

99
/// `UnsafeCell`-based shared bus [`SpiDevice`] implementation.
1010
///
@@ -19,10 +19,9 @@ use crate::spi::shared::transaction;
1919
/// rules, such as the RTIC framework.
2020
///
2121
pub struct AtomicDevice<'a, BUS, CS, D> {
22-
bus: &'a UnsafeCell<BUS>,
22+
bus: &'a AtomicCell<BUS>,
2323
cs: CS,
2424
delay: D,
25-
busy: portable_atomic::AtomicBool,
2625
}
2726

2827
#[derive(Debug, Copy, Clone)]
@@ -40,13 +39,8 @@ pub enum AtomicError<T: Error> {
4039
impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> {
4140
/// Create a new [`AtomicDevice`].
4241
#[inline]
43-
pub fn new(bus: &'a UnsafeCell<BUS>, cs: CS, delay: D) -> Self {
44-
Self {
45-
bus,
46-
cs,
47-
delay,
48-
busy: portable_atomic::AtomicBool::from(false),
49-
}
42+
pub fn new(bus: &'a AtomicCell<BUS>, cs: CS, delay: D) -> Self {
43+
Self { bus, cs, delay }
5044
}
5145
}
5246

@@ -72,18 +66,15 @@ where
7266
/// The returned device will panic if you try to execute a transaction
7367
/// that contains any operations of type [`Operation::DelayNs`].
7468
#[inline]
75-
pub fn new_no_delay(bus: &'a UnsafeCell<BUS>, cs: CS) -> Self {
69+
pub fn new_no_delay(bus: &'a AtomicCell<BUS>, cs: CS) -> Self {
7670
Self {
7771
bus,
7872
cs,
7973
delay: super::NoDelay,
80-
busy: portable_atomic::AtomicBool::from(false),
8174
}
8275
}
8376
}
8477

85-
unsafe impl<'a, BUS, CS, D> Send for AtomicDevice<'a, BUS, CS, D> {}
86-
8778
impl<T: Error> Error for AtomicError<T> {
8879
fn kind(&self) -> ErrorKind {
8980
match self {
@@ -109,7 +100,8 @@ where
109100
{
110101
#[inline]
111102
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
112-
self.busy
103+
self.bus
104+
.busy
113105
.compare_exchange(
114106
false,
115107
true,
@@ -118,11 +110,13 @@ where
118110
)
119111
.map_err(|_| AtomicError::Busy)?;
120112

121-
let bus = unsafe { &mut *self.bus.get() };
113+
let bus = unsafe { &mut *self.bus.bus.get() };
122114

123115
let result = transaction(operations, bus, &mut self.delay, &mut self.cs);
124116

125-
self.busy.store(false, core::sync::atomic::Ordering::SeqCst);
117+
self.bus
118+
.busy
119+
.store(false, core::sync::atomic::Ordering::SeqCst);
126120

127121
result.map_err(AtomicError::Other)
128122
}

embedded-hal-bus/src/util.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! Utilities shared by all bus types.
2+
3+
use core::cell::UnsafeCell;
4+
5+
/// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice).
6+
///
7+
/// To use `AtomicDevice`, you must wrap the bus with this struct, and then
8+
/// construct multiple `AtomicDevice` instances with references to it.
9+
pub struct AtomicCell<BUS> {
10+
pub(crate) bus: UnsafeCell<BUS>,
11+
pub(crate) busy: portable_atomic::AtomicBool,
12+
}
13+
14+
unsafe impl<BUS: Send> Send for AtomicCell<BUS> {}
15+
unsafe impl<BUS: Send> Sync for AtomicCell<BUS> {}
16+
17+
impl<BUS> AtomicCell<BUS> {
18+
/// Create a new `AtomicCell`
19+
pub fn new(bus: BUS) -> Self {
20+
Self {
21+
bus: UnsafeCell::new(bus),
22+
busy: portable_atomic::AtomicBool::from(false),
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)