aboutsummaryrefslogtreecommitdiff
path: root/embassy-extras/src
diff options
context:
space:
mode:
authorLiam Murphy <[email protected]>2021-07-29 15:11:26 +1000
committerLiam Murphy <[email protected]>2021-07-29 15:11:26 +1000
commitd5ba35424d7eef2cc0c501758d214ce3a6febfc1 (patch)
tree8d3b07073b7f5f2ea67bb061a990b019c79aeeb5 /embassy-extras/src
parent4d9514cbcb97343c3a75bfa565d753c44c2a0e27 (diff)
Replace `PeripheralStateUnchecked` with `register_interrupt_unchecked`
Diffstat (limited to 'embassy-extras/src')
-rw-r--r--embassy-extras/src/peripheral.rs66
-rw-r--r--embassy-extras/src/peripheral_shared.rs52
-rw-r--r--embassy-extras/src/usb/mod.rs9
3 files changed, 74 insertions, 53 deletions
diff --git a/embassy-extras/src/peripheral.rs b/embassy-extras/src/peripheral.rs
index 725a58a46..1868edd7d 100644
--- a/embassy-extras/src/peripheral.rs
+++ b/embassy-extras/src/peripheral.rs
@@ -6,42 +6,20 @@ use cortex_m::peripheral::scb::{Exception, SystemHandler, VectActive};
6use cortex_m::peripheral::{NVIC, SCB}; 6use cortex_m::peripheral::{NVIC, SCB};
7use embassy::interrupt::{Interrupt, InterruptExt}; 7use embassy::interrupt::{Interrupt, InterruptExt};
8 8
9/// A version of `PeripheralState` without the `'static` bound,
10/// for cases where the compiler can't statically make sure
11/// that `on_interrupt` doesn't reference anything which might be invalidated.
12///
13/// # Safety
14/// When types implementing this trait are used with `PeripheralMutex`,
15/// no fields referenced by `on_interrupt`'s lifetimes must end without first calling `Drop` on the `PeripheralMutex`.
16pub unsafe trait PeripheralStateUnchecked: Send {
17 type Interrupt: Interrupt;
18 fn on_interrupt(&mut self);
19}
20
21/// A type which can be used as state with `PeripheralMutex`. 9/// A type which can be used as state with `PeripheralMutex`.
22/// 10///
23/// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt, 11/// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt,
24/// and `&mut T` is only `Send` where `T: Send`. 12/// and `&mut T` is only `Send` where `T: Send`.
25/// 13///
26/// It also requires `'static`, because although `Pin` guarantees that the memory of the state won't be invalidated, 14/// It also requires `'static` to be used safely with `PeripheralMutex::register_interrupt`,
15/// because although `Pin` guarantees that the memory of the state won't be invalidated,
27/// it doesn't guarantee that the lifetime will last. 16/// it doesn't guarantee that the lifetime will last.
28pub trait PeripheralState: Send + 'static { 17pub trait PeripheralState: Send {
29 type Interrupt: Interrupt; 18 type Interrupt: Interrupt;
30 fn on_interrupt(&mut self); 19 fn on_interrupt(&mut self);
31} 20}
32 21
33// SAFETY: `T` has to live for `'static` to implement `PeripheralState`, thus its lifetime cannot end. 22pub struct PeripheralMutex<S: PeripheralState> {
34unsafe impl<T> PeripheralStateUnchecked for T
35where
36 T: PeripheralState,
37{
38 type Interrupt = T::Interrupt;
39 fn on_interrupt(&mut self) {
40 self.on_interrupt()
41 }
42}
43
44pub struct PeripheralMutex<S: PeripheralStateUnchecked> {
45 state: UnsafeCell<S>, 23 state: UnsafeCell<S>,
46 24
47 irq_setup_done: bool, 25 irq_setup_done: bool,
@@ -98,7 +76,25 @@ pub(crate) fn can_be_preempted(irq: &impl Interrupt) -> bool {
98 } 76 }
99} 77}
100 78
101impl<S: PeripheralStateUnchecked> PeripheralMutex<S> { 79impl<S: PeripheralState + 'static> PeripheralMutex<S> {
80 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
81 ///
82 /// This requires this `PeripheralMutex`'s `PeripheralState` to live for `'static`,
83 /// because `Pin` only guarantees that it's memory won't be repurposed,
84 /// not that it's lifetime will last.
85 ///
86 /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`.
87 ///
88 /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`;
89 /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`.
90 pub fn register_interrupt(self: Pin<&mut Self>) {
91 // SAFETY: `S: 'static`, so there's no way it's lifetime can expire.
92 unsafe { self.register_interrupt_unchecked() }
93 }
94}
95
96impl<S: PeripheralState> PeripheralMutex<S> {
97 /// Create a new `PeripheralMutex` wrapping `irq`, with the initial state `state`.
102 pub fn new(state: S, irq: S::Interrupt) -> Self { 98 pub fn new(state: S, irq: S::Interrupt) -> Self {
103 if can_be_preempted(&irq) { 99 if can_be_preempted(&irq) {
104 panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps"); 100 panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps");
@@ -114,8 +110,18 @@ impl<S: PeripheralStateUnchecked> PeripheralMutex<S> {
114 } 110 }
115 } 111 }
116 112
117 pub fn register_interrupt(self: Pin<&mut Self>) { 113 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
118 let this = unsafe { self.get_unchecked_mut() }; 114 ///
115 /// # Safety
116 /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler
117 /// must not end without `Drop` being called on this `PeripheralMutex`.
118 ///
119 /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`,
120 /// or making sure that nothing like `mem::forget` is used on the `PeripheralMutex`.
121
122 // TODO: this name isn't the best.
123 pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) {
124 let this = self.get_unchecked_mut();
119 if this.irq_setup_done { 125 if this.irq_setup_done {
120 return; 126 return;
121 } 127 }
@@ -172,7 +178,7 @@ impl<S: PeripheralStateUnchecked> PeripheralMutex<S> {
172 } 178 }
173} 179}
174 180
175impl<S: PeripheralStateUnchecked> Drop for PeripheralMutex<S> { 181impl<S: PeripheralState> Drop for PeripheralMutex<S> {
176 fn drop(&mut self) { 182 fn drop(&mut self) {
177 self.irq.disable(); 183 self.irq.disable();
178 self.irq.remove_handler(); 184 self.irq.remove_handler();
diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs
index 788ac3f96..71d746341 100644
--- a/embassy-extras/src/peripheral_shared.rs
+++ b/embassy-extras/src/peripheral_shared.rs
@@ -5,30 +5,19 @@ use embassy::interrupt::{Interrupt, InterruptExt};
5 5
6use crate::peripheral::can_be_preempted; 6use crate::peripheral::can_be_preempted;
7 7
8/// A version of `PeripheralState` without the `'static` bound,
9/// for cases where the compiler can't statically make sure
10/// that `on_interrupt` doesn't reference anything which might be invalidated.
11///
12/// # Safety
13/// When types implementing this trait are used with `Peripheral`,
14/// no fields referenced by `on_interrupt`'s lifetimes must end without first calling `Drop` on the `Peripheral`.
15pub unsafe trait PeripheralStateUnchecked: Sync {
16 type Interrupt: Interrupt;
17 fn on_interrupt(&self);
18}
19
20/// A type which can be used as state with `Peripheral`. 8/// A type which can be used as state with `Peripheral`.
21/// 9///
22/// It needs to be `Sync` because references are shared between the 'thread' which owns the `Peripheral` and the interrupt. 10/// It needs to be `Sync` because references are shared between the 'thread' which owns the `Peripheral` and the interrupt.
23/// 11///
24/// It also requires `'static`, because although `Pin` guarantees that the memory of the state won't be invalidated, 12/// It also requires `'static` to be used safely with `Peripheral::register_interrupt`,
13/// because although `Pin` guarantees that the memory of the state won't be invalidated,
25/// it doesn't guarantee that the lifetime will last. 14/// it doesn't guarantee that the lifetime will last.
26pub trait PeripheralState: Sync + 'static { 15pub trait PeripheralState: Sync {
27 type Interrupt: Interrupt; 16 type Interrupt: Interrupt;
28 fn on_interrupt(&self); 17 fn on_interrupt(&self);
29} 18}
30 19
31pub struct Peripheral<S: PeripheralStateUnchecked> { 20pub struct Peripheral<S: PeripheralState> {
32 state: S, 21 state: S,
33 22
34 irq_setup_done: bool, 23 irq_setup_done: bool,
@@ -38,7 +27,24 @@ pub struct Peripheral<S: PeripheralStateUnchecked> {
38 _pinned: PhantomPinned, 27 _pinned: PhantomPinned,
39} 28}
40 29
41impl<S: PeripheralStateUnchecked> Peripheral<S> { 30impl<S: PeripheralState + 'static> Peripheral<S> {
31 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
32 ///
33 /// This requires this `Peripheral`'s `PeripheralState` to live for `'static`,
34 /// because `Pin` only guarantees that it's memory won't be repurposed,
35 /// not that it's lifetime will last.
36 ///
37 /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`.
38 ///
39 /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`;
40 /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`.
41 pub fn register_interrupt(self: Pin<&mut Self>) {
42 // SAFETY: `S: 'static`, so there's no way it's lifetime can expire.
43 unsafe { self.register_interrupt_unchecked() }
44 }
45}
46
47impl<S: PeripheralState> Peripheral<S> {
42 pub fn new(irq: S::Interrupt, state: S) -> Self { 48 pub fn new(irq: S::Interrupt, state: S) -> Self {
43 if can_be_preempted(&irq) { 49 if can_be_preempted(&irq) {
44 panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps"); 50 panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps");
@@ -54,8 +60,16 @@ impl<S: PeripheralStateUnchecked> Peripheral<S> {
54 } 60 }
55 } 61 }
56 62
57 pub fn register_interrupt(self: Pin<&mut Self>) { 63 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
58 let this = unsafe { self.get_unchecked_mut() }; 64 ///
65 /// # Safety
66 /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler
67 /// must not end without `Drop` being called on this `Peripheral`.
68 ///
69 /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`,
70 /// or making sure that nothing like `mem::forget` is used on the `Peripheral`.
71 pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) {
72 let this = self.get_unchecked_mut();
59 if this.irq_setup_done { 73 if this.irq_setup_done {
60 return; 74 return;
61 } 75 }
@@ -100,7 +114,7 @@ impl<S: PeripheralStateUnchecked> Peripheral<S> {
100 } 114 }
101} 115}
102 116
103impl<S: PeripheralStateUnchecked> Drop for Peripheral<S> { 117impl<S: PeripheralState> Drop for Peripheral<S> {
104 fn drop(&mut self) { 118 fn drop(&mut self) {
105 self.irq.disable(); 119 self.irq.disable();
106 self.irq.remove_handler(); 120 self.irq.remove_handler();
diff --git a/embassy-extras/src/usb/mod.rs b/embassy-extras/src/usb/mod.rs
index 481987a6c..1fb501d7f 100644
--- a/embassy-extras/src/usb/mod.rs
+++ b/embassy-extras/src/usb/mod.rs
@@ -9,7 +9,7 @@ use usb_device::device::UsbDevice;
9mod cdc_acm; 9mod cdc_acm;
10pub mod usb_serial; 10pub mod usb_serial;
11 11
12use crate::peripheral::{PeripheralMutex, PeripheralStateUnchecked}; 12use crate::peripheral::{PeripheralMutex, PeripheralState};
13use embassy::interrupt::Interrupt; 13use embassy::interrupt::Interrupt;
14use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; 14use usb_serial::{ReadInterface, UsbSerial, WriteInterface};
15 15
@@ -63,7 +63,9 @@ where
63 let mutex = Pin::new_unchecked(&mut *mutex); 63 let mutex = Pin::new_unchecked(&mut *mutex);
64 64
65 // Use inner to register the irq 65 // Use inner to register the irq
66 mutex.register_interrupt(); 66 // SAFETY: the safety contract of this function makes sure the `UsbDevice` won't be invalidated
67 // without the `PeripheralMutex` being dropped.
68 mutex.register_interrupt_unchecked();
67 } 69 }
68} 70}
69 71
@@ -127,8 +129,7 @@ where
127 } 129 }
128} 130}
129 131
130// SAFETY: The safety contract of `PeripheralStateUnchecked` is forwarded to `Usb::start`. 132impl<'bus, B, T, I> PeripheralState for State<'bus, B, T, I>
131unsafe impl<'bus, B, T, I> PeripheralStateUnchecked for State<'bus, B, T, I>
132where 133where
133 B: UsbBus, 134 B: UsbBus,
134 T: ClassSet<B>, 135 T: ClassSet<B>,