Skip to content

Commit

Permalink
Hide the funky Mutex<RefCell<Option<T>>> types from application code.
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanpallant authored and listochkin committed Jan 26, 2024
1 parent 87321cf commit 7e6d9e6
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 103 deletions.
1 change: 1 addition & 0 deletions nrf52-code/boards/dongle/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ version = "0.0.0"
cortex-m = {version = "0.7.6", features = ["critical-section-single-core"]}
cortex-m-rt = "0.7.2"
cortex-m-semihosting = "0.5.0"
critical-section = "1.1.2"
defmt = "0.3.5"
defmt-rtt = "0.4"
embedded-hal = "0.2.7"
Expand Down
45 changes: 45 additions & 0 deletions nrf52-code/boards/dongle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,51 @@ impl core::fmt::Write for &Ringbuffer {
}
}

/// The global type for sharing things with an interrupt handler
pub struct GlobalIrqState<T> {
inner: critical_section::Mutex<core::cell::RefCell<Option<T>>>,
}

impl<T> GlobalIrqState<T> {
/// Create a new, empty, object
pub const fn new() -> GlobalIrqState<T> {
GlobalIrqState {
inner: critical_section::Mutex::new(core::cell::RefCell::new(None)),
}
}

/// Load a value into the global
///
/// Returns the old value, if any
pub fn load(&self, value: T) -> Option<T> {
critical_section::with(|cs| self.inner.borrow(cs).replace(Some(value)))
}
}

/// The local type for sharing things with an interrupt handler
pub struct LocalIrqState<T> {
inner: Option<T>,
}

impl<T> LocalIrqState<T> {
/// Create a new, empty, object
pub const fn new() -> LocalIrqState<T> {
LocalIrqState { inner: None }
}

/// Grab a mutable reference to the contents.
///
/// If the value is empty, the contents are taken from a mutex-locked global
/// variable. That global must have been initialised before calling this
/// function. If not, this function panics.
pub fn get_or_init_with(&mut self, global: &GlobalIrqState<T>) -> &mut T {
let result = self.inner.get_or_insert_with(|| {
critical_section::with(|cs| global.inner.borrow(cs).replace(None).unwrap())
});
result
}
}

/// The ways that initialisation can fail
#[derive(Debug, Copy, Clone, defmt::Format)]
pub enum Error {
Expand Down
1 change: 1 addition & 0 deletions nrf52-code/loopback-fw/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 25 additions & 52 deletions nrf52-code/loopback-fw/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
#![no_std]
#![no_main]

use core::cell::RefCell;
use core::fmt::Write;
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};

use cortex_m_rt::entry;
use critical_section::Mutex;
use usb_device::class_prelude::UsbBusAllocator;
use usb_device::device::{UsbDevice, UsbDeviceBuilder, UsbVidPid};
use usbd_hid::hid_class::HIDClass;
Expand All @@ -20,20 +18,20 @@ use dongle::peripheral::interrupt;
use dongle::{
hal::usbd,
ieee802154::{Channel, Packet},
UsbBus,
GlobalIrqState, LocalIrqState, UsbBus,
};

/// A buffer for holding bytes we want to send to the USB Serial port
static RING_BUFFER: dongle::Ringbuffer = dongle::Ringbuffer::new();

/// The USB Device Driver (owned by the USBD interrupt).
static USB_DEVICE: Mutex<RefCell<Option<UsbDevice<UsbBus>>>> = Mutex::new(RefCell::new(None));
static USB_DEVICE: GlobalIrqState<UsbDevice<UsbBus>> = GlobalIrqState::new();

/// The USB Serial Device Driver (owned by the USBD interrupt).
static USB_SERIAL: Mutex<RefCell<Option<SerialPort<UsbBus>>>> = Mutex::new(RefCell::new(None));
static USB_SERIAL: GlobalIrqState<SerialPort<UsbBus>> = GlobalIrqState::new();

/// The USB Human Interface Device Driver (owned by the USBD interrupt).
static USB_HID: Mutex<RefCell<Option<HIDClass<UsbBus>>>> = Mutex::new(RefCell::new(None));
static USB_HID: GlobalIrqState<HIDClass<UsbBus>> = GlobalIrqState::new();

/// Track how many CRC successes we had receiving radio packets
static RX_COUNT: AtomicU32 = AtomicU32::new(0);
Expand Down Expand Up @@ -67,17 +65,14 @@ fn main() -> ! {
board.usbd,
board.clocks,
)));
*USB_BUS = Some(usb_bus);

// This reference has static lifetime
let bus_ref = USB_BUS.as_ref().unwrap();
USB_BUS.replace(usb_bus);

// Grab a reference to the USB Bus allocator. We are promising to the
// compiler not to take mutable access to this global variable whilst this
// reference exists!
critical_section::with(|cs| {
*USB_SERIAL.borrow(cs).borrow_mut() = Some(SerialPort::new(bus_ref));
});
let bus_ref = USB_BUS.as_ref().unwrap();

USB_SERIAL.load(SerialPort::new(bus_ref));

let desc = &[
0x06, 0x00, 0xFF, // Item(Global): Usage Page, data= [ 0x00 0xff ] 65280
Expand All @@ -104,21 +99,17 @@ fn main() -> ! {
// Preferred_State No_Null_Position Non_Volatile Bitfield
0xC0, // Item(Main ): End Collection, data=none
];
let hid = HIDClass::new(bus_ref, desc, 100);
critical_section::with(|cs| {
*USB_HID.borrow(cs).borrow_mut() = Some(hid);
});

let vid_pid = UsbVidPid(consts::USB_VID_DEMO, consts::USB_PID_DONGLE_PUZZLE);
let usb_dev = UsbDeviceBuilder::new(bus_ref, vid_pid)
.manufacturer("Ferrous Systems")
.product("Dongle Loopback")
.device_class(USB_CLASS_CDC)
.max_packet_size_0(64) // (makes control transfers 8x faster)
.build();
critical_section::with(|cs| {
*USB_DEVICE.borrow(cs).borrow_mut() = Some(usb_dev);
});
USB_HID.load(HIDClass::new(bus_ref, desc, 100));

let vid_pid = UsbVidPid(consts::USB_VID_DEMO, consts::USB_PID_DONGLE_LOOPBACK);
USB_DEVICE.load(
UsbDeviceBuilder::new(bus_ref, vid_pid)
.manufacturer("Ferrous Systems")
.product("Dongle Loopback")
.device_class(USB_CLASS_CDC)
.max_packet_size_0(64) // (makes control transfers 8x faster)
.build(),
);

let mut current_ch_id = 20;
board.radio.set_channel(dongle::ieee802154::Channel::_20);
Expand Down Expand Up @@ -220,33 +211,15 @@ fn main() -> ! {
/// USB UART.
#[interrupt]
fn USBD() {
static mut LOCAL_USB_DEVICE: Option<UsbDevice<UsbBus>> = None;
static mut LOCAL_USB_SERIAL: Option<SerialPort<UsbBus>> = None;
static mut LOCAL_USB_HID: Option<HIDClass<UsbBus>> = None;
static mut LOCAL_USB_DEVICE: LocalIrqState<UsbDevice<'static, UsbBus>> = LocalIrqState::new();
static mut LOCAL_USB_SERIAL: LocalIrqState<SerialPort<'static, UsbBus>> = LocalIrqState::new();
static mut LOCAL_USB_HID: LocalIrqState<HIDClass<'static, UsbBus>> = LocalIrqState::new();
static mut IS_PENDING: Option<u8> = None;

// Grab a reference to our local vars, moving the object out of the global as required...

let usb_dev = LOCAL_USB_DEVICE.get_or_insert_with(|| {
critical_section::with(|cs| {
// Move USB device here, leaving a None in its place
USB_DEVICE.borrow(cs).replace(None).unwrap()
})
});

let serial = LOCAL_USB_SERIAL.get_or_insert_with(|| {
critical_section::with(|cs| {
// Move USB device here, leaving a None in its place
USB_SERIAL.borrow(cs).replace(None).unwrap()
})
});

let hid = LOCAL_USB_HID.get_or_insert_with(|| {
critical_section::with(|cs| {
// Move USB device here, leaving a None in its place
USB_HID.borrow(cs).replace(None).unwrap()
})
});
let usb_dev = LOCAL_USB_DEVICE.get_or_init_with(&USB_DEVICE);
let serial = LOCAL_USB_SERIAL.get_or_init_with(&USB_SERIAL);
let hid = LOCAL_USB_HID.get_or_init_with(&USB_HID);

let mut buf = [0u8; 64];

Expand Down
1 change: 1 addition & 0 deletions nrf52-code/puzzle-fw/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 24 additions & 51 deletions nrf52-code/puzzle-fw/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
#![no_std]
#![no_main]

use core::cell::RefCell;
use core::fmt::Write;
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};

use cortex_m_rt::entry;
use critical_section::Mutex;
use usb_device::class_prelude::UsbBusAllocator;
use usb_device::device::{UsbDevice, UsbDeviceBuilder, UsbVidPid};
use usbd_hid::hid_class::HIDClass;
Expand All @@ -20,7 +18,7 @@ use dongle::peripheral::interrupt;
use dongle::{
hal::usbd,
ieee802154::{Channel, Packet},
UsbBus,
GlobalIrqState, LocalIrqState, UsbBus,
};

/// The secret message, but encoded.
Expand All @@ -38,13 +36,13 @@ static CIPHER_LETTERS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/CIPHER_
static RING_BUFFER: dongle::Ringbuffer = dongle::Ringbuffer::new();

/// The USB Device Driver (owned by the USBD interrupt).
static USB_DEVICE: Mutex<RefCell<Option<UsbDevice<UsbBus>>>> = Mutex::new(RefCell::new(None));
static USB_DEVICE: GlobalIrqState<UsbDevice<UsbBus>> = GlobalIrqState::new();

/// The USB Serial Device Driver (owned by the USBD interrupt).
static USB_SERIAL: Mutex<RefCell<Option<SerialPort<UsbBus>>>> = Mutex::new(RefCell::new(None));
static USB_SERIAL: GlobalIrqState<SerialPort<UsbBus>> = GlobalIrqState::new();

/// The USB Human Interface Device Driver (owned by the USBD interrupt).
static USB_HID: Mutex<RefCell<Option<HIDClass<UsbBus>>>> = Mutex::new(RefCell::new(None));
static USB_HID: GlobalIrqState<HIDClass<UsbBus>> = GlobalIrqState::new();

/// Track how many CRC successes we had receiving radio packets
static RX_COUNT: AtomicU32 = AtomicU32::new(0);
Expand Down Expand Up @@ -78,17 +76,14 @@ fn main() -> ! {
board.usbd,
board.clocks,
)));
*USB_BUS = Some(usb_bus);

// This reference has static lifetime
let bus_ref = USB_BUS.as_ref().unwrap();
USB_BUS.replace(usb_bus);

// Grab a reference to the USB Bus allocator. We are promising to the
// compiler not to take mutable access to this global variable whilst this
// reference exists!
critical_section::with(|cs| {
*USB_SERIAL.borrow(cs).borrow_mut() = Some(SerialPort::new(bus_ref));
});
let bus_ref = USB_BUS.as_ref().unwrap();

USB_SERIAL.load(SerialPort::new(bus_ref));

let desc = &[
0x06, 0x00, 0xFF, // Item(Global): Usage Page, data= [ 0x00 0xff ] 65280
Expand All @@ -115,21 +110,17 @@ fn main() -> ! {
// Preferred_State No_Null_Position Non_Volatile Bitfield
0xC0, // Item(Main ): End Collection, data=none
];
let hid = HIDClass::new(bus_ref, desc, 100);
critical_section::with(|cs| {
*USB_HID.borrow(cs).borrow_mut() = Some(hid);
});
USB_HID.load(HIDClass::new(bus_ref, desc, 100));

let vid_pid = UsbVidPid(consts::USB_VID_DEMO, consts::USB_PID_DONGLE_PUZZLE);
let usb_dev = UsbDeviceBuilder::new(bus_ref, vid_pid)
.manufacturer("Ferrous Systems")
.product("Dongle Puzzle")
.device_class(USB_CLASS_CDC)
.max_packet_size_0(64) // (makes control transfers 8x faster)
.build();
critical_section::with(|cs| {
*USB_DEVICE.borrow(cs).borrow_mut() = Some(usb_dev);
});
USB_DEVICE.load(
UsbDeviceBuilder::new(bus_ref, vid_pid)
.manufacturer("Ferrous Systems")
.product("Dongle Puzzle")
.device_class(USB_CLASS_CDC)
.max_packet_size_0(64) // (makes control transfers 8x faster)
.build(),
);

let mut current_ch_id = 25;
board.radio.set_channel(dongle::ieee802154::Channel::_25);
Expand Down Expand Up @@ -173,7 +164,7 @@ fn main() -> ! {
);
match handle_packet(&mut pkt, &dict) {
Command::SendSecret => {
pkt.copy_from_slice(&ENCODED_MESSAGE);
pkt.copy_from_slice(ENCODED_MESSAGE);
let _ = writeln!(&RING_BUFFER, "TX Secret");
board.leds.ld2_blue.on();
board.leds.ld2_green.off();
Expand Down Expand Up @@ -301,33 +292,15 @@ fn handle_packet(packet: &mut Packet, dict: &heapless::LinearMap<u8, u8, 128>) -
/// USB UART.
#[interrupt]
fn USBD() {
static mut LOCAL_USB_DEVICE: Option<UsbDevice<UsbBus>> = None;
static mut LOCAL_USB_SERIAL: Option<SerialPort<UsbBus>> = None;
static mut LOCAL_USB_HID: Option<HIDClass<UsbBus>> = None;
static mut LOCAL_USB_DEVICE: LocalIrqState<UsbDevice<'static, UsbBus>> = LocalIrqState::new();
static mut LOCAL_USB_SERIAL: LocalIrqState<SerialPort<'static, UsbBus>> = LocalIrqState::new();
static mut LOCAL_USB_HID: LocalIrqState<HIDClass<'static, UsbBus>> = LocalIrqState::new();
static mut IS_PENDING: Option<u8> = None;

// Grab a reference to our local vars, moving the object out of the global as required...

let usb_dev = LOCAL_USB_DEVICE.get_or_insert_with(|| {
critical_section::with(|cs| {
// Move USB device here, leaving a None in its place
USB_DEVICE.borrow(cs).replace(None).unwrap()
})
});

let serial = LOCAL_USB_SERIAL.get_or_insert_with(|| {
critical_section::with(|cs| {
// Move USB device here, leaving a None in its place
USB_SERIAL.borrow(cs).replace(None).unwrap()
})
});

let hid = LOCAL_USB_HID.get_or_insert_with(|| {
critical_section::with(|cs| {
// Move USB device here, leaving a None in its place
USB_HID.borrow(cs).replace(None).unwrap()
})
});
let usb_dev = LOCAL_USB_DEVICE.get_or_init_with(&USB_DEVICE);
let serial = LOCAL_USB_SERIAL.get_or_init_with(&USB_SERIAL);
let hid = LOCAL_USB_HID.get_or_init_with(&USB_HID);

let mut buf = [0u8; 64];

Expand Down

0 comments on commit 7e6d9e6

Please sign in to comment.