Skip to content

Commit e37b925

Browse files
author
Dan Whitman
committed
bsp(metro_m4)!: Makes the usb_logging example safe and suppresses the static mut warnings.
1 parent dd9537c commit e37b925

File tree

2 files changed

+95
-29
lines changed

2 files changed

+95
-29
lines changed

boards/metro_m4/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ version = "0.18.2"
1414
[package.metadata]
1515
chip = "ATSAMD51J19A"
1616

17+
[dependencies]
18+
heapless = "0.9.1"
19+
1720
[dependencies.cortex-m-rt]
1821
optional = true
1922
version = "0.7"

boards/metro_m4/examples/usb_logging.rs

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#![no_std]
22
#![no_main]
3+
#![allow(static_mut_refs)]
34

45
use metro_m4 as bsp;
56

67
use bsp::ehal;
78
use bsp::hal;
89
use bsp::pac;
910

11+
use core::cell::OnceCell;
1012
use cortex_m::asm::delay as cycle_delay;
1113
use cortex_m::peripheral::NVIC;
1214
use ehal::digital::StatefulOutputPin;
@@ -40,20 +42,20 @@ fn main() -> ! {
4042
let mut red_led: bsp::RedLed = pins.d13.into();
4143

4244
let bus_allocator = unsafe {
43-
USB_ALLOCATOR = Some(bsp::usb_allocator(
45+
let _ = USB_ALLOCATOR.set(bsp::usb_allocator(
4446
peripherals.usb,
4547
&mut clocks,
4648
&mut peripherals.mclk,
4749
pins.usb_dm,
4850
pins.usb_dp,
4951
));
50-
USB_ALLOCATOR.as_ref().unwrap()
52+
USB_ALLOCATOR.get().unwrap()
5153
};
5254

5355
unsafe {
54-
USB_SERIAL = Some(SerialPort::new(bus_allocator));
55-
USB_BUS = Some(
56-
UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x2222, 0x3333))
56+
let _ = USB_SERIAL.set(SerialPort::new(bus_allocator));
57+
let _ = USB_BUS.set(
58+
UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd))
5759
.strings(&[StringDescriptors::new(LangID::EN)
5860
.manufacturer("Fake company")
5961
.product("Serial port")
@@ -65,44 +67,110 @@ fn main() -> ! {
6567
}
6668

6769
unsafe {
70+
core.NVIC.set_priority(interrupt::USB_OTHER, 1);
6871
core.NVIC.set_priority(interrupt::USB_TRCPT0, 1);
69-
NVIC::unmask(interrupt::USB_TRCPT0);
7072
core.NVIC.set_priority(interrupt::USB_TRCPT1, 1);
71-
NVIC::unmask(interrupt::USB_TRCPT1);
72-
core.NVIC.set_priority(interrupt::USB_SOF_HSOF, 1);
73-
NVIC::unmask(interrupt::USB_SOF_HSOF);
74-
core.NVIC.set_priority(interrupt::USB_OTHER, 1);
7573
NVIC::unmask(interrupt::USB_OTHER);
74+
NVIC::unmask(interrupt::USB_TRCPT0);
75+
NVIC::unmask(interrupt::USB_TRCPT1);
7676
}
7777

7878
// Flash the LED in a spin loop to demonstrate that USB is
7979
// entirely interrupt driven.
8080
loop {
8181
cycle_delay(5 * 1024 * 1024);
8282
red_led.toggle().unwrap();
83-
// Turn off interrupts so we don't fight with the interrupt
84-
cortex_m::interrupt::free(|_| unsafe {
85-
if USB_BUS.as_mut().is_some() {
86-
if let Some(serial) = USB_SERIAL.as_mut() {
87-
let _ = serial.write("Hello USB\n".as_bytes());
88-
}
89-
}
90-
});
83+
84+
serial_writeln!("Hello USB");
9185
}
9286
}
9387

94-
static mut USB_ALLOCATOR: Option<UsbBusAllocator<UsbBus>> = None;
95-
static mut USB_BUS: Option<UsbDevice<UsbBus>> = None;
96-
static mut USB_SERIAL: Option<SerialPort<UsbBus>> = None;
88+
static mut USB_ALLOCATOR: OnceCell<UsbBusAllocator<UsbBus>> = OnceCell::new();
89+
static mut USB_BUS: OnceCell<UsbDevice<UsbBus>> = OnceCell::new();
90+
static mut USB_SERIAL: OnceCell<SerialPort<UsbBus>> = OnceCell::new();
91+
92+
/// Borrows the global singleton `UsbSerial` for a brief period with interrupts
93+
/// disabled
94+
///
95+
/// # Arguments
96+
/// `borrower`: The closure that gets run borrowing the global `UsbSerial`
97+
///
98+
/// # Safety
99+
/// the global singleton `UsbSerial` can be safely borrowed because we disable
100+
/// interrupts while it is being borrowed, guaranteeing that interrupt handlers
101+
/// like `USB` cannot mutate `UsbSerial` while we are as well.
102+
///
103+
/// # Panic
104+
/// If `init` has not been called and we haven't initialized our global
105+
/// singleton `UsbSerial`, we will panic.
106+
fn usbserial_get<T, R>(borrower: T) -> R
107+
where
108+
T: Fn(&mut SerialPort<UsbBus>) -> R,
109+
{
110+
usb_free(|_| unsafe {
111+
let usb_serial = USB_SERIAL.get_mut().expect("UsbSerial not initialized");
112+
borrower(usb_serial)
113+
})
114+
}
115+
116+
/// Execute closure `f` in an interrupt-free context.
117+
///
118+
/// This as also known as a "critical section".
119+
#[inline]
120+
fn usb_free<F, R>(f: F) -> R
121+
where
122+
F: FnOnce(&cortex_m::interrupt::CriticalSection) -> R,
123+
{
124+
NVIC::mask(interrupt::USB_OTHER);
125+
NVIC::mask(interrupt::USB_TRCPT0);
126+
NVIC::mask(interrupt::USB_TRCPT1);
127+
128+
let r = f(unsafe { &cortex_m::interrupt::CriticalSection::new() });
129+
130+
unsafe {
131+
NVIC::unmask(interrupt::USB_OTHER);
132+
NVIC::unmask(interrupt::USB_TRCPT0);
133+
NVIC::unmask(interrupt::USB_TRCPT1);
134+
};
135+
136+
r
137+
}
138+
139+
/// Writes the given message out over USB serial.
140+
///
141+
/// # Arguments
142+
/// * println args: variable arguments passed along to `core::write!`
143+
///
144+
/// # Warning
145+
/// as this function deals with a static mut, and it is also accessed in the
146+
/// USB interrupt handler, we both have unsafe code for unwrapping a static mut
147+
/// as well as disabling of interrupts while we do so.
148+
///
149+
/// # Safety
150+
/// the only time the static mut is used, we have interrupts disabled so we know
151+
/// we have sole access
152+
#[macro_export]
153+
macro_rules! serial_writeln {
154+
($($tt:tt)+) => {{
155+
use core::fmt::Write;
156+
157+
let mut s: heapless::String<256> = heapless::String::new();
158+
core::write!(&mut s, $($tt)*).unwrap();
159+
usbserial_get(|usbserial| {
160+
usbserial.write(s.as_bytes()).ok();
161+
usbserial.write("\r\n".as_bytes()).ok();
162+
});
163+
}};
164+
}
97165

98166
fn poll_usb() {
99167
unsafe {
100-
if let Some(usb_dev) = USB_BUS.as_mut() {
101-
if let Some(serial) = USB_SERIAL.as_mut() {
168+
if let Some(usb_dev) = USB_BUS.get_mut() {
169+
if let Some(serial) = USB_SERIAL.get_mut() {
102170
usb_dev.poll(&mut [serial]);
103171

104172
// Make the other side happy
105-
let mut buf = [0u8; 16];
173+
let mut buf = [0u8; 64];
106174
let _ = serial.read(&mut buf);
107175
};
108176
}
@@ -119,11 +187,6 @@ fn USB_TRCPT1() {
119187
poll_usb();
120188
}
121189

122-
#[interrupt]
123-
fn USB_SOF_HSOF() {
124-
poll_usb();
125-
}
126-
127190
#[interrupt]
128191
fn USB_OTHER() {
129192
poll_usb();

0 commit comments

Comments
 (0)