From 242315143b9fc91058dcfbcc2bae7d568587d362 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 15:28:54 +0100 Subject: [PATCH 01/16] Add basic support for Radio and Ble advertisement listening. --- crates/api-desc/src/lib.rs | 2 + crates/api-desc/src/radio.rs | 64 ++++++++++ crates/board/src/lib.rs | 7 + crates/board/src/radio.rs | 58 +++++++++ crates/prelude/src/lib.rs | 1 + crates/prelude/src/radio.rs | 98 ++++++++++++++ crates/runner-nordic/Cargo.lock | 70 +++++++++- crates/runner-nordic/Cargo.toml | 2 + crates/runner-nordic/src/main.rs | 163 +++++++++++++++++++++++- crates/runner-nordic/src/radio.rs | 120 +++++++++++++++++ crates/runner-nordic/src/tasks.rs | 2 + crates/runner-nordic/src/tasks/clock.rs | 7 +- crates/runner-nordic/src/tasks/radio.rs | 68 ++++++++++ crates/scheduler/src/call.rs | 2 + crates/scheduler/src/call/radio.rs | 70 ++++++++++ crates/scheduler/src/event.rs | 4 + crates/scheduler/src/event/radio.rs | 37 ++++++ 17 files changed, 765 insertions(+), 10 deletions(-) create mode 100644 crates/api-desc/src/radio.rs create mode 100644 crates/board/src/radio.rs create mode 100644 crates/prelude/src/radio.rs create mode 100644 crates/runner-nordic/src/radio.rs create mode 100644 crates/runner-nordic/src/tasks/radio.rs create mode 100644 crates/scheduler/src/call/radio.rs create mode 100644 crates/scheduler/src/event/radio.rs diff --git a/crates/api-desc/src/lib.rs b/crates/api-desc/src/lib.rs index b8c750180..bd2da8f50 100644 --- a/crates/api-desc/src/lib.rs +++ b/crates/api-desc/src/lib.rs @@ -27,6 +27,7 @@ mod id; mod led; mod macros; mod platform; +mod radio; mod rng; mod scheduling; mod store; @@ -47,6 +48,7 @@ impl Default for Api { debug::new(), led::new(), platform::new(), + radio::new(), rng::new(), scheduling::new(), store::new(), diff --git a/crates/api-desc/src/radio.rs b/crates/api-desc/src/radio.rs new file mode 100644 index 000000000..8daaeca33 --- /dev/null +++ b/crates/api-desc/src/radio.rs @@ -0,0 +1,64 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; + +pub(crate) fn new() -> Item { + let docs = docs! { + /// Radio operations. + }; + let name = "radio".into(); + let items = vec![ + item! { + /// Reads radio packet into a buffer. + fn read "rr" { + /// Address of the buffer. + ptr: *mut u8, + + /// Length of the buffer in bytes. + len: usize, + } -> { + /// Number of bytes read (or negative value for errors). + /// + /// This function does not block and may return zero. + len: isize, + } + }, + item! { + /// Register a handler for radio events. + fn register "re" { + /// Function called on radio events. + /// + /// The function takes its opaque `data` as argument. + handler_func: fn { data: *mut u8 }, + + /// The opaque data to use when calling the handler function. + handler_data: *mut u8, + } -> {} + }, + item! { + /// Unregister handlers for radio events. + fn unregister "rd" { + } -> {} + }, + item! { + /// Describes errors on radio operations. + enum Error { + Unknown = 0, + } + }, + ]; + Item::Mod(Mod { docs, name, items }) +} + diff --git a/crates/board/src/lib.rs b/crates/board/src/lib.rs index 69f5c0b23..40c3fa13d 100644 --- a/crates/board/src/lib.rs +++ b/crates/board/src/lib.rs @@ -32,6 +32,7 @@ pub mod crypto; pub mod debug; pub mod led; pub mod platform; +pub mod radio; pub mod rng; mod storage; pub mod timer; @@ -71,6 +72,7 @@ pub trait Api: Send + 'static { type Debug: debug::Api; type Led: led::Api; type Platform: platform::Api; + type Radio: radio::Api; type Rng: rng::Api; type Storage: Singleton + wasefire_store::Storage + Send; type Timer: timer::Api; @@ -107,6 +109,9 @@ pub enum Event { /// Button event. Button(button::Event), + /// Radio event. + Radio(radio::Event), + /// Timer event. Timer(timer::Event), @@ -137,6 +142,7 @@ pub type Crypto = ::Crypto; pub type Debug = ::Debug; pub type Led = ::Led; pub type Platform = ::Platform; +pub type Radio = ::Radio; pub type Rng = ::Rng; pub type Storage = ::Storage; pub type Timer = ::Timer; @@ -224,6 +230,7 @@ mod tests { type Debug = Unsupported; type Led = Unsupported; type Platform = Unsupported; + type Radio = Unsupported; type Rng = Unsupported; type Storage = Unsupported; type Timer = Unsupported; diff --git a/crates/board/src/radio.rs b/crates/board/src/radio.rs new file mode 100644 index 000000000..efa15a7b5 --- /dev/null +++ b/crates/board/src/radio.rs @@ -0,0 +1,58 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Radio interface. + +use crate::{Error, Support, Unsupported}; + +/// Radio event. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum Event { + /// A radio packet has been received + Received, +} + +impl From for crate::Event { + fn from(event: Event) -> Self { + crate::Event::Radio(event) + } +} + +/// Radio interface. +pub trait Api: Support { + /// Enables radio events + fn enable() -> Result<(), Error>; + + /// Disables radio events + fn disable() -> Result<(), Error>; + + /// Reads from the radio receive queue into a buffer. + /// + /// Returns the number of bytes read. It could be zero if there's nothing to read. + fn read(output: &mut [u8]) -> Result; +} + +impl Api for Unsupported { + fn enable() -> Result<(), Error> { + unreachable!() + } + + fn disable() -> Result<(), Error> { + unreachable!() + } + + fn read(_: &mut [u8]) -> Result { + unreachable!() + } +} diff --git a/crates/prelude/src/lib.rs b/crates/prelude/src/lib.rs index 9881959b2..e14d0fd76 100644 --- a/crates/prelude/src/lib.rs +++ b/crates/prelude/src/lib.rs @@ -45,6 +45,7 @@ pub mod crypto; pub mod debug; pub mod led; pub mod platform; +pub mod radio; pub mod rng; pub mod scheduling; pub mod serial; diff --git a/crates/prelude/src/radio.rs b/crates/prelude/src/radio.rs new file mode 100644 index 000000000..5fd86cb0e --- /dev/null +++ b/crates/prelude/src/radio.rs @@ -0,0 +1,98 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use alloc::boxed::Box; + +use wasefire_applet_api::radio as api; +use wasefire_applet_api::radio::Error; + +/// Reads from radio packet queue into a buffer without blocking. +/// +/// Returns how many bytes were read (and thus written to the buffer). This function does not block, +/// so if there are no data available for read, zero is returned. +pub fn read(buf: &mut [u8]) -> Result { + let params = api::read::Params { ptr: buf.as_mut_ptr(), len: buf.len() }; + let api::read::Results { len } = unsafe { api::read(params) }; + if len < 0 { + return Err(Error::Unknown); + } + Ok(len as usize) +} + +/// Provides callback support for radio events. +pub trait Handler: 'static { + /// Called when a radio packet is received. + fn event(&self); +} + +impl Handler for F { + fn event(&self) { + self() + } +} + +/// Provides listening support for radio events. +#[must_use] +pub struct Listener { + handler: *mut H, +} + +impl Listener { + /// Starts listening for radio events. + /// + /// The listener stops listening when dropped. + /// + /// # Examples + /// + /// ```ignore + /// Listener::new(|| debug!("Radio packet has been received")) + /// ``` + pub fn new(handler: H) -> Self { + let handler_func = Self::call; + let handler = Box::into_raw(Box::new(handler)); + let handler_data = handler as *mut u8; + unsafe { api::register(api::register::Params { handler_func, handler_data }) }; + Listener { handler } + } + + /// Stops listening. + /// + /// This is equivalent to calling `core::mem::drop()`. + pub fn stop(self) { + core::mem::drop(self); + } + + /// Drops the listener but continues listening. + /// + /// This is equivalent to calling `core::mem::forget()`. This can be useful if the listener is + /// created deeply in the stack but the callback must continue processing events until the + /// applet exits or traps. + pub fn leak(self) { + core::mem::forget(self); + } + + extern "C" fn call(data: *mut u8) { + let handler = unsafe { &mut *(data as *mut H) }; + handler.event(); + } +} + +impl Drop for Listener { + fn drop(&mut self) { + unsafe { api::unregister() }; + unsafe { + let _ = Box::from_raw(self.handler); + } + } +} diff --git a/crates/runner-nordic/Cargo.lock b/crates/runner-nordic/Cargo.lock index 5cc5c40c3..7def4d00f 100644 --- a/crates/runner-nordic/Cargo.lock +++ b/crates/runner-nordic/Cargo.lock @@ -97,6 +97,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block-buffer" version = "0.10.4" @@ -336,7 +342,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2d011b2fee29fb7d659b83c43fce9a2cb4df453e16d441a51448e448f3f98" dependencies = [ - "bitflags", + "bitflags 1.3.2", "defmt-macros", ] @@ -493,6 +499,21 @@ dependencies = [ "typenum", ] +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + [[package]] name = "generic-array" version = "0.14.7" @@ -852,6 +873,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "rubble" +version = "0.0.5" +source = "git+https://github.com/jmichelp/rubble.git?rev=d545f4f#d545f4f598d081c0177f38500792e353a7d932a3" +dependencies = [ + "bitflags 2.4.1", + "defmt", + "fugit", + "heapless", + "p256", + "rand_core", + "sha2", + "zerocopy", +] + +[[package]] +name = "rubble-nrf5x" +version = "0.0.5" +source = "git+https://github.com/jmichelp/rubble.git?rev=d545f4f#d545f4f598d081c0177f38500792e353a7d932a3" +dependencies = [ + "nrf52840-pac", + "rubble", +] + [[package]] name = "runner-nordic" version = "0.1.0" @@ -869,6 +914,8 @@ dependencies = [ "nrf52840-hal", "panic-abort", "panic-probe", + "rubble", + "rubble-nrf5x", "typenum", "usb-device", "usbd-serial", @@ -1210,6 +1257,27 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "zerocopy" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96f8f25c15a0edc9b07eb66e7e6e97d124c0505435c382fde1ab7ceb188aa956" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "855e0f6af9cd72b87d8a6c586f3cb583f5cdcc62c2c80869d8cd7e96fdf7ee20" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/crates/runner-nordic/Cargo.toml b/crates/runner-nordic/Cargo.toml index 837c20a1b..7276f01e4 100644 --- a/crates/runner-nordic/Cargo.toml +++ b/crates/runner-nordic/Cargo.toml @@ -20,6 +20,8 @@ header = { path = "crates/header" } nrf52840-hal = "0.16.0" panic-abort = { version = "0.3.2", optional = true } panic-probe = { version = "0.3.1", features = ["print-defmt"], optional = true } +rubble-nrf5x = { git = "https://github.com/jmichelp/rubble.git", rev = "d545f4f", features = ["52840"] } +rubble = { git = "https://github.com/jmichelp/rubble.git", rev = "d545f4f" } typenum = { version = "1.17.0", default-features = false } usb-device = "0.3.0" usbd-serial = "0.2.0" diff --git a/crates/runner-nordic/src/main.rs b/crates/runner-nordic/src/main.rs index 8f02d873c..ba4a5b5cf 100644 --- a/crates/runner-nordic/src/main.rs +++ b/crates/runner-nordic/src/main.rs @@ -25,6 +25,7 @@ mod storage; mod systick; mod tasks; +use alloc::collections::VecDeque; use core::cell::RefCell; use core::mem::MaybeUninit; @@ -37,7 +38,7 @@ use nrf52840_hal::ccm::{Ccm, DataRate}; use nrf52840_hal::clocks::{self, ExternalOscillator, Internal, LfOscStopped}; use nrf52840_hal::gpio::{Level, Output, Pin, PushPull}; use nrf52840_hal::gpiote::Gpiote; -use nrf52840_hal::pac::{interrupt, Interrupt}; +use nrf52840_hal::pac::{interrupt, Interrupt, TIMER0}; use nrf52840_hal::prelude::InputPin; use nrf52840_hal::rng::Rng; use nrf52840_hal::usbd::{UsbPeripheral, Usbd}; @@ -46,6 +47,14 @@ use nrf52840_hal::{gpio, uarte}; use panic_abort as _; #[cfg(feature = "debug")] use panic_probe as _; +use rubble::beacon::{BeaconScanner, ScanCallback}; +use rubble::bytes::{ByteWriter, ToBytes}; +use rubble::link::ad_structure::AdStructure; +use rubble::link::filter::AllowAll; +use rubble::link::{DeviceAddress, Metadata, MIN_PDU_BUF}; +use rubble::time::Timer; +use rubble_nrf5x::radio::{BleRadio, PacketBuffer}; +use rubble_nrf5x::timer::BleTimer; use usb_device::class_prelude::UsbBusAllocator; use usb_device::device::{StringDescriptors, UsbDevice, UsbDeviceBuilder, UsbVidPid}; use usbd_serial::{SerialPort, USB_CLASS_CDC}; @@ -77,6 +86,10 @@ struct State { timers: Timers, ccm: Ccm, leds: [Pin>; >::SUPPORT], + ble_radio: BleRadio, + ble_scanner: BeaconScanner, + ble_timer: BleTimer, + ble_packet_queue: VecDeque, rng: Rng, storage: Option, uarts: Uarts, @@ -86,6 +99,7 @@ struct State { pub enum Board {} static STATE: Mutex>> = Mutex::new(RefCell::new(None)); +static BLE_PACKET: Mutex>> = Mutex::new(RefCell::new(None)); fn with_state(f: impl FnOnce(&mut State) -> R) -> R { critical_section::with(|cs| f(STATE.borrow_ref_mut(cs).as_mut().unwrap())) @@ -95,6 +109,9 @@ fn with_state(f: impl FnOnce(&mut State) -> R) -> R { fn main() -> ! { static mut CLOCKS: MaybeUninit = MaybeUninit::uninit(); static mut USB_BUS: MaybeUninit> = MaybeUninit::uninit(); + // TX buffer is mandatory even when we only listen + static mut BLE_TX: MaybeUninit = MaybeUninit::uninit(); + static mut BLE_RX: MaybeUninit = MaybeUninit::uninit(); #[cfg(feature = "debug")] let c = nrf52840_hal::pac::CorePeripherals::take().unwrap(); @@ -116,7 +133,7 @@ fn main() -> ! { port0.p0_15.into_push_pull_output(Level::High).degrade(), port0.p0_16.into_push_pull_output(Level::High).degrade(), ]; - let timers = Timers::new(p.TIMER0, p.TIMER1, p.TIMER2, p.TIMER3, p.TIMER4); + let timers = Timers::new(p.TIMER1, p.TIMER2, p.TIMER3, p.TIMER4); let gpiote = Gpiote::new(p.GPIOTE); // We enable all USB interrupts except STARTED and EPDATA which are feedback loops. p.USBD.inten.write(|w| unsafe { w.bits(0x00fffffd) }); @@ -129,6 +146,15 @@ fn main() -> ! { .unwrap() .device_class(USB_CLASS_CDC) .build(); + let ble_radio = BleRadio::new( + p.RADIO, + &p.FICR, + BLE_TX.write([0; MIN_PDU_BUF]), + BLE_RX.write([0; MIN_PDU_BUF]), + ); + let ble_timer = BleTimer::init(p.TIMER0); + let ble_scanner = BeaconScanner::new(BleAdvScanCallback); + let ble_packet_queue = VecDeque::::new(); let rng = Rng::new(p.RNG); let ccm = Ccm::init(p.CCM, p.AAR, DataRate::_1Mbit); storage::init(p.NVMC); @@ -142,8 +168,23 @@ fn main() -> ! { }; let uarts = Uarts::new(p.UARTE0, pins, p.UARTE1); let events = Events::default(); - let state = - State { events, buttons, gpiote, serial, timers, ccm, leds, rng, storage, uarts, usb_dev }; + let state = State { + events, + buttons, + gpiote, + serial, + timers, + ccm, + leds, + ble_radio, + ble_scanner, + ble_timer, + ble_packet_queue, + rng, + storage, + uarts, + usb_dev, + }; // We first set the board and then enable interrupts so that interrupts may assume the board is // always present. critical_section::with(|cs| STATE.replace(cs, Some(state))); @@ -159,6 +200,80 @@ fn main() -> ! { Scheduler::::run(); } +pub struct RadioMetadata { + ticks: u32, + freq: u16, + rssi: i8, + pdu_type: u8, +} + +impl RadioMetadata { + pub fn len(&self) -> usize { + core::mem::size_of::() + + core::mem::size_of::() + + core::mem::size_of::() + + core::mem::size_of::() + } + + pub fn is_empty(&self) -> bool { + false + } +} + +impl From for RadioMetadata { + fn from(value: Metadata) -> Self { + RadioMetadata { + ticks: value.timestamp.unwrap().ticks(), + freq: match value.channel { + ch @ 0 ..= 10 => 2404 + 2 * (ch as u16), + ch @ 11 ..= 36 => 2404 + 2 * (ch as u16 + 1), + 37 => 2402, + 38 => 2426, + 39 => 2480, + _ => 0, + }, + rssi: value.rssi.unwrap(), + pdu_type: u8::from(value.pdu_type.unwrap()), + } + } +} + +pub struct BlePacket { + addr: [u8; 6], + metadata: RadioMetadata, + data: alloc::vec::Vec, +} + +impl BlePacket { + pub fn len(&self) -> usize { + self.addr.len() + self.metadata.len() + self.data.len() + } + + pub fn is_empty(&self) -> bool { + false + } +} + +pub struct BleAdvScanCallback; + +impl ScanCallback for BleAdvScanCallback { + fn beacon<'a, I>(&mut self, addr: DeviceAddress, data: I, metadata: Metadata) + where I: Iterator> { + let mut buf: [u8; MIN_PDU_BUF] = [0; MIN_PDU_BUF]; + let mut writer = ByteWriter::new(&mut buf); + for p in data { + assert!(p.to_bytes(&mut writer).is_ok()); + } + let len = MIN_PDU_BUF - writer.space_left(); + let packet = BlePacket { + addr: *addr.raw(), + metadata: metadata.clone().into(), + data: buf[.. len].to_vec(), + }; + assert!(critical_section::with(|cs| BLE_PACKET.replace(cs, Some(packet))).is_none()); + } +} + macro_rules! interrupts { ($($name:ident = $func:ident($($arg:expr),*$(,)?)),*$(,)?) => { const INTERRUPTS: &[Interrupt] = &[$(Interrupt::$name),*]; @@ -173,7 +288,8 @@ macro_rules! interrupts { interrupts! { GPIOTE = gpiote(), - TIMER0 = timer(0), + RADIO = radio(), + TIMER0 = radio_timer(), TIMER1 = timer(1), TIMER2 = timer(2), TIMER3 = timer(3), @@ -196,6 +312,43 @@ fn gpiote() { }); } +fn radio() { + with_state(|state| { + if let Some(next_update) = + state.ble_radio.recv_beacon_interrupt(state.ble_timer.now(), &mut state.ble_scanner) + { + state.ble_timer.configure_interrupt(next_update); + critical_section::with(|cs| { + if let Some(packet) = BLE_PACKET.take(cs) { + if state.ble_packet_queue.len() < 50 { + state.ble_packet_queue.push_back(packet); + state.events.push(board::radio::Event::Received.into()); + log::warn!("BLE queue size: {}", state.ble_packet_queue.len()); + } else { + log::warn!( + "BLE Packet dropped. Queue size: {}", + state.ble_packet_queue.len() + ); + } + } + }); + } + }) +} + +fn radio_timer() { + with_state(|state| { + if !state.ble_timer.is_interrupt_pending() { + return; + } + state.ble_timer.clear_interrupt(); + + let cmd = state.ble_scanner.timer_update(state.ble_timer.now()); + state.ble_radio.configure_receiver(cmd.radio); + state.ble_timer.configure_interrupt(cmd.next_update); + }); +} + fn timer(timer: usize) { let timer = Id::new(timer).unwrap(); with_state(|state| { diff --git a/crates/runner-nordic/src/radio.rs b/crates/runner-nordic/src/radio.rs new file mode 100644 index 000000000..b5003bdca --- /dev/null +++ b/crates/runner-nordic/src/radio.rs @@ -0,0 +1,120 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use rubble::beacon::ScanCallback; +use rubble::bytes::ByteReader; +use rubble::link::{Cmd, Metadata, NextUpdate, RadioCmd}; +use rubble::link::advertising::{Header, Pdu}; +use rubble::link::filter::{self, AddressFilter, ScanFilter}; +use rubble::phy::AdvertisingChannel; +use rubble::time::{Duration, Instant}; + +/// A passive scanner for BLE trackers advertisements. +/// Based on the Beacon scanner in rubble but incorporate some tricks +/// to handle BLE trackers. +pub struct BleTrackerScanner { + cb: C, + filter: ScanFilter, + interval: Duration, + channel: AdvertisingChannel, +} + +impl BleTrackerScanner { + /// Creates a `BleTrackerScanner` that will report beacons from any device. + pub fn new(callback: C) -> Self { + Self::with_filter(callback, filter::AllowAll) + } +} + +impl BleTrackerScanner { + /// Creates a `BleTrackerScanner` with a custom device filter. + pub fn with_filter(callback: C, scan_filter: F) -> Self { + Self { + cb: callback, + filter: ScanFilter::new(scan_filter), + interval: Duration::micros(0), + channel: AdvertisingChannel::first(), + } + } + + /// Configures the `BleTrackerScanner` and returns a `Cmd` to apply to the radio. + /// + /// The `next_update` field of the returned `Cmd` specifies when to call `timer_update` the next + /// time. The timer used for this needs to be pretty accurate because of advanced advertising packets, + /// which require to precisely switch channel to a data channel to capture the whole advertisement. + /// Otherwise it is only used to switch to the next advertising channel after `interval` elapses. + pub fn configure(&mut self, now: Instant, interval: Duration) -> Cmd { + self.interval = interval; + self.channel = AdvertisingChannel::first(); + + Cmd { + // Switch channels + next_update: NextUpdate::At(now + self.interval), + + radio: RadioCmd::ListenAdvertising { + channel: self.channel, + }, + + queued_work: false, + } + } + + /// Updates the `BleTrackerScanner` after the configured timer has fired. + /// + /// This switches to the next advertising channel and will listen there. + pub fn timer_update(&mut self, now: Instant) -> Cmd { + self.channel = self.channel.cycle(); + + Cmd { + // Switch channels + next_update: NextUpdate::At(now + self.interval), + + radio: RadioCmd::ListenAdvertising { + channel: self.channel, + }, + + queued_work: false, + } + } + + /// Processes a received advertising channel packet. + /// + /// This should be called whenever the radio receives a packet on the configured advertising + /// channel. + pub fn process_adv_packet( + &mut self, + header: Header, + payload: &[u8], + metadata: Metadata, + ) -> Cmd { + if metadata.crc_ok && header.type_().is_beacon() { + // Partially decode to get the device ID and run it through the filter + if let Ok(pdu) = Pdu::from_header_and_payload(header, &mut ByteReader::new(payload)) { + if self.filter.should_scan(*pdu.sender()) { + let ad = pdu.advertising_data().unwrap(); + self.cb.beacon(*pdu.sender(), ad, metadata); + } + } + } + + Cmd { + next_update: NextUpdate::Keep, + radio: RadioCmd::ListenAdvertising { + channel: self.channel, + }, + queued_work: false, + } + } +} + diff --git a/crates/runner-nordic/src/tasks.rs b/crates/runner-nordic/src/tasks.rs index 5b963abb2..6ae59bd86 100644 --- a/crates/runner-nordic/src/tasks.rs +++ b/crates/runner-nordic/src/tasks.rs @@ -23,6 +23,7 @@ mod crypto; mod debug; pub mod led; pub mod platform; +mod radio; mod rng; pub mod uart; pub mod usb; @@ -54,6 +55,7 @@ impl board::Api for Board { type Debug = debug::Impl; type Led = led::Impl; type Platform = platform::Impl; + type Radio = radio::Impl; type Rng = rng::Impl; type Storage = crate::storage::Storage; type Timer = clock::Impl; diff --git a/crates/runner-nordic/src/tasks/clock.rs b/crates/runner-nordic/src/tasks/clock.rs index 4ca122851..080afd280 100644 --- a/crates/runner-nordic/src/tasks/clock.rs +++ b/crates/runner-nordic/src/tasks/clock.rs @@ -16,7 +16,7 @@ use alloc::boxed::Box; use cortex_m::prelude::_embedded_hal_timer_CountDown; use embedded_hal::timer::Cancel; -use nrf52840_hal::pac::{TIMER0, TIMER1, TIMER2, TIMER3, TIMER4}; +use nrf52840_hal::pac::{TIMER1, TIMER2, TIMER3, TIMER4}; use nrf52840_hal::timer::{Instance, OneShot, Periodic}; use nrf52840_hal::Timer; use wasefire_board_api::timer::{Api, Command}; @@ -28,7 +28,7 @@ use crate::with_state; pub enum Impl {} impl Support for Impl { - const SUPPORT: usize = 5; + const SUPPORT: usize = 4; } impl Api for Impl { @@ -55,9 +55,8 @@ impl Api for Impl { pub struct Timers([ErasedTimer; >::SUPPORT]); impl Timers { - pub fn new(t0: TIMER0, t1: TIMER1, t2: TIMER2, t3: TIMER3, t4: TIMER4) -> Self { + pub fn new(t1: TIMER1, t2: TIMER2, t3: TIMER3, t4: TIMER4) -> Self { Timers([ - ErasedTimer::new(t0), ErasedTimer::new(t1), ErasedTimer::new(t2), ErasedTimer::new(t3), diff --git a/crates/runner-nordic/src/tasks/radio.rs b/crates/runner-nordic/src/tasks/radio.rs new file mode 100644 index 000000000..c9eeda51d --- /dev/null +++ b/crates/runner-nordic/src/tasks/radio.rs @@ -0,0 +1,68 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use rubble::bytes::ByteWriter; +use rubble::link::{NextUpdate, RadioCmd}; +use rubble::time::{Duration, Timer}; +use wasefire_board_api::radio::Api; +use wasefire_board_api::{Error, Support}; +use wasefire_logger as logger; + +use crate::with_state; + +pub enum Impl {} + +impl Api for Impl { + fn enable() -> Result<(), Error> { + with_state(|state| { + let scanner_cmd = + state.ble_scanner.configure(state.ble_timer.now(), Duration::millis(500)); + state.ble_radio.configure_receiver(scanner_cmd.radio); + state.ble_timer.configure_interrupt(scanner_cmd.next_update); + Ok(()) + }) + } + + fn disable() -> Result<(), Error> { + with_state(|state| { + state.ble_timer.configure_interrupt(NextUpdate::Disable); + state.ble_radio.configure_receiver(RadioCmd::Off); + Ok(()) + }) + } + + fn read(output: &mut [u8]) -> Result { + with_state(|state| { + let packet = state.ble_packet_queue.pop_front().ok_or(Error::World)?; + let len = packet.len(); + let mut writer = ByteWriter::new(output); + // Serialize RadioMetadata first, then BDADDR and append the packet + let res: Result<(), rubble::Error> = try { + writer.write_u32_le(packet.metadata.ticks)?; + writer.write_u16_le(packet.metadata.freq)?; + writer.write_slice(&packet.metadata.rssi.to_le_bytes())?; + writer.write_u8(packet.metadata.pdu_type)?; + writer.write_slice(&packet.addr)?; + writer.write_slice(&packet.data)?; + }; + res.map_err(|_| Error::World)?; + logger::trace!("{}{:?} = read({})", len, &output[.. len], output.len()); + Ok(len) + }) + } +} + +impl Support for Impl { + const SUPPORT: bool = true; +} diff --git a/crates/scheduler/src/call.rs b/crates/scheduler/src/call.rs index 2ae7a7ae1..f66ac9b6b 100644 --- a/crates/scheduler/src/call.rs +++ b/crates/scheduler/src/call.rs @@ -23,6 +23,7 @@ mod crypto; mod debug; mod led; mod platform; +mod radio; mod rng; mod scheduling; mod store; @@ -37,6 +38,7 @@ pub fn process(call: Api>) { Api::Debug(call) => debug::process(call), Api::Led(call) => led::process(call), Api::Platform(call) => platform::process(call), + Api::Radio(call) => radio::process(call), Api::Rng(call) => rng::process(call), Api::Scheduling(call) => scheduling::process(call), Api::Store(call) => store::process(call), diff --git a/crates/scheduler/src/call/radio.rs b/crates/scheduler/src/call/radio.rs new file mode 100644 index 000000000..c56b6bda5 --- /dev/null +++ b/crates/scheduler/src/call/radio.rs @@ -0,0 +1,70 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use wasefire_applet_api::radio::{self as api, Api}; +use wasefire_board_api::radio::Api as _; +use wasefire_board_api::{self as board, Api as Board}; + +use crate::applet::store::MemoryApi; +use crate::event::radio::Key; +use crate::event::Handler; +use crate::{DispatchSchedulerCall, SchedulerCall, Trap}; + +pub fn process(call: Api>) { + match call { + Api::Register(call) => register(call), + Api::Unregister(call) => unregister(call), + Api::Read(call) => read(call), + } +} + +fn register(mut call: SchedulerCall) { + let api::register::Params { handler_func, handler_data } = call.read(); + let inst = call.inst(); + let results = try { + call.scheduler().applet.enable(Handler { + key: Key::Received.into(), + inst, + func: *handler_func, + data: *handler_data, + })?; + board::Radio::::enable().map_err(|_| Trap)?; + api::register::Results {} + }; + call.reply(results); +} + +fn unregister(mut call: SchedulerCall) { + let results = try { + board::Radio::::disable().map_err(|_| Trap)?; + call.scheduler().disable_event(Key::Received.into())?; + api::unregister::Results {} + }; + call.reply(results); +} + +fn read(mut call: SchedulerCall) { + let api::read::Params { ptr, len } = call.read(); + let scheduler = call.scheduler(); + let memory = scheduler.applet.memory(); + let results = try { + let output = memory.get_mut(*ptr, *len)?; + let len = match board::Radio::::read(output) { + Ok(len) => (len as u32).into(), + Err(_) => u32::MAX.into(), + }; + api::read::Results { len } + }; + call.reply(results); +} diff --git a/crates/scheduler/src/event.rs b/crates/scheduler/src/event.rs index dd207c96a..2400b8f6b 100644 --- a/crates/scheduler/src/event.rs +++ b/crates/scheduler/src/event.rs @@ -25,6 +25,7 @@ use crate::Scheduler; pub mod button; pub mod timer; +pub mod radio; pub mod uart; pub mod usb; @@ -39,6 +40,7 @@ pub struct InstId; #[derivative(Ord = "feature_allow_slow_enum")] pub enum Key { Button(button::Key), + Radio(radio::Key), Timer(timer::Key), Uart(uart::Key), Usb(usb::Key), @@ -62,6 +64,7 @@ impl<'a, B: Board> From<&'a Event> for Key { fn from(event: &'a Event) -> Self { match event { Event::Button(event) => Key::Button(event.into()), + Event::Radio(event) => Key::Radio(event.into()), Event::Timer(event) => Key::Timer(event.into()), Event::Uart(event) => Key::Uart(event.into()), Event::Usb(event) => Key::Usb(event.into()), @@ -106,6 +109,7 @@ pub fn process(scheduler: &mut Scheduler, event: Event) { let mut params = vec![*func, *data]; match event { Event::Button(event) => button::process(event, &mut params), + Event::Radio(_) => radio::process(), Event::Timer(_) => timer::process(), Event::Uart(_) => uart::process(), Event::Usb(event) => usb::process(event), diff --git a/crates/scheduler/src/event/radio.rs b/crates/scheduler/src/event/radio.rs new file mode 100644 index 000000000..0e6ad201e --- /dev/null +++ b/crates/scheduler/src/event/radio.rs @@ -0,0 +1,37 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use wasefire_board_api::radio::Event; +use wasefire_board_api::Api as Board; + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum Key { + Received, +} + +impl From for crate::event::Key { + fn from(key: Key) -> Self { + crate::event::Key::Radio(key) + } +} + +impl<'a> From<&'a Event> for Key { + fn from(event: &'a Event) -> Self { + match event { + Event::Received => Key::Received, + } + } +} + +pub fn process() {} From c841457f8d6e8d41eb4284ca2d942c88e654a977 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 15:40:59 +0100 Subject: [PATCH 02/16] Update changelog --- crates/api-desc/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/api-desc/CHANGELOG.md b/crates/api-desc/CHANGELOG.md index 2cb3bfb46..fa01a0e25 100644 --- a/crates/api-desc/CHANGELOG.md +++ b/crates/api-desc/CHANGELOG.md @@ -4,6 +4,7 @@ ### Minor +- Add `radio` module - Add `platform::update::is_supported()` - Add `platform::update` module - Add `platform` module with `platform::reboot()` function From 44b4866a09b19a071ee9629ca1738b7691036d4a Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 15:42:35 +0100 Subject: [PATCH 03/16] Radio is unsupported on runner-host --- crates/runner-host/src/board.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/runner-host/src/board.rs b/crates/runner-host/src/board.rs index c5361d175..d0f628bb4 100644 --- a/crates/runner-host/src/board.rs +++ b/crates/runner-host/src/board.rs @@ -65,6 +65,7 @@ impl Api for Board { type Debug = debug::Impl; type Led = led::Impl; type Platform = Unsupported; + type Radio = Unsupported; type Rng = rng::Impl; type Storage = storage::Impl; type Timer = timer::Impl; From f396c34be9c689bc5a740d27cfb90e89fbd677fb Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 15:56:23 +0100 Subject: [PATCH 04/16] Changelog --- crates/board/CHANGELOG.md | 1 + crates/prelude/CHANGELOG.md | 1 + crates/scheduler/CHANGELOG.md | 1 + 3 files changed, 3 insertions(+) diff --git a/crates/board/CHANGELOG.md b/crates/board/CHANGELOG.md index d479e07b5..85e6e5028 100644 --- a/crates/board/CHANGELOG.md +++ b/crates/board/CHANGELOG.md @@ -4,6 +4,7 @@ ### Major +- Add `radio` module - Add `platform::Api::Update` (fix #47) - Add `Api::Platform` with `platform::Api::reboot()` function - Require the APIs to be `Send` and the top-level API to be `'static` too diff --git a/crates/prelude/CHANGELOG.md b/crates/prelude/CHANGELOG.md index 61e429cd9..fbb3bbeea 100644 --- a/crates/prelude/CHANGELOG.md +++ b/crates/prelude/CHANGELOG.md @@ -4,6 +4,7 @@ ### Major +- Add `radio` module - Rename `native` to `test` and use `native` for native applets ### Minor diff --git a/crates/scheduler/CHANGELOG.md b/crates/scheduler/CHANGELOG.md index f307b38c2..f4d7f46b2 100644 --- a/crates/scheduler/CHANGELOG.md +++ b/crates/scheduler/CHANGELOG.md @@ -4,6 +4,7 @@ ### Major +- Add `radio` module - Add `native` and `wasm` features (exactly one must be chosen) ### Minor From 29ea5970e83785e609b8f772f49e4165368efd3b Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 16:05:02 +0100 Subject: [PATCH 05/16] cargo fmt --- crates/api-desc/src/radio.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/api-desc/src/radio.rs b/crates/api-desc/src/radio.rs index 8daaeca33..84b5b961f 100644 --- a/crates/api-desc/src/radio.rs +++ b/crates/api-desc/src/radio.rs @@ -61,4 +61,3 @@ pub(crate) fn new() -> Item { ]; Item::Mod(Mod { docs, name, items }) } - From 395764e69aaa10a7cc7da2cb1f9e04ac53a56cb2 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 16:29:47 +0100 Subject: [PATCH 06/16] Make CI happy --- crates/scheduler/CHANGELOG.md | 2 +- crates/scheduler/src/event.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/scheduler/CHANGELOG.md b/crates/scheduler/CHANGELOG.md index f4d7f46b2..5e9cb26ec 100644 --- a/crates/scheduler/CHANGELOG.md +++ b/crates/scheduler/CHANGELOG.md @@ -92,4 +92,4 @@ ## 0.1.0 - + diff --git a/crates/scheduler/src/event.rs b/crates/scheduler/src/event.rs index 2400b8f6b..9d0c536f1 100644 --- a/crates/scheduler/src/event.rs +++ b/crates/scheduler/src/event.rs @@ -24,8 +24,8 @@ use wasefire_logger as log; use crate::Scheduler; pub mod button; -pub mod timer; pub mod radio; +pub mod timer; pub mod uart; pub mod usb; From adecdcebd3c6883d765938654fe946b06a9daa56 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 16:44:08 +0100 Subject: [PATCH 07/16] toml format --- crates/runner-nordic/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/runner-nordic/Cargo.toml b/crates/runner-nordic/Cargo.toml index 7276f01e4..6030771b5 100644 --- a/crates/runner-nordic/Cargo.toml +++ b/crates/runner-nordic/Cargo.toml @@ -20,8 +20,10 @@ header = { path = "crates/header" } nrf52840-hal = "0.16.0" panic-abort = { version = "0.3.2", optional = true } panic-probe = { version = "0.3.1", features = ["print-defmt"], optional = true } -rubble-nrf5x = { git = "https://github.com/jmichelp/rubble.git", rev = "d545f4f", features = ["52840"] } rubble = { git = "https://github.com/jmichelp/rubble.git", rev = "d545f4f" } +rubble-nrf5x = { git = "https://github.com/jmichelp/rubble.git", rev = "d545f4f", features = [ + "52840", +] } typenum = { version = "1.17.0", default-features = false } usb-device = "0.3.0" usbd-serial = "0.2.0" From 0eb5fc72abd32224c3e886730309de35c47b2507 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 16:50:28 +0100 Subject: [PATCH 08/16] bump changelog --- crates/api-desc/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/api-desc/CHANGELOG.md b/crates/api-desc/CHANGELOG.md index fa01a0e25..b9662d704 100644 --- a/crates/api-desc/CHANGELOG.md +++ b/crates/api-desc/CHANGELOG.md @@ -79,4 +79,4 @@ ## 0.1.0 - + From 3e2b9b9421c01cace9bfa1e26c04cc30bba8ac68 Mon Sep 17 00:00:00 2001 From: Jean-Michel Picod Date: Wed, 13 Dec 2023 16:57:32 +0100 Subject: [PATCH 09/16] update TS api --- examples/assemblyscript/api.ts | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/examples/assemblyscript/api.ts b/examples/assemblyscript/api.ts index 9a743da82..ea89ee6d1 100644 --- a/examples/assemblyscript/api.ts +++ b/examples/assemblyscript/api.ts @@ -749,6 +749,44 @@ ): isize // END OF MODULE platform +// START OF MODULE radio +// Radio operations. + // Reads radio packet into a buffer. + @external("env", "rr") + export declare function radio_read( + // Address of the buffer. + ptr: usize, + + // Length of the buffer in bytes. + len: usize, + // Number of bytes read (or negative value for errors). + // + // This function does not block and may return zero. + ): isize + + // Register a handler for radio events. + @external("env", "re") + export declare function radio_register( + // Function called on radio events. + // + // The function takes its opaque `data` as argument. + handler_func: usize, + + // The opaque data to use when calling the handler function. + handler_data: usize, + ): void + + // Unregister handlers for radio events. + @external("env", "rd") + export declare function radio_unregister( + ): void + + // Describes errors on radio operations. + enum radio_Error { + Unknown = 0, + } +// END OF MODULE radio + // START OF MODULE rng // Random number generators. // Fills a slice with random bytes. From dba7ee5c142f42d324dcd01368437eaa8dddc5fb Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Thu, 14 Dec 2023 16:43:32 +0100 Subject: [PATCH 10/16] Apply suggestions from code review --- crates/api-desc/src/radio.rs | 3 +-- crates/board/CHANGELOG.md | 3 ++- crates/board/src/radio.rs | 6 +++--- crates/runner-nordic/src/main.rs | 2 +- crates/runner-nordic/src/radio.rs | 8 +------- crates/runner-nordic/src/tasks/radio.rs | 6 ++---- 6 files changed, 10 insertions(+), 18 deletions(-) diff --git a/crates/api-desc/src/radio.rs b/crates/api-desc/src/radio.rs index 84b5b961f..e22f4d0ce 100644 --- a/crates/api-desc/src/radio.rs +++ b/crates/api-desc/src/radio.rs @@ -49,8 +49,7 @@ pub(crate) fn new() -> Item { }, item! { /// Unregister handlers for radio events. - fn unregister "rd" { - } -> {} + fn unregister "rd" {} -> {} }, item! { /// Describes errors on radio operations. diff --git a/crates/board/CHANGELOG.md b/crates/board/CHANGELOG.md index 85e6e5028..78115a22e 100644 --- a/crates/board/CHANGELOG.md +++ b/crates/board/CHANGELOG.md @@ -4,7 +4,8 @@ ### Major -- Add `radio` module +- Add `Api::Radio` for radio APIs +- Add `radio::Api::Ble` for Bluetooth Low Energy - Add `platform::Api::Update` (fix #47) - Add `Api::Platform` with `platform::Api::reboot()` function - Require the APIs to be `Send` and the top-level API to be `'static` too diff --git a/crates/board/src/radio.rs b/crates/board/src/radio.rs index efa15a7b5..6f906f56e 100644 --- a/crates/board/src/radio.rs +++ b/crates/board/src/radio.rs @@ -19,7 +19,7 @@ use crate::{Error, Support, Unsupported}; /// Radio event. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Event { - /// A radio packet has been received + /// A radio packet has been received. Received, } @@ -31,10 +31,10 @@ impl From for crate::Event { /// Radio interface. pub trait Api: Support { - /// Enables radio events + /// Enables radio events. fn enable() -> Result<(), Error>; - /// Disables radio events + /// Disables radio events. fn disable() -> Result<(), Error>; /// Reads from the radio receive queue into a buffer. diff --git a/crates/runner-nordic/src/main.rs b/crates/runner-nordic/src/main.rs index ba4a5b5cf..a40d8652e 100644 --- a/crates/runner-nordic/src/main.rs +++ b/crates/runner-nordic/src/main.rs @@ -109,7 +109,7 @@ fn with_state(f: impl FnOnce(&mut State) -> R) -> R { fn main() -> ! { static mut CLOCKS: MaybeUninit = MaybeUninit::uninit(); static mut USB_BUS: MaybeUninit> = MaybeUninit::uninit(); - // TX buffer is mandatory even when we only listen + // TX buffer is mandatory even when we only listen. static mut BLE_TX: MaybeUninit = MaybeUninit::uninit(); static mut BLE_RX: MaybeUninit = MaybeUninit::uninit(); diff --git a/crates/runner-nordic/src/radio.rs b/crates/runner-nordic/src/radio.rs index b5003bdca..9237731c0 100644 --- a/crates/runner-nordic/src/radio.rs +++ b/crates/runner-nordic/src/radio.rs @@ -59,13 +59,10 @@ impl BleTrackerScanner { self.channel = AdvertisingChannel::first(); Cmd { - // Switch channels next_update: NextUpdate::At(now + self.interval), - radio: RadioCmd::ListenAdvertising { channel: self.channel, }, - queued_work: false, } } @@ -77,13 +74,10 @@ impl BleTrackerScanner { self.channel = self.channel.cycle(); Cmd { - // Switch channels next_update: NextUpdate::At(now + self.interval), - radio: RadioCmd::ListenAdvertising { channel: self.channel, }, - queued_work: false, } } @@ -99,7 +93,7 @@ impl BleTrackerScanner { metadata: Metadata, ) -> Cmd { if metadata.crc_ok && header.type_().is_beacon() { - // Partially decode to get the device ID and run it through the filter + // Partially decode to get the device ID and run it through the filter. if let Ok(pdu) = Pdu::from_header_and_payload(header, &mut ByteReader::new(payload)) { if self.filter.should_scan(*pdu.sender()) { let ad = pdu.advertising_data().unwrap(); diff --git a/crates/runner-nordic/src/tasks/radio.rs b/crates/runner-nordic/src/tasks/radio.rs index c9eeda51d..d61697da6 100644 --- a/crates/runner-nordic/src/tasks/radio.rs +++ b/crates/runner-nordic/src/tasks/radio.rs @@ -47,7 +47,7 @@ impl Api for Impl { let packet = state.ble_packet_queue.pop_front().ok_or(Error::World)?; let len = packet.len(); let mut writer = ByteWriter::new(output); - // Serialize RadioMetadata first, then BDADDR and append the packet + // Serialize RadioMetadata first, then BDADDR and append the packet. let res: Result<(), rubble::Error> = try { writer.write_u32_le(packet.metadata.ticks)?; writer.write_u16_le(packet.metadata.freq)?; @@ -63,6 +63,4 @@ impl Api for Impl { } } -impl Support for Impl { - const SUPPORT: bool = true; -} +impl Supported for Impl {} From 06953340e44dbc0f186b49bf06c144787b717a03 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Fri, 15 Dec 2023 12:46:05 +0100 Subject: [PATCH 11/16] Move API to radio::ble --- crates/api-desc/CHANGELOG.md | 2 +- crates/api-desc/src/lib.rs | 23 +++ crates/api-desc/src/macros.rs | 9 ++ crates/api-desc/src/radio.rs | 34 +--- crates/api-desc/src/radio/ble.rs | 78 ++++++++++ crates/board/Cargo.lock | 145 ++++++++++++++++++ crates/board/Cargo.toml | 1 + crates/board/src/radio.rs | 35 ++--- crates/board/src/radio/ble.rs | 60 ++++++++ crates/prelude/Cargo.lock | 1 + crates/prelude/Cargo.toml | 1 + crates/prelude/src/debug.rs | 3 +- crates/prelude/src/radio.rs | 83 +--------- crates/prelude/src/radio/ble.rs | 93 +++++++++++ crates/prelude/src/usb.rs | 4 +- crates/runner-host/Cargo.lock | 1 + crates/runner-nordic/Cargo.lock | 1 + crates/runner-nordic/src/main.rs | 25 +-- crates/runner-nordic/src/tasks/radio.rs | 47 +----- crates/runner-nordic/src/tasks/radio/ble.rs | 68 ++++++++ crates/scheduler/Cargo.lock | 1 + crates/scheduler/src/call/radio.rs | 56 +------ crates/scheduler/src/call/radio/ble.rs | 81 ++++++++++ crates/scheduler/src/call/usb.rs | 4 +- crates/scheduler/src/event.rs | 2 +- crates/scheduler/src/event/radio.rs | 12 +- crates/scheduler/src/event/radio/ble.rs | 37 +++++ crates/scheduler/src/event/usb.rs | 4 +- examples/assemblyscript/api.ts | 87 +++++++---- examples/rust/blink/Cargo.lock | 1 + examples/rust/blink_periodic/Cargo.lock | 1 + examples/rust/button/Cargo.lock | 1 + examples/rust/button_abort/Cargo.lock | 1 + examples/rust/ccm/Cargo.lock | 1 + examples/rust/clock/Cargo.lock | 1 + examples/rust/ctap/Cargo.lock | 1 + examples/rust/ec_test/Cargo.lock | 1 + examples/rust/echo/Cargo.lock | 1 + examples/rust/exercises/client/Cargo.lock | 1 + examples/rust/exercises/interface/Cargo.lock | 1 + examples/rust/exercises/part-1-sol/Cargo.lock | 1 + examples/rust/exercises/part-1/Cargo.lock | 1 + examples/rust/exercises/part-2-sol/Cargo.lock | 1 + examples/rust/exercises/part-2/Cargo.lock | 1 + examples/rust/exercises/part-3-sol/Cargo.lock | 1 + examples/rust/exercises/part-3/Cargo.lock | 1 + examples/rust/exercises/part-4-sol/Cargo.lock | 1 + examples/rust/exercises/part-4/Cargo.lock | 1 + examples/rust/exercises/part-5-sol/Cargo.lock | 1 + examples/rust/exercises/part-5/Cargo.lock | 1 + examples/rust/exercises/part-6-sol/Cargo.lock | 1 + examples/rust/exercises/part-6/Cargo.lock | 1 + examples/rust/exercises/part-7-sol/Cargo.lock | 1 + examples/rust/exercises/part-7/Cargo.lock | 1 + examples/rust/gcm_test/Cargo.lock | 1 + examples/rust/hash_test/Cargo.lock | 1 + examples/rust/hello/Cargo.lock | 1 + examples/rust/hsm/Cargo.lock | 1 + examples/rust/hsm/common/Cargo.lock | 1 + examples/rust/led/Cargo.lock | 1 + examples/rust/memory_game/Cargo.lock | 1 + examples/rust/oom/Cargo.lock | 1 + examples/rust/panic/Cargo.lock | 1 + examples/rust/perf/Cargo.lock | 1 + examples/rust/rand/Cargo.lock | 1 + examples/rust/reboot/Cargo.lock | 1 + examples/rust/rng_test/Cargo.lock | 1 + examples/rust/store/Cargo.lock | 1 + examples/rust/store_test/Cargo.lock | 1 + examples/rust/sync_test/Cargo.lock | 1 + examples/rust/syscall_test/Cargo.lock | 1 + examples/rust/timer_test/Cargo.lock | 1 + examples/rust/update/Cargo.lock | 1 + 73 files changed, 749 insertions(+), 293 deletions(-) create mode 100644 crates/api-desc/src/radio/ble.rs create mode 100644 crates/board/src/radio/ble.rs create mode 100644 crates/prelude/src/radio/ble.rs create mode 100644 crates/runner-nordic/src/tasks/radio/ble.rs create mode 100644 crates/scheduler/src/call/radio/ble.rs create mode 100644 crates/scheduler/src/event/radio/ble.rs diff --git a/crates/api-desc/CHANGELOG.md b/crates/api-desc/CHANGELOG.md index b9662d704..c367b7b2d 100644 --- a/crates/api-desc/CHANGELOG.md +++ b/crates/api-desc/CHANGELOG.md @@ -4,7 +4,7 @@ ### Minor -- Add `radio` module +- Add `radio::ble` module - Add `platform::update::is_supported()` - Add `platform::update` module - Add `platform` module with `platform::reboot()` function diff --git a/crates/api-desc/src/lib.rs b/crates/api-desc/src/lib.rs index bd2da8f50..f340e1bc5 100644 --- a/crates/api-desc/src/lib.rs +++ b/crates/api-desc/src/lib.rs @@ -143,6 +143,10 @@ enum Type { signed: bool, bits: Option, }, + Array { + type_: Box, + length: usize, + }, Pointer { mutable: bool, @@ -569,6 +573,7 @@ impl Type { Type::Integer { bits: None, .. } => true, Type::Integer { bits: Some(32), .. } => true, Type::Integer { bits: Some(_), .. } => false, + Type::Array { .. } => false, Type::Pointer { .. } => true, Type::Function { .. } => true, } @@ -578,6 +583,7 @@ impl Type { match self { Type::Integer { bits: None, .. } => true, Type::Integer { bits: Some(_), .. } => false, + Type::Array { .. } => false, Type::Pointer { .. } => true, Type::Function { .. } => true, } @@ -595,9 +601,19 @@ impl Type { match self { Type::Integer { signed: true, bits: None } => quote!(isize), Type::Integer { signed: false, bits: None } => quote!(usize), + Type::Integer { signed: true, bits: Some(8) } => quote!(i8), + Type::Integer { signed: false, bits: Some(8) } => quote!(u8), + Type::Integer { signed: true, bits: Some(16) } => quote!(i16), + Type::Integer { signed: false, bits: Some(16) } => quote!(u16), + Type::Integer { signed: true, bits: Some(32) } => quote!(i32), Type::Integer { signed: false, bits: Some(32) } => quote!(u32), + Type::Integer { signed: true, bits: Some(64) } => quote!(i64), Type::Integer { signed: false, bits: Some(64) } => quote!(u64), Type::Integer { .. } => unimplemented!(), + Type::Array { type_, length } => { + let type_ = type_.wasm_rust(); + quote!([#type_; #length]) + } Type::Pointer { mutable, type_ } => { let mutable = if *mutable { quote!(mut) } else { quote!(const) }; let type_ = match type_ { @@ -617,9 +633,16 @@ impl Type { match self { Type::Integer { signed: true, bits: None } => write!(output, "isize"), Type::Integer { signed: false, bits: None } => write!(output, "usize"), + Type::Integer { signed: true, bits: Some(8) } => write!(output, "i8"), + Type::Integer { signed: false, bits: Some(8) } => write!(output, "u8"), + Type::Integer { signed: true, bits: Some(16) } => write!(output, "i16"), + Type::Integer { signed: false, bits: Some(16) } => write!(output, "u16"), + Type::Integer { signed: true, bits: Some(32) } => write!(output, "i32"), Type::Integer { signed: false, bits: Some(32) } => write!(output, "u32"), + Type::Integer { signed: true, bits: Some(64) } => write!(output, "i64"), Type::Integer { signed: false, bits: Some(64) } => write!(output, "u64"), Type::Integer { .. } => unimplemented!(), + Type::Array { .. } => write!(output, "unimplemented"), // TODO: Is there a way to decorate this better? Type::Pointer { mutable: _, type_: _ } => write!(output, "usize"), // TODO: Is there a way to decorate this better? diff --git a/crates/api-desc/src/macros.rs b/crates/api-desc/src/macros.rs index 432d1e0ea..f88baaac6 100644 --- a/crates/api-desc/src/macros.rs +++ b/crates/api-desc/src/macros.rs @@ -16,8 +16,17 @@ macro_rules! type_ { (usize) => (Type::Integer { signed: false, bits: None }); (isize) => (Type::Integer { signed: true, bits: None }); + (u8) => (Type::Integer { signed: false, bits: Some(8) }); + (i8) => (Type::Integer { signed: true, bits: Some(8) }); + (u16) => (Type::Integer { signed: false, bits: Some(16) }); + (i16) => (Type::Integer { signed: true, bits: Some(16) }); (u32) => (Type::Integer { signed: false, bits: Some(32) }); + (i32) => (Type::Integer { signed: true, bits: Some(32) }); (u64) => (Type::Integer { signed: false, bits: Some(64) }); + (i64) => (Type::Integer { signed: true, bits: Some(64) }); + ([$len:tt; $($type:tt)*]) => { + Type::Array { type_: Box::new(type_!($($type)*)), length: $len } + }; (*const $($type:tt)*) => (type_!(*false $($type)*)); (*mut $($type:tt)*) => (type_!(*true $($type)*)); (*$mut:tt u8) => (Type::Pointer { mutable: $mut, type_: None }); diff --git a/crates/api-desc/src/radio.rs b/crates/api-desc/src/radio.rs index e22f4d0ce..e51618eb4 100644 --- a/crates/api-desc/src/radio.rs +++ b/crates/api-desc/src/radio.rs @@ -14,49 +14,21 @@ use crate::*; +mod ble; + pub(crate) fn new() -> Item { let docs = docs! { /// Radio operations. }; let name = "radio".into(); let items = vec![ - item! { - /// Reads radio packet into a buffer. - fn read "rr" { - /// Address of the buffer. - ptr: *mut u8, - - /// Length of the buffer in bytes. - len: usize, - } -> { - /// Number of bytes read (or negative value for errors). - /// - /// This function does not block and may return zero. - len: isize, - } - }, - item! { - /// Register a handler for radio events. - fn register "re" { - /// Function called on radio events. - /// - /// The function takes its opaque `data` as argument. - handler_func: fn { data: *mut u8 }, - - /// The opaque data to use when calling the handler function. - handler_data: *mut u8, - } -> {} - }, - item! { - /// Unregister handlers for radio events. - fn unregister "rd" {} -> {} - }, item! { /// Describes errors on radio operations. enum Error { Unknown = 0, } }, + ble::new(), ]; Item::Mod(Mod { docs, name, items }) } diff --git a/crates/api-desc/src/radio/ble.rs b/crates/api-desc/src/radio/ble.rs new file mode 100644 index 000000000..8c987cbc9 --- /dev/null +++ b/crates/api-desc/src/radio/ble.rs @@ -0,0 +1,78 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; + +pub(crate) fn new() -> Item { + let docs = docs! { + /// Bluetooth Low Energy (BLE) operations. + }; + let name = "ble".into(); + let items = vec![ + item! { + /// BLE events. + enum Event { + /// Advertisement packets. + Advertisement = 0, + } + }, + item! { + /// Advertisement packet. + struct Advertisement { + ticks: u32, + freq: u16, + rssi: i8, + pdu_type: u8, + addr: [6; u8], + data_len: u8, + data: [31; u8], + _padding: [2; u8], + } + }, + item! { + /// Reads the next advertisement packet into a buffer, if any. + fn read_advertisement "rlra" { + /// Pointer to the [`super::Advertisement`] packet. + ptr: *mut u8, + } -> { + /// One if a packet was read. Zero if there was no packet to read. Otherwise + /// complement of error number. + res: isize, + } + }, + item! { + /// Register a handler for radio events. + fn register "rle" { + /// Radio [`super::Event`] to listen to. + event: u32, + + /// Function called on radio events. + /// + /// The function takes its opaque `data` as argument. + handler_func: fn { data: *mut u8 }, + + /// The opaque data to use when calling the handler function. + handler_data: *mut u8, + } -> {} + }, + item! { + /// Unregister handlers for radio events. + fn unregister "rd" { + /// Radio [`super::Event`] to stop listening to. + event: u32, + } -> {} + }, + ]; + Item::Mod(Mod { docs, name, items }) +} diff --git a/crates/board/Cargo.lock b/crates/board/Cargo.lock index b25b66a2b..50491a41f 100644 --- a/crates/board/Cargo.lock +++ b/crates/board/Cargo.lock @@ -37,6 +37,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "atomic-polyfill" version = "0.1.11" @@ -73,6 +85,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -107,6 +139,44 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + [[package]] name = "const-oid" version = "0.9.5" @@ -159,6 +229,32 @@ dependencies = [ "cipher", ] +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "data-encoding-macro" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c01c06f5f429efdf2bae21eb67c28b3df3cf85b7dd2d8ef09c0838dac5d33e" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0047d07f2c89b17dd631c80450d69841a6b5d7fb17278cbc43d7e4cfcf2576f3" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + [[package]] name = "defmt" version = "0.3.5" @@ -329,6 +425,12 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hmac" version = "0.12.1" @@ -534,6 +636,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sealed" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "sec1" version = "0.7.3" @@ -693,6 +807,36 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "wasefire-applet-api" +version = "0.6.0-git" +dependencies = [ + "bytemuck", + "sealed", + "wasefire-applet-api-macro", +] + +[[package]] +name = "wasefire-applet-api-desc" +version = "0.1.6-git" +dependencies = [ + "anyhow", + "clap", + "data-encoding", + "data-encoding-macro", + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "wasefire-applet-api-macro" +version = "0.6.0-git" +dependencies = [ + "proc-macro2", + "wasefire-applet-api-desc", +] + [[package]] name = "wasefire-board-api" version = "0.6.0-git" @@ -716,6 +860,7 @@ dependencies = [ "typenum", "usb-device", "usbd-serial", + "wasefire-applet-api", "wasefire-logger", "wasefire-store", ] diff --git a/crates/board/Cargo.toml b/crates/board/Cargo.toml index 42a6bf172..30c6f2586 100644 --- a/crates/board/Cargo.toml +++ b/crates/board/Cargo.toml @@ -34,6 +34,7 @@ signature = { version = "2.1.0", default-features = false, optional = true } typenum = { version = "1.17.0", default-features = false } usb-device = { version = "0.3.0", default-features = false } usbd-serial = { version = "0.2.0", default-features = false } +wasefire-applet-api = { version = "0.6.0-git", path = "../api", features = ["host"] } wasefire-logger = { version = "0.1.4-git", path = "../logger" } wasefire-store = { version = "0.2.3-git", path = "../store" } diff --git a/crates/board/src/radio.rs b/crates/board/src/radio.rs index 6f906f56e..c47b425e9 100644 --- a/crates/board/src/radio.rs +++ b/crates/board/src/radio.rs @@ -14,13 +14,15 @@ //! Radio interface. -use crate::{Error, Support, Unsupported}; +use crate::Unsupported; + +pub mod ble; /// Radio event. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Event { - /// A radio packet has been received. - Received, + /// BLE event. + Ble(ble::Event), } impl From for crate::Event { @@ -30,29 +32,12 @@ impl From for crate::Event { } /// Radio interface. -pub trait Api: Support { - /// Enables radio events. - fn enable() -> Result<(), Error>; - - /// Disables radio events. - fn disable() -> Result<(), Error>; - - /// Reads from the radio receive queue into a buffer. - /// - /// Returns the number of bytes read. It could be zero if there's nothing to read. - fn read(output: &mut [u8]) -> Result; +pub trait Api: Send { + type Ble: ble::Api; } -impl Api for Unsupported { - fn enable() -> Result<(), Error> { - unreachable!() - } +pub type Ble = as Api>::Ble; - fn disable() -> Result<(), Error> { - unreachable!() - } - - fn read(_: &mut [u8]) -> Result { - unreachable!() - } +impl Api for Unsupported { + type Ble = Unsupported; } diff --git a/crates/board/src/radio/ble.rs b/crates/board/src/radio/ble.rs new file mode 100644 index 000000000..e9ede897e --- /dev/null +++ b/crates/board/src/radio/ble.rs @@ -0,0 +1,60 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Bluetooth Low Energy (BLE) interface. + +use wasefire_applet_api::radio::ble::Advertisement; + +use crate::{Error, Support, Unsupported}; + +/// BLE event. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum Event { + /// Received an advertisement packet. + Advertisement, +} + +impl From for crate::Event { + fn from(event: Event) -> Self { + super::Event::Ble(event).into() + } +} + +/// BLE interface. +pub trait Api: Support { + /// Enables radio events. + fn enable(event: &Event) -> Result<(), Error>; + + /// Disables radio events. + fn disable(event: &Event) -> Result<(), Error>; + + /// Reads the next advertisement packet, if any. + /// + /// Returns whether a packet was read. + fn read_advertisement(packet: &mut Advertisement) -> Result; +} + +impl Api for Unsupported { + fn enable(_: &Event) -> Result<(), Error> { + unreachable!() + } + + fn disable(_: &Event) -> Result<(), Error> { + unreachable!() + } + + fn read_advertisement(_: &mut Advertisement) -> Result { + unreachable!() + } +} diff --git a/crates/prelude/Cargo.lock b/crates/prelude/Cargo.lock index f3f084c1a..557ed61c5 100644 --- a/crates/prelude/Cargo.lock +++ b/crates/prelude/Cargo.lock @@ -286,6 +286,7 @@ name = "wasefire" version = "0.5.0-git" dependencies = [ "aead", + "bytemuck", "const-default", "crypto-common", "digest", diff --git a/crates/prelude/Cargo.toml b/crates/prelude/Cargo.toml index 30d28fbb2..93d2e944f 100644 --- a/crates/prelude/Cargo.toml +++ b/crates/prelude/Cargo.toml @@ -17,6 +17,7 @@ features = ["rust-crypto"] [dependencies] aead = { version = "0.5.2", default-features = false, features = ["alloc"], optional = true } +bytemuck = { version = "1.14.0", default-features = false } const-default = { version = "1.0.0", default-features = false } crypto-common = { version = "0.1.6", default-features = false, optional = true } digest = { version = "0.10.7", default-features = false, features = ["mac"], optional = true } diff --git a/crates/prelude/src/debug.rs b/crates/prelude/src/debug.rs index ed5f73873..ffba0e400 100644 --- a/crates/prelude/src/debug.rs +++ b/crates/prelude/src/debug.rs @@ -18,6 +18,7 @@ // size (and other performance) doesn't matter. This permits to have a simple debugging that is // completely excluded from release applets. +use bytemuck::Zeroable; use wasefire_applet_api::debug as api; pub use wasefire_applet_api::debug::Perf; @@ -41,7 +42,7 @@ pub fn time() -> u64 { /// Returns the time in micro-seconds since some initial event, split by component. pub fn perf() -> Perf { - let mut result = Perf { platform: 0, applets: 0, waiting: 0 }; + let mut result = Perf::zeroed(); let params = api::perf::Params { ptr: &mut result as *mut Perf as *mut u8 }; unsafe { api::perf(params) }; result diff --git a/crates/prelude/src/radio.rs b/crates/prelude/src/radio.rs index 5fd86cb0e..42cd09377 100644 --- a/crates/prelude/src/radio.rs +++ b/crates/prelude/src/radio.rs @@ -12,87 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use alloc::boxed::Box; +//! Provides API for radio. -use wasefire_applet_api::radio as api; -use wasefire_applet_api::radio::Error; +/// Errors returned by radio operations. +pub use wasefire_applet_api::radio::Error; -/// Reads from radio packet queue into a buffer without blocking. -/// -/// Returns how many bytes were read (and thus written to the buffer). This function does not block, -/// so if there are no data available for read, zero is returned. -pub fn read(buf: &mut [u8]) -> Result { - let params = api::read::Params { ptr: buf.as_mut_ptr(), len: buf.len() }; - let api::read::Results { len } = unsafe { api::read(params) }; +pub mod ble; + +fn convert(len: isize) -> Result { if len < 0 { return Err(Error::Unknown); } Ok(len as usize) } - -/// Provides callback support for radio events. -pub trait Handler: 'static { - /// Called when a radio packet is received. - fn event(&self); -} - -impl Handler for F { - fn event(&self) { - self() - } -} - -/// Provides listening support for radio events. -#[must_use] -pub struct Listener { - handler: *mut H, -} - -impl Listener { - /// Starts listening for radio events. - /// - /// The listener stops listening when dropped. - /// - /// # Examples - /// - /// ```ignore - /// Listener::new(|| debug!("Radio packet has been received")) - /// ``` - pub fn new(handler: H) -> Self { - let handler_func = Self::call; - let handler = Box::into_raw(Box::new(handler)); - let handler_data = handler as *mut u8; - unsafe { api::register(api::register::Params { handler_func, handler_data }) }; - Listener { handler } - } - - /// Stops listening. - /// - /// This is equivalent to calling `core::mem::drop()`. - pub fn stop(self) { - core::mem::drop(self); - } - - /// Drops the listener but continues listening. - /// - /// This is equivalent to calling `core::mem::forget()`. This can be useful if the listener is - /// created deeply in the stack but the callback must continue processing events until the - /// applet exits or traps. - pub fn leak(self) { - core::mem::forget(self); - } - - extern "C" fn call(data: *mut u8) { - let handler = unsafe { &mut *(data as *mut H) }; - handler.event(); - } -} - -impl Drop for Listener { - fn drop(&mut self) { - unsafe { api::unregister() }; - unsafe { - let _ = Box::from_raw(self.handler); - } - } -} diff --git a/crates/prelude/src/radio/ble.rs b/crates/prelude/src/radio/ble.rs new file mode 100644 index 000000000..e78be5027 --- /dev/null +++ b/crates/prelude/src/radio/ble.rs @@ -0,0 +1,93 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use alloc::boxed::Box; + +use bytemuck::Zeroable; +use wasefire_applet_api::radio::ble::{Advertisement, Event}; +use wasefire_applet_api::radio::{ble as api, Error}; + +/// Reads the next advertisement packet, if any. +pub fn read_advertisement() -> Result, Error> { + let mut result = Advertisement::zeroed(); + let params = api::read_advertisement::Params { ptr: &mut result as *mut _ as *mut u8 }; + let api::read_advertisement::Results { res } = unsafe { api::read_advertisement(params) }; + Ok(match super::convert(res)? { + 0 => None, + 1 => Some(result), + _ => unreachable!(), + }) +} + +/// Provides callback support for BLE events. +pub trait Handler: 'static { + /// Called when a BLE event triggers. + fn event(&self, event: Event); +} + +impl Handler for F { + fn event(&self, event: Event) { + self(event) + } +} + +/// Provides listening support for BLE events. +#[must_use] +pub struct Listener { + handler: *mut H, +} + +impl Listener { + /// Starts listening for BLE events. + /// + /// The listener stops listening when dropped. + pub fn new(event: Event, handler: H) -> Self { + assert!(matches!(event, Event::Advertisement), "only advertisement events are supported"); + let event = event as u32; + let handler_func = Self::call; + let handler = Box::into_raw(Box::new(handler)); + let handler_data = handler as *mut u8; + unsafe { api::register(api::register::Params { event, handler_func, handler_data }) }; + Listener { handler } + } + + /// Stops listening. + /// + /// This is equivalent to calling `core::mem::drop()`. + pub fn stop(self) { + core::mem::drop(self); + } + + /// Drops the listener but continues listening. + /// + /// This is equivalent to calling `core::mem::forget()`. This can be useful if the listener is + /// created deeply in the stack but the callback must continue processing events until the + /// applet exits or traps. + pub fn leak(self) { + core::mem::forget(self); + } + + extern "C" fn call(data: *mut u8) { + let handler = unsafe { &mut *(data as *mut H) }; + handler.event(Event::Advertisement); + } +} + +impl Drop for Listener { + fn drop(&mut self) { + let params = api::unregister::Params { event: Event::Advertisement as u32 }; + unsafe { api::unregister(params) }; + drop(unsafe { Box::from_raw(self.handler) }); + } +} diff --git a/crates/prelude/src/usb.rs b/crates/prelude/src/usb.rs index a01bd27de..869d16d3b 100644 --- a/crates/prelude/src/usb.rs +++ b/crates/prelude/src/usb.rs @@ -17,11 +17,11 @@ //! Only serial is currently supported. HID and its derivatives (e.g. CTAP) will be added in the //! future. -pub mod serial; - /// Errors returned by USB operations. pub use wasefire_applet_api::usb::Error; +pub mod serial; + fn convert(len: isize) -> Result { if len < 0 { return Err(Error::Unknown); diff --git a/crates/runner-host/Cargo.lock b/crates/runner-host/Cargo.lock index 69b541c5a..602ea6545 100644 --- a/crates/runner-host/Cargo.lock +++ b/crates/runner-host/Cargo.lock @@ -1812,6 +1812,7 @@ dependencies = [ "typenum", "usb-device", "usbd-serial", + "wasefire-applet-api", "wasefire-logger", "wasefire-store", ] diff --git a/crates/runner-nordic/Cargo.lock b/crates/runner-nordic/Cargo.lock index 7def4d00f..1b436bb53 100644 --- a/crates/runner-nordic/Cargo.lock +++ b/crates/runner-nordic/Cargo.lock @@ -1206,6 +1206,7 @@ dependencies = [ "typenum", "usb-device", "usbd-serial", + "wasefire-applet-api", "wasefire-logger", "wasefire-store", ] diff --git a/crates/runner-nordic/src/main.rs b/crates/runner-nordic/src/main.rs index a40d8652e..00d57dad4 100644 --- a/crates/runner-nordic/src/main.rs +++ b/crates/runner-nordic/src/main.rs @@ -207,19 +207,6 @@ pub struct RadioMetadata { pdu_type: u8, } -impl RadioMetadata { - pub fn len(&self) -> usize { - core::mem::size_of::() - + core::mem::size_of::() - + core::mem::size_of::() - + core::mem::size_of::() - } - - pub fn is_empty(&self) -> bool { - false - } -} - impl From for RadioMetadata { fn from(value: Metadata) -> Self { RadioMetadata { @@ -244,16 +231,6 @@ pub struct BlePacket { data: alloc::vec::Vec, } -impl BlePacket { - pub fn len(&self) -> usize { - self.addr.len() + self.metadata.len() + self.data.len() - } - - pub fn is_empty(&self) -> bool { - false - } -} - pub struct BleAdvScanCallback; impl ScanCallback for BleAdvScanCallback { @@ -322,7 +299,7 @@ fn radio() { if let Some(packet) = BLE_PACKET.take(cs) { if state.ble_packet_queue.len() < 50 { state.ble_packet_queue.push_back(packet); - state.events.push(board::radio::Event::Received.into()); + state.events.push(board::radio::ble::Event::Advertisement.into()); log::warn!("BLE queue size: {}", state.ble_packet_queue.len()); } else { log::warn!( diff --git a/crates/runner-nordic/src/tasks/radio.rs b/crates/runner-nordic/src/tasks/radio.rs index d61697da6..63e7d3d8f 100644 --- a/crates/runner-nordic/src/tasks/radio.rs +++ b/crates/runner-nordic/src/tasks/radio.rs @@ -12,55 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use rubble::bytes::ByteWriter; -use rubble::link::{NextUpdate, RadioCmd}; -use rubble::time::{Duration, Timer}; use wasefire_board_api::radio::Api; -use wasefire_board_api::{Error, Support}; -use wasefire_logger as logger; -use crate::with_state; +mod ble; pub enum Impl {} impl Api for Impl { - fn enable() -> Result<(), Error> { - with_state(|state| { - let scanner_cmd = - state.ble_scanner.configure(state.ble_timer.now(), Duration::millis(500)); - state.ble_radio.configure_receiver(scanner_cmd.radio); - state.ble_timer.configure_interrupt(scanner_cmd.next_update); - Ok(()) - }) - } - - fn disable() -> Result<(), Error> { - with_state(|state| { - state.ble_timer.configure_interrupt(NextUpdate::Disable); - state.ble_radio.configure_receiver(RadioCmd::Off); - Ok(()) - }) - } - - fn read(output: &mut [u8]) -> Result { - with_state(|state| { - let packet = state.ble_packet_queue.pop_front().ok_or(Error::World)?; - let len = packet.len(); - let mut writer = ByteWriter::new(output); - // Serialize RadioMetadata first, then BDADDR and append the packet. - let res: Result<(), rubble::Error> = try { - writer.write_u32_le(packet.metadata.ticks)?; - writer.write_u16_le(packet.metadata.freq)?; - writer.write_slice(&packet.metadata.rssi.to_le_bytes())?; - writer.write_u8(packet.metadata.pdu_type)?; - writer.write_slice(&packet.addr)?; - writer.write_slice(&packet.data)?; - }; - res.map_err(|_| Error::World)?; - logger::trace!("{}{:?} = read({})", len, &output[.. len], output.len()); - Ok(len) - }) - } + type Ble = ble::Impl; } - -impl Supported for Impl {} diff --git a/crates/runner-nordic/src/tasks/radio/ble.rs b/crates/runner-nordic/src/tasks/radio/ble.rs new file mode 100644 index 000000000..c9320ff48 --- /dev/null +++ b/crates/runner-nordic/src/tasks/radio/ble.rs @@ -0,0 +1,68 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use rubble::link::{NextUpdate, RadioCmd}; +use rubble::time::{Duration, Timer}; +use wasefire_applet_api::radio::ble::Advertisement; +use wasefire_board_api::radio::ble::{Api, Event}; +use wasefire_board_api::{Error, Supported}; +use wasefire_logger as log; + +use crate::with_state; + +pub enum Impl {} + +impl Api for Impl { + fn enable(event: &Event) -> Result<(), Error> { + match event { + Event::Advertisement => with_state(|state| { + let scanner_cmd = + state.ble_scanner.configure(state.ble_timer.now(), Duration::millis(500)); + state.ble_radio.configure_receiver(scanner_cmd.radio); + state.ble_timer.configure_interrupt(scanner_cmd.next_update); + Ok(()) + }), + } + } + + fn disable(event: &Event) -> Result<(), Error> { + match event { + Event::Advertisement => with_state(|state| { + state.ble_timer.configure_interrupt(NextUpdate::Disable); + state.ble_radio.configure_receiver(RadioCmd::Off); + Ok(()) + }), + } + } + + fn read_advertisement(output: &mut Advertisement) -> Result { + with_state(|state| { + let input = match state.ble_packet_queue.pop_front() { + Some(x) => x, + None => return Ok(false), + }; + output.ticks = input.metadata.ticks; + output.freq = input.metadata.freq; + output.rssi = input.metadata.rssi; + output.pdu_type = input.metadata.pdu_type; + output.addr = input.addr; + output.data_len = input.data.len() as u8; + output.data[.. input.data.len()].copy_from_slice(&input.data); + log::trace!("read_advertisement() -> {:?}", log::Debug2Format(output)); + Ok(true) + }) + } +} + +impl Supported for Impl {} diff --git a/crates/scheduler/Cargo.lock b/crates/scheduler/Cargo.lock index 068e2b8ec..415aa1536 100644 --- a/crates/scheduler/Cargo.lock +++ b/crates/scheduler/Cargo.lock @@ -551,6 +551,7 @@ dependencies = [ "typenum", "usb-device", "usbd-serial", + "wasefire-applet-api", "wasefire-logger", "wasefire-store", ] diff --git a/crates/scheduler/src/call/radio.rs b/crates/scheduler/src/call/radio.rs index c56b6bda5..9162ec504 100644 --- a/crates/scheduler/src/call/radio.rs +++ b/crates/scheduler/src/call/radio.rs @@ -12,59 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use wasefire_applet_api::radio::{self as api, Api}; -use wasefire_board_api::radio::Api as _; -use wasefire_board_api::{self as board, Api as Board}; +use wasefire_applet_api::radio::Api; +use wasefire_board_api::Api as Board; -use crate::applet::store::MemoryApi; -use crate::event::radio::Key; -use crate::event::Handler; -use crate::{DispatchSchedulerCall, SchedulerCall, Trap}; +use crate::DispatchSchedulerCall; + +mod ble; pub fn process(call: Api>) { match call { - Api::Register(call) => register(call), - Api::Unregister(call) => unregister(call), - Api::Read(call) => read(call), + Api::Ble(call) => ble::process(call), } } - -fn register(mut call: SchedulerCall) { - let api::register::Params { handler_func, handler_data } = call.read(); - let inst = call.inst(); - let results = try { - call.scheduler().applet.enable(Handler { - key: Key::Received.into(), - inst, - func: *handler_func, - data: *handler_data, - })?; - board::Radio::::enable().map_err(|_| Trap)?; - api::register::Results {} - }; - call.reply(results); -} - -fn unregister(mut call: SchedulerCall) { - let results = try { - board::Radio::::disable().map_err(|_| Trap)?; - call.scheduler().disable_event(Key::Received.into())?; - api::unregister::Results {} - }; - call.reply(results); -} - -fn read(mut call: SchedulerCall) { - let api::read::Params { ptr, len } = call.read(); - let scheduler = call.scheduler(); - let memory = scheduler.applet.memory(); - let results = try { - let output = memory.get_mut(*ptr, *len)?; - let len = match board::Radio::::read(output) { - Ok(len) => (len as u32).into(), - Err(_) => u32::MAX.into(), - }; - api::read::Results { len } - }; - call.reply(results); -} diff --git a/crates/scheduler/src/call/radio/ble.rs b/crates/scheduler/src/call/radio/ble.rs new file mode 100644 index 000000000..f2613c575 --- /dev/null +++ b/crates/scheduler/src/call/radio/ble.rs @@ -0,0 +1,81 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use wasefire_applet_api::radio::ble::{self as api, Advertisement, Api}; +use wasefire_board_api::radio::ble::{Api as _, Event}; +use wasefire_board_api::{self as board, Api as Board}; + +use crate::applet::store::MemoryApi; +use crate::event::radio::ble::Key; +use crate::event::Handler; +use crate::{DispatchSchedulerCall, SchedulerCall, Trap}; + +pub fn process(call: Api>) { + match call { + Api::Register(call) => register(call), + Api::Unregister(call) => unregister(call), + Api::ReadAdvertisement(call) => read_advertisement(call), + } +} + +fn register(mut call: SchedulerCall) { + let api::register::Params { event, handler_func, handler_data } = call.read(); + let inst = call.inst(); + let scheduler = call.scheduler(); + let results = try { + let event = convert_event(event)?; + scheduler.applet.enable(Handler { + key: Key::from(&event).into(), + inst, + func: *handler_func, + data: *handler_data, + })?; + board::radio::Ble::::enable(&event).map_err(|_| Trap)?; + api::register::Results {} + }; + call.reply(results); +} + +fn unregister(mut call: SchedulerCall) { + let api::unregister::Params { event } = call.read(); + let scheduler = call.scheduler(); + let results = try { + let event = convert_event(event)?; + board::radio::Ble::::disable(&event).map_err(|_| Trap)?; + scheduler.disable_event(Key::from(&event).into())?; + api::unregister::Results {} + }; + call.reply(results); +} + +fn read_advertisement(mut call: SchedulerCall) { + let api::read_advertisement::Params { ptr } = call.read(); + let scheduler = call.scheduler(); + let memory = scheduler.applet.memory(); + let results = try { + let packet = memory.from_bytes_mut::(*ptr)?; + let res = match board::radio::Ble::::read_advertisement(packet) { + Ok(read) => (read as u32).into(), + Err(_) => u32::MAX.into(), + }; + api::read_advertisement::Results { res } + }; + call.reply(results); +} + +fn convert_event(event: u32) -> Result { + Ok(match api::Event::try_from(event)? { + api::Event::Advertisement => Event::Advertisement, + }) +} diff --git a/crates/scheduler/src/call/usb.rs b/crates/scheduler/src/call/usb.rs index 4d4a62b66..99d4202c2 100644 --- a/crates/scheduler/src/call/usb.rs +++ b/crates/scheduler/src/call/usb.rs @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod serial; - use wasefire_applet_api::usb::Api; use wasefire_board_api::Api as Board; use crate::DispatchSchedulerCall; +mod serial; + pub fn process(call: Api>) { match call { Api::Serial(call) => serial::process(call), diff --git a/crates/scheduler/src/event.rs b/crates/scheduler/src/event.rs index 9d0c536f1..88ab6e7cc 100644 --- a/crates/scheduler/src/event.rs +++ b/crates/scheduler/src/event.rs @@ -109,7 +109,7 @@ pub fn process(scheduler: &mut Scheduler, event: Event) { let mut params = vec![*func, *data]; match event { Event::Button(event) => button::process(event, &mut params), - Event::Radio(_) => radio::process(), + Event::Radio(event) => radio::process(event), Event::Timer(_) => timer::process(), Event::Uart(_) => uart::process(), Event::Usb(event) => usb::process(event), diff --git a/crates/scheduler/src/event/radio.rs b/crates/scheduler/src/event/radio.rs index 0e6ad201e..2b6850579 100644 --- a/crates/scheduler/src/event/radio.rs +++ b/crates/scheduler/src/event/radio.rs @@ -15,9 +15,11 @@ use wasefire_board_api::radio::Event; use wasefire_board_api::Api as Board; +pub mod ble; + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Key { - Received, + Ble(ble::Key), } impl From for crate::event::Key { @@ -29,9 +31,13 @@ impl From for crate::event::Key { impl<'a> From<&'a Event> for Key { fn from(event: &'a Event) -> Self { match event { - Event::Received => Key::Received, + Event::Ble(event) => Key::Ble(event.into()), } } } -pub fn process() {} +pub fn process(event: Event) { + match event { + Event::Ble(_) => ble::process(), + } +} diff --git a/crates/scheduler/src/event/radio/ble.rs b/crates/scheduler/src/event/radio/ble.rs new file mode 100644 index 000000000..e0883e1bb --- /dev/null +++ b/crates/scheduler/src/event/radio/ble.rs @@ -0,0 +1,37 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use wasefire_board_api::radio::ble::Event; +use wasefire_board_api::Api as Board; + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum Key { + Advertisement, +} + +impl From for crate::event::Key { + fn from(key: Key) -> Self { + super::Key::Ble(key).into() + } +} + +impl<'a> From<&'a Event> for Key { + fn from(event: &'a Event) -> Self { + match event { + Event::Advertisement => Key::Advertisement, + } + } +} + +pub fn process() {} diff --git a/crates/scheduler/src/event/usb.rs b/crates/scheduler/src/event/usb.rs index 47bec0654..ca6182b4c 100644 --- a/crates/scheduler/src/event/usb.rs +++ b/crates/scheduler/src/event/usb.rs @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub mod serial; - use wasefire_board_api::usb::Event; use wasefire_board_api::Api as Board; +pub mod serial; + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Key { Serial(serial::Key), diff --git a/examples/assemblyscript/api.ts b/examples/assemblyscript/api.ts index ea89ee6d1..fcc8cc9b4 100644 --- a/examples/assemblyscript/api.ts +++ b/examples/assemblyscript/api.ts @@ -751,40 +751,69 @@ // START OF MODULE radio // Radio operations. - // Reads radio packet into a buffer. - @external("env", "rr") - export declare function radio_read( - // Address of the buffer. - ptr: usize, + // Describes errors on radio operations. + enum radio_Error { + Unknown = 0, + } - // Length of the buffer in bytes. - len: usize, - // Number of bytes read (or negative value for errors). - // - // This function does not block and may return zero. - ): isize + // START OF MODULE radio_ble + // Bluetooth Low Energy (BLE) operations. + // BLE events. + enum radio_ble_Event { + // Advertisement packets. + Advertisement = 0, + } - // Register a handler for radio events. - @external("env", "re") - export declare function radio_register( - // Function called on radio events. - // - // The function takes its opaque `data` as argument. - handler_func: usize, + // Advertisement packet. + class radio_ble_Advertisement { + ticks: u32; - // The opaque data to use when calling the handler function. - handler_data: usize, - ): void + freq: u16; - // Unregister handlers for radio events. - @external("env", "rd") - export declare function radio_unregister( - ): void + rssi: i8; - // Describes errors on radio operations. - enum radio_Error { - Unknown = 0, - } + pdu_type: u8; + + addr: unimplemented; + + data_len: u8; + + data: unimplemented; + + _padding: unimplemented; + } + + // Reads the next advertisement packet into a buffer, if any. + @external("env", "rlra") + export declare function radio_ble_read_advertisement( + // Pointer to the [`super::Advertisement`] packet. + ptr: usize, + // One if a packet was read. Zero if there was no packet to read. Otherwise + // complement of error number. + ): isize + + // Register a handler for radio events. + @external("env", "rle") + export declare function radio_ble_register( + // Radio [`super::Event`] to listen to. + event: u32, + + // Function called on radio events. + // + // The function takes its opaque `data` as argument. + handler_func: usize, + + // The opaque data to use when calling the handler function. + handler_data: usize, + ): void + + // Unregister handlers for radio events. + @external("env", "rd") + export declare function radio_ble_unregister( + // Radio [`super::Event`] to stop listening to. + event: u32, + ): void + // END OF MODULE radio_ble // END OF MODULE radio // START OF MODULE rng diff --git a/examples/rust/blink/Cargo.lock b/examples/rust/blink/Cargo.lock index 0c0a9bc62..10748298e 100644 --- a/examples/rust/blink/Cargo.lock +++ b/examples/rust/blink/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/blink_periodic/Cargo.lock b/examples/rust/blink_periodic/Cargo.lock index b609c303a..287facb0f 100644 --- a/examples/rust/blink_periodic/Cargo.lock +++ b/examples/rust/blink_periodic/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/button/Cargo.lock b/examples/rust/button/Cargo.lock index 0716a0bf0..5ab1fa45c 100644 --- a/examples/rust/button/Cargo.lock +++ b/examples/rust/button/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/button_abort/Cargo.lock b/examples/rust/button_abort/Cargo.lock index cd907b5cb..7bdf921bb 100644 --- a/examples/rust/button_abort/Cargo.lock +++ b/examples/rust/button_abort/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/ccm/Cargo.lock b/examples/rust/ccm/Cargo.lock index 69bd4c537..09f31921f 100644 --- a/examples/rust/ccm/Cargo.lock +++ b/examples/rust/ccm/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/clock/Cargo.lock b/examples/rust/clock/Cargo.lock index d0ff242af..2c95b33a9 100644 --- a/examples/rust/clock/Cargo.lock +++ b/examples/rust/clock/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/ctap/Cargo.lock b/examples/rust/ctap/Cargo.lock index 14bddb95a..1223a8ba8 100644 --- a/examples/rust/ctap/Cargo.lock +++ b/examples/rust/ctap/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/ec_test/Cargo.lock b/examples/rust/ec_test/Cargo.lock index 33f75d797..4c87e9731 100644 --- a/examples/rust/ec_test/Cargo.lock +++ b/examples/rust/ec_test/Cargo.lock @@ -532,6 +532,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/echo/Cargo.lock b/examples/rust/echo/Cargo.lock index 2b87952d4..6fa3ac686 100644 --- a/examples/rust/echo/Cargo.lock +++ b/examples/rust/echo/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/client/Cargo.lock b/examples/rust/exercises/client/Cargo.lock index 0c749750c..0f0c6c6eb 100644 --- a/examples/rust/exercises/client/Cargo.lock +++ b/examples/rust/exercises/client/Cargo.lock @@ -824,6 +824,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/interface/Cargo.lock b/examples/rust/exercises/interface/Cargo.lock index 4c832e54b..f09c98ebb 100644 --- a/examples/rust/exercises/interface/Cargo.lock +++ b/examples/rust/exercises/interface/Cargo.lock @@ -301,6 +301,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-1-sol/Cargo.lock b/examples/rust/exercises/part-1-sol/Cargo.lock index 69778bba1..200af89c2 100644 --- a/examples/rust/exercises/part-1-sol/Cargo.lock +++ b/examples/rust/exercises/part-1-sol/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-1/Cargo.lock b/examples/rust/exercises/part-1/Cargo.lock index 8f4ef0288..84a8b5a3b 100644 --- a/examples/rust/exercises/part-1/Cargo.lock +++ b/examples/rust/exercises/part-1/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-2-sol/Cargo.lock b/examples/rust/exercises/part-2-sol/Cargo.lock index 6fbaa16d0..739433040 100644 --- a/examples/rust/exercises/part-2-sol/Cargo.lock +++ b/examples/rust/exercises/part-2-sol/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-2/Cargo.lock b/examples/rust/exercises/part-2/Cargo.lock index 49ced841d..f2f84513e 100644 --- a/examples/rust/exercises/part-2/Cargo.lock +++ b/examples/rust/exercises/part-2/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-3-sol/Cargo.lock b/examples/rust/exercises/part-3-sol/Cargo.lock index 83965af14..4c27e3a2d 100644 --- a/examples/rust/exercises/part-3-sol/Cargo.lock +++ b/examples/rust/exercises/part-3-sol/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-3/Cargo.lock b/examples/rust/exercises/part-3/Cargo.lock index dea854399..1312493d0 100644 --- a/examples/rust/exercises/part-3/Cargo.lock +++ b/examples/rust/exercises/part-3/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-4-sol/Cargo.lock b/examples/rust/exercises/part-4-sol/Cargo.lock index 3fac6c173..70c66e62c 100644 --- a/examples/rust/exercises/part-4-sol/Cargo.lock +++ b/examples/rust/exercises/part-4-sol/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-4/Cargo.lock b/examples/rust/exercises/part-4/Cargo.lock index 89395447a..0617d0e75 100644 --- a/examples/rust/exercises/part-4/Cargo.lock +++ b/examples/rust/exercises/part-4/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-5-sol/Cargo.lock b/examples/rust/exercises/part-5-sol/Cargo.lock index 1780d285c..e128f5fa6 100644 --- a/examples/rust/exercises/part-5-sol/Cargo.lock +++ b/examples/rust/exercises/part-5-sol/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-5/Cargo.lock b/examples/rust/exercises/part-5/Cargo.lock index 7465d3e89..cb3e8e109 100644 --- a/examples/rust/exercises/part-5/Cargo.lock +++ b/examples/rust/exercises/part-5/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-6-sol/Cargo.lock b/examples/rust/exercises/part-6-sol/Cargo.lock index 00e094576..21186553b 100644 --- a/examples/rust/exercises/part-6-sol/Cargo.lock +++ b/examples/rust/exercises/part-6-sol/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-6/Cargo.lock b/examples/rust/exercises/part-6/Cargo.lock index 32371e727..2cddc2c35 100644 --- a/examples/rust/exercises/part-6/Cargo.lock +++ b/examples/rust/exercises/part-6/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-7-sol/Cargo.lock b/examples/rust/exercises/part-7-sol/Cargo.lock index 5ba02d92c..facf30957 100644 --- a/examples/rust/exercises/part-7-sol/Cargo.lock +++ b/examples/rust/exercises/part-7-sol/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/exercises/part-7/Cargo.lock b/examples/rust/exercises/part-7/Cargo.lock index 82f83f743..ca1abece9 100644 --- a/examples/rust/exercises/part-7/Cargo.lock +++ b/examples/rust/exercises/part-7/Cargo.lock @@ -309,6 +309,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/gcm_test/Cargo.lock b/examples/rust/gcm_test/Cargo.lock index 86b7fd9bb..ebd8e05bd 100644 --- a/examples/rust/gcm_test/Cargo.lock +++ b/examples/rust/gcm_test/Cargo.lock @@ -294,6 +294,7 @@ name = "wasefire" version = "0.5.0-git" dependencies = [ "aead", + "bytemuck", "const-default", "crypto-common", "digest", diff --git a/examples/rust/hash_test/Cargo.lock b/examples/rust/hash_test/Cargo.lock index 399bed9bd..cdc491df4 100644 --- a/examples/rust/hash_test/Cargo.lock +++ b/examples/rust/hash_test/Cargo.lock @@ -544,6 +544,7 @@ name = "wasefire" version = "0.5.0-git" dependencies = [ "aead", + "bytemuck", "const-default", "crypto-common", "digest", diff --git a/examples/rust/hello/Cargo.lock b/examples/rust/hello/Cargo.lock index 0db5eba1a..241d152a8 100644 --- a/examples/rust/hello/Cargo.lock +++ b/examples/rust/hello/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/hsm/Cargo.lock b/examples/rust/hsm/Cargo.lock index 1a4483632..f0d31b6e9 100644 --- a/examples/rust/hsm/Cargo.lock +++ b/examples/rust/hsm/Cargo.lock @@ -264,6 +264,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/hsm/common/Cargo.lock b/examples/rust/hsm/common/Cargo.lock index 7fa1c6709..6e9038981 100644 --- a/examples/rust/hsm/common/Cargo.lock +++ b/examples/rust/hsm/common/Cargo.lock @@ -277,6 +277,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/led/Cargo.lock b/examples/rust/led/Cargo.lock index 003cc6b95..35b553812 100644 --- a/examples/rust/led/Cargo.lock +++ b/examples/rust/led/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/memory_game/Cargo.lock b/examples/rust/memory_game/Cargo.lock index 638cf03bd..e62f360fc 100644 --- a/examples/rust/memory_game/Cargo.lock +++ b/examples/rust/memory_game/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/oom/Cargo.lock b/examples/rust/oom/Cargo.lock index fd00dfeba..e7caf5f6a 100644 --- a/examples/rust/oom/Cargo.lock +++ b/examples/rust/oom/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/panic/Cargo.lock b/examples/rust/panic/Cargo.lock index a93e663fe..fa5da5b55 100644 --- a/examples/rust/panic/Cargo.lock +++ b/examples/rust/panic/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/perf/Cargo.lock b/examples/rust/perf/Cargo.lock index 431be026c..4ad7235ea 100644 --- a/examples/rust/perf/Cargo.lock +++ b/examples/rust/perf/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/rand/Cargo.lock b/examples/rust/rand/Cargo.lock index 00ef88092..12a493718 100644 --- a/examples/rust/rand/Cargo.lock +++ b/examples/rust/rand/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/reboot/Cargo.lock b/examples/rust/reboot/Cargo.lock index 4c73fdc15..86e64acba 100644 --- a/examples/rust/reboot/Cargo.lock +++ b/examples/rust/reboot/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/rng_test/Cargo.lock b/examples/rust/rng_test/Cargo.lock index 0cab8440a..c9ed99c9d 100644 --- a/examples/rust/rng_test/Cargo.lock +++ b/examples/rust/rng_test/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/store/Cargo.lock b/examples/rust/store/Cargo.lock index 25e59dc69..ba384dd78 100644 --- a/examples/rust/store/Cargo.lock +++ b/examples/rust/store/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/store_test/Cargo.lock b/examples/rust/store_test/Cargo.lock index 85998afad..99eaecf15 100644 --- a/examples/rust/store_test/Cargo.lock +++ b/examples/rust/store_test/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/sync_test/Cargo.lock b/examples/rust/sync_test/Cargo.lock index c37ad9290..eb1f3ae69 100644 --- a/examples/rust/sync_test/Cargo.lock +++ b/examples/rust/sync_test/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/syscall_test/Cargo.lock b/examples/rust/syscall_test/Cargo.lock index 8d00a7538..fecae8ef3 100644 --- a/examples/rust/syscall_test/Cargo.lock +++ b/examples/rust/syscall_test/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/timer_test/Cargo.lock b/examples/rust/timer_test/Cargo.lock index 841a4fc5f..ba9a196e3 100644 --- a/examples/rust/timer_test/Cargo.lock +++ b/examples/rust/timer_test/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", diff --git a/examples/rust/update/Cargo.lock b/examples/rust/update/Cargo.lock index c66daeee5..984f9065d 100644 --- a/examples/rust/update/Cargo.lock +++ b/examples/rust/update/Cargo.lock @@ -256,6 +256,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "wasefire" version = "0.5.0-git" dependencies = [ + "bytemuck", "const-default", "generic-array", "rlsf", From c1af9cb859605517d2625aa0913f4a97d1cbb070 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Fri, 15 Dec 2023 13:19:40 +0100 Subject: [PATCH 12/16] Remove unused file --- crates/runner-nordic/src/radio.rs | 114 ------------------------------ 1 file changed, 114 deletions(-) delete mode 100644 crates/runner-nordic/src/radio.rs diff --git a/crates/runner-nordic/src/radio.rs b/crates/runner-nordic/src/radio.rs deleted file mode 100644 index 9237731c0..000000000 --- a/crates/runner-nordic/src/radio.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use rubble::beacon::ScanCallback; -use rubble::bytes::ByteReader; -use rubble::link::{Cmd, Metadata, NextUpdate, RadioCmd}; -use rubble::link::advertising::{Header, Pdu}; -use rubble::link::filter::{self, AddressFilter, ScanFilter}; -use rubble::phy::AdvertisingChannel; -use rubble::time::{Duration, Instant}; - -/// A passive scanner for BLE trackers advertisements. -/// Based on the Beacon scanner in rubble but incorporate some tricks -/// to handle BLE trackers. -pub struct BleTrackerScanner { - cb: C, - filter: ScanFilter, - interval: Duration, - channel: AdvertisingChannel, -} - -impl BleTrackerScanner { - /// Creates a `BleTrackerScanner` that will report beacons from any device. - pub fn new(callback: C) -> Self { - Self::with_filter(callback, filter::AllowAll) - } -} - -impl BleTrackerScanner { - /// Creates a `BleTrackerScanner` with a custom device filter. - pub fn with_filter(callback: C, scan_filter: F) -> Self { - Self { - cb: callback, - filter: ScanFilter::new(scan_filter), - interval: Duration::micros(0), - channel: AdvertisingChannel::first(), - } - } - - /// Configures the `BleTrackerScanner` and returns a `Cmd` to apply to the radio. - /// - /// The `next_update` field of the returned `Cmd` specifies when to call `timer_update` the next - /// time. The timer used for this needs to be pretty accurate because of advanced advertising packets, - /// which require to precisely switch channel to a data channel to capture the whole advertisement. - /// Otherwise it is only used to switch to the next advertising channel after `interval` elapses. - pub fn configure(&mut self, now: Instant, interval: Duration) -> Cmd { - self.interval = interval; - self.channel = AdvertisingChannel::first(); - - Cmd { - next_update: NextUpdate::At(now + self.interval), - radio: RadioCmd::ListenAdvertising { - channel: self.channel, - }, - queued_work: false, - } - } - - /// Updates the `BleTrackerScanner` after the configured timer has fired. - /// - /// This switches to the next advertising channel and will listen there. - pub fn timer_update(&mut self, now: Instant) -> Cmd { - self.channel = self.channel.cycle(); - - Cmd { - next_update: NextUpdate::At(now + self.interval), - radio: RadioCmd::ListenAdvertising { - channel: self.channel, - }, - queued_work: false, - } - } - - /// Processes a received advertising channel packet. - /// - /// This should be called whenever the radio receives a packet on the configured advertising - /// channel. - pub fn process_adv_packet( - &mut self, - header: Header, - payload: &[u8], - metadata: Metadata, - ) -> Cmd { - if metadata.crc_ok && header.type_().is_beacon() { - // Partially decode to get the device ID and run it through the filter. - if let Ok(pdu) = Pdu::from_header_and_payload(header, &mut ByteReader::new(payload)) { - if self.filter.should_scan(*pdu.sender()) { - let ad = pdu.advertising_data().unwrap(); - self.cb.beacon(*pdu.sender(), ad, metadata); - } - } - } - - Cmd { - next_update: NextUpdate::Keep, - radio: RadioCmd::ListenAdvertising { - channel: self.channel, - }, - queued_work: false, - } - } -} - From a94c049a5eb2a9bff30d103062537883446d35f4 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Fri, 15 Dec 2023 13:35:48 +0100 Subject: [PATCH 13/16] More radio logic to radio module --- crates/runner-nordic/src/main.rs | 113 ++--------------- crates/runner-nordic/src/tasks.rs | 2 +- crates/runner-nordic/src/tasks/radio.rs | 2 +- crates/runner-nordic/src/tasks/radio/ble.rs | 130 ++++++++++++++++++-- 4 files changed, 132 insertions(+), 115 deletions(-) diff --git a/crates/runner-nordic/src/main.rs b/crates/runner-nordic/src/main.rs index 00d57dad4..ea9064104 100644 --- a/crates/runner-nordic/src/main.rs +++ b/crates/runner-nordic/src/main.rs @@ -25,7 +25,6 @@ mod storage; mod systick; mod tasks; -use alloc::collections::VecDeque; use core::cell::RefCell; use core::mem::MaybeUninit; @@ -38,7 +37,7 @@ use nrf52840_hal::ccm::{Ccm, DataRate}; use nrf52840_hal::clocks::{self, ExternalOscillator, Internal, LfOscStopped}; use nrf52840_hal::gpio::{Level, Output, Pin, PushPull}; use nrf52840_hal::gpiote::Gpiote; -use nrf52840_hal::pac::{interrupt, Interrupt, TIMER0}; +use nrf52840_hal::pac::{interrupt, Interrupt}; use nrf52840_hal::prelude::InputPin; use nrf52840_hal::rng::Rng; use nrf52840_hal::usbd::{UsbPeripheral, Usbd}; @@ -47,14 +46,8 @@ use nrf52840_hal::{gpio, uarte}; use panic_abort as _; #[cfg(feature = "debug")] use panic_probe as _; -use rubble::beacon::{BeaconScanner, ScanCallback}; -use rubble::bytes::{ByteWriter, ToBytes}; -use rubble::link::ad_structure::AdStructure; -use rubble::link::filter::AllowAll; -use rubble::link::{DeviceAddress, Metadata, MIN_PDU_BUF}; -use rubble::time::Timer; +use rubble::link::MIN_PDU_BUF; use rubble_nrf5x::radio::{BleRadio, PacketBuffer}; -use rubble_nrf5x::timer::BleTimer; use usb_device::class_prelude::UsbBusAllocator; use usb_device::device::{StringDescriptors, UsbDevice, UsbDeviceBuilder, UsbVidPid}; use usbd_serial::{SerialPort, USB_CLASS_CDC}; @@ -66,6 +59,7 @@ use {wasefire_board_api as board, wasefire_logger as log}; use crate::storage::Storage; use crate::tasks::button::{self, channel, Button}; use crate::tasks::clock::Timers; +use crate::tasks::radio::ble::Ble; use crate::tasks::uart::Uarts; use crate::tasks::usb::Usb; use crate::tasks::{led, Events}; @@ -84,12 +78,9 @@ struct State { gpiote: Gpiote, serial: Serial<'static, Usb>, timers: Timers, + ble: Ble, ccm: Ccm, leds: [Pin>; >::SUPPORT], - ble_radio: BleRadio, - ble_scanner: BeaconScanner, - ble_timer: BleTimer, - ble_packet_queue: VecDeque, rng: Rng, storage: Option, uarts: Uarts, @@ -99,7 +90,6 @@ struct State { pub enum Board {} static STATE: Mutex>> = Mutex::new(RefCell::new(None)); -static BLE_PACKET: Mutex>> = Mutex::new(RefCell::new(None)); fn with_state(f: impl FnOnce(&mut State) -> R) -> R { critical_section::with(|cs| f(STATE.borrow_ref_mut(cs).as_mut().unwrap())) @@ -146,15 +136,13 @@ fn main() -> ! { .unwrap() .device_class(USB_CLASS_CDC) .build(); - let ble_radio = BleRadio::new( + let radio = BleRadio::new( p.RADIO, &p.FICR, BLE_TX.write([0; MIN_PDU_BUF]), BLE_RX.write([0; MIN_PDU_BUF]), ); - let ble_timer = BleTimer::init(p.TIMER0); - let ble_scanner = BeaconScanner::new(BleAdvScanCallback); - let ble_packet_queue = VecDeque::::new(); + let ble = Ble::new(radio, p.TIMER0); let rng = Rng::new(p.RNG); let ccm = Ccm::init(p.CCM, p.AAR, DataRate::_1Mbit); storage::init(p.NVMC); @@ -174,12 +162,9 @@ fn main() -> ! { gpiote, serial, timers, + ble, ccm, leds, - ble_radio, - ble_scanner, - ble_timer, - ble_packet_queue, rng, storage, uarts, @@ -200,57 +185,6 @@ fn main() -> ! { Scheduler::::run(); } -pub struct RadioMetadata { - ticks: u32, - freq: u16, - rssi: i8, - pdu_type: u8, -} - -impl From for RadioMetadata { - fn from(value: Metadata) -> Self { - RadioMetadata { - ticks: value.timestamp.unwrap().ticks(), - freq: match value.channel { - ch @ 0 ..= 10 => 2404 + 2 * (ch as u16), - ch @ 11 ..= 36 => 2404 + 2 * (ch as u16 + 1), - 37 => 2402, - 38 => 2426, - 39 => 2480, - _ => 0, - }, - rssi: value.rssi.unwrap(), - pdu_type: u8::from(value.pdu_type.unwrap()), - } - } -} - -pub struct BlePacket { - addr: [u8; 6], - metadata: RadioMetadata, - data: alloc::vec::Vec, -} - -pub struct BleAdvScanCallback; - -impl ScanCallback for BleAdvScanCallback { - fn beacon<'a, I>(&mut self, addr: DeviceAddress, data: I, metadata: Metadata) - where I: Iterator> { - let mut buf: [u8; MIN_PDU_BUF] = [0; MIN_PDU_BUF]; - let mut writer = ByteWriter::new(&mut buf); - for p in data { - assert!(p.to_bytes(&mut writer).is_ok()); - } - let len = MIN_PDU_BUF - writer.space_left(); - let packet = BlePacket { - addr: *addr.raw(), - metadata: metadata.clone().into(), - data: buf[.. len].to_vec(), - }; - assert!(critical_section::with(|cs| BLE_PACKET.replace(cs, Some(packet))).is_none()); - } -} - macro_rules! interrupts { ($($name:ident = $func:ident($($arg:expr),*$(,)?)),*$(,)?) => { const INTERRUPTS: &[Interrupt] = &[$(Interrupt::$name),*]; @@ -290,40 +224,11 @@ fn gpiote() { } fn radio() { - with_state(|state| { - if let Some(next_update) = - state.ble_radio.recv_beacon_interrupt(state.ble_timer.now(), &mut state.ble_scanner) - { - state.ble_timer.configure_interrupt(next_update); - critical_section::with(|cs| { - if let Some(packet) = BLE_PACKET.take(cs) { - if state.ble_packet_queue.len() < 50 { - state.ble_packet_queue.push_back(packet); - state.events.push(board::radio::ble::Event::Advertisement.into()); - log::warn!("BLE queue size: {}", state.ble_packet_queue.len()); - } else { - log::warn!( - "BLE Packet dropped. Queue size: {}", - state.ble_packet_queue.len() - ); - } - } - }); - } - }) + with_state(|state| state.ble.tick(|event| state.events.push(event.into()))) } fn radio_timer() { - with_state(|state| { - if !state.ble_timer.is_interrupt_pending() { - return; - } - state.ble_timer.clear_interrupt(); - - let cmd = state.ble_scanner.timer_update(state.ble_timer.now()); - state.ble_radio.configure_receiver(cmd.radio); - state.ble_timer.configure_interrupt(cmd.next_update); - }); + with_state(|state| state.ble.tick_timer()) } fn timer(timer: usize) { diff --git a/crates/runner-nordic/src/tasks.rs b/crates/runner-nordic/src/tasks.rs index 6ae59bd86..ea0f29e76 100644 --- a/crates/runner-nordic/src/tasks.rs +++ b/crates/runner-nordic/src/tasks.rs @@ -23,7 +23,7 @@ mod crypto; mod debug; pub mod led; pub mod platform; -mod radio; +pub mod radio; mod rng; pub mod uart; pub mod usb; diff --git a/crates/runner-nordic/src/tasks/radio.rs b/crates/runner-nordic/src/tasks/radio.rs index 63e7d3d8f..fde65598d 100644 --- a/crates/runner-nordic/src/tasks/radio.rs +++ b/crates/runner-nordic/src/tasks/radio.rs @@ -14,7 +14,7 @@ use wasefire_board_api::radio::Api; -mod ble; +pub mod ble; pub enum Impl {} diff --git a/crates/runner-nordic/src/tasks/radio/ble.rs b/crates/runner-nordic/src/tasks/radio/ble.rs index c9320ff48..54d6e9ce9 100644 --- a/crates/runner-nordic/src/tasks/radio/ble.rs +++ b/crates/runner-nordic/src/tasks/radio/ble.rs @@ -12,8 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -use rubble::link::{NextUpdate, RadioCmd}; +use alloc::collections::VecDeque; +use core::cell::RefCell; + +use critical_section::Mutex; +use nrf52840_hal::pac::TIMER0; +use rubble::beacon::{BeaconScanner, ScanCallback}; +use rubble::bytes::{ByteWriter, ToBytes}; +use rubble::link::ad_structure::AdStructure; +use rubble::link::filter::AllowAll; +use rubble::link::{DeviceAddress, Metadata, NextUpdate, RadioCmd, MIN_PDU_BUF}; use rubble::time::{Duration, Timer}; +use rubble_nrf5x::radio::BleRadio; +use rubble_nrf5x::timer::BleTimer; use wasefire_applet_api::radio::ble::Advertisement; use wasefire_board_api::radio::ble::{Api, Event}; use wasefire_board_api::{Error, Supported}; @@ -23,14 +34,16 @@ use crate::with_state; pub enum Impl {} +impl Supported for Impl {} + impl Api for Impl { fn enable(event: &Event) -> Result<(), Error> { match event { Event::Advertisement => with_state(|state| { - let scanner_cmd = - state.ble_scanner.configure(state.ble_timer.now(), Duration::millis(500)); - state.ble_radio.configure_receiver(scanner_cmd.radio); - state.ble_timer.configure_interrupt(scanner_cmd.next_update); + let ble = &mut state.ble; + let scanner_cmd = ble.scanner.configure(ble.timer.now(), Duration::millis(500)); + ble.radio.configure_receiver(scanner_cmd.radio); + ble.timer.configure_interrupt(scanner_cmd.next_update); Ok(()) }), } @@ -39,8 +52,9 @@ impl Api for Impl { fn disable(event: &Event) -> Result<(), Error> { match event { Event::Advertisement => with_state(|state| { - state.ble_timer.configure_interrupt(NextUpdate::Disable); - state.ble_radio.configure_receiver(RadioCmd::Off); + let ble = &mut state.ble; + ble.timer.configure_interrupt(NextUpdate::Disable); + ble.radio.configure_receiver(RadioCmd::Off); Ok(()) }), } @@ -48,7 +62,8 @@ impl Api for Impl { fn read_advertisement(output: &mut Advertisement) -> Result { with_state(|state| { - let input = match state.ble_packet_queue.pop_front() { + let ble = &mut state.ble; + let input = match ble.packet_queue.pop_front() { Some(x) => x, None => return Ok(false), }; @@ -65,4 +80,101 @@ impl Api for Impl { } } -impl Supported for Impl {} +pub struct Ble { + radio: BleRadio, + scanner: BeaconScanner, + timer: BleTimer, + packet_queue: VecDeque, +} + +impl Ble { + pub fn new(radio: BleRadio, timer: TIMER0) -> Self { + let timer = BleTimer::init(timer); + let scanner = BeaconScanner::new(BleAdvScanCallback); + let packet_queue = VecDeque::::new(); + Ble { radio, scanner, timer, packet_queue } + } + + pub fn tick(&mut self, mut push: impl FnMut(Event)) { + let next_update = + match self.radio.recv_beacon_interrupt(self.timer.now(), &mut self.scanner) { + Some(x) => x, + None => return, + }; + self.timer.configure_interrupt(next_update); + critical_section::with(|cs| { + if let Some(packet) = BLE_PACKET.take(cs) { + if self.packet_queue.len() < 50 { + self.packet_queue.push_back(packet); + push(Event::Advertisement); + log::debug!("BLE queue size: {}", self.packet_queue.len()); + } else { + log::warn!("BLE Packet dropped. Queue size: {}", self.packet_queue.len()); + } + } + }); + } + + pub fn tick_timer(&mut self) { + if !self.timer.is_interrupt_pending() { + return; + } + self.timer.clear_interrupt(); + let cmd = self.scanner.timer_update(self.timer.now()); + self.radio.configure_receiver(cmd.radio); + self.timer.configure_interrupt(cmd.next_update); + } +} + +static BLE_PACKET: Mutex>> = Mutex::new(RefCell::new(None)); + +struct RadioMetadata { + ticks: u32, + freq: u16, + rssi: i8, + pdu_type: u8, +} + +impl From for RadioMetadata { + fn from(value: Metadata) -> Self { + RadioMetadata { + ticks: value.timestamp.unwrap().ticks(), + freq: match value.channel { + ch @ 0 ..= 10 => 2404 + 2 * (ch as u16), + ch @ 11 ..= 36 => 2404 + 2 * (ch as u16 + 1), + 37 => 2402, + 38 => 2426, + 39 => 2480, + _ => 0, + }, + rssi: value.rssi.unwrap(), + pdu_type: u8::from(value.pdu_type.unwrap()), + } + } +} + +struct BlePacket { + addr: [u8; 6], + metadata: RadioMetadata, + data: alloc::vec::Vec, +} + +struct BleAdvScanCallback; + +impl ScanCallback for BleAdvScanCallback { + fn beacon<'a, I>(&mut self, addr: DeviceAddress, data: I, metadata: Metadata) + where I: Iterator> { + let mut buf: [u8; MIN_PDU_BUF] = [0; MIN_PDU_BUF]; + let mut writer = ByteWriter::new(&mut buf); + for p in data { + assert!(p.to_bytes(&mut writer).is_ok()); + } + let len = MIN_PDU_BUF - writer.space_left(); + let packet = BlePacket { + addr: *addr.raw(), + metadata: metadata.clone().into(), + data: buf[.. len].to_vec(), + }; + assert!(critical_section::with(|cs| BLE_PACKET.replace(cs, Some(packet))).is_none()); + } +} From 40872121ddb0801e9ee5c0a79e0c68fc30b114f7 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Fri, 15 Dec 2023 13:37:49 +0100 Subject: [PATCH 14/16] Update changelogs --- crates/board/CHANGELOG.md | 2 +- crates/prelude/CHANGELOG.md | 2 +- crates/scheduler/CHANGELOG.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/board/CHANGELOG.md b/crates/board/CHANGELOG.md index 78115a22e..58574a49a 100644 --- a/crates/board/CHANGELOG.md +++ b/crates/board/CHANGELOG.md @@ -95,4 +95,4 @@ ## 0.1.0 - + diff --git a/crates/prelude/CHANGELOG.md b/crates/prelude/CHANGELOG.md index fbb3bbeea..e96f4ec0d 100644 --- a/crates/prelude/CHANGELOG.md +++ b/crates/prelude/CHANGELOG.md @@ -4,7 +4,7 @@ ### Major -- Add `radio` module +- Add `radio::ble` module - Rename `native` to `test` and use `native` for native applets ### Minor diff --git a/crates/scheduler/CHANGELOG.md b/crates/scheduler/CHANGELOG.md index 5e9cb26ec..6a24cf40c 100644 --- a/crates/scheduler/CHANGELOG.md +++ b/crates/scheduler/CHANGELOG.md @@ -4,11 +4,11 @@ ### Major -- Add `radio` module - Add `native` and `wasm` features (exactly one must be chosen) ### Minor +- Support `radio::ble` module - Support configuring the memory size with `WASEFIRE_MEMORY_PAGE_COUNT` - Support `platform::update::is_supported()` - Support `platform::update` From 07834fd902e93466e799754db0a6e2e93a85213b Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Fri, 15 Dec 2023 13:47:23 +0100 Subject: [PATCH 15/16] Fixes --- crates/api-desc/CHANGELOG.md | 1 + crates/api-desc/src/radio/ble.rs | 2 +- crates/board/CHANGELOG.md | 2 +- crates/board/src/radio/ble.rs | 4 ++-- crates/prelude/CHANGELOG.md | 1 + crates/prelude/src/radio.rs | 5 +---- crates/prelude/src/usb.rs | 5 +---- crates/runner-nordic/src/tasks/radio/ble.rs | 25 +++++++++------------ crates/sync/CHANGELOG.md | 2 +- crates/sync/src/take.rs | 12 +++++++++- 10 files changed, 31 insertions(+), 28 deletions(-) diff --git a/crates/api-desc/CHANGELOG.md b/crates/api-desc/CHANGELOG.md index c367b7b2d..371336995 100644 --- a/crates/api-desc/CHANGELOG.md +++ b/crates/api-desc/CHANGELOG.md @@ -5,6 +5,7 @@ ### Minor - Add `radio::ble` module +- Support array types and all `{i,u}{8,16,32,64}` integers - Add `platform::update::is_supported()` - Add `platform::update` module - Add `platform` module with `platform::reboot()` function diff --git a/crates/api-desc/src/radio/ble.rs b/crates/api-desc/src/radio/ble.rs index 8c987cbc9..1507e5f78 100644 --- a/crates/api-desc/src/radio/ble.rs +++ b/crates/api-desc/src/radio/ble.rs @@ -68,7 +68,7 @@ pub(crate) fn new() -> Item { }, item! { /// Unregister handlers for radio events. - fn unregister "rd" { + fn unregister "rld" { /// Radio [`super::Event`] to stop listening to. event: u32, } -> {} diff --git a/crates/board/CHANGELOG.md b/crates/board/CHANGELOG.md index 58574a49a..b8c324386 100644 --- a/crates/board/CHANGELOG.md +++ b/crates/board/CHANGELOG.md @@ -95,4 +95,4 @@ ## 0.1.0 - + diff --git a/crates/board/src/radio/ble.rs b/crates/board/src/radio/ble.rs index e9ede897e..50f90a9c4 100644 --- a/crates/board/src/radio/ble.rs +++ b/crates/board/src/radio/ble.rs @@ -33,10 +33,10 @@ impl From for crate::Event { /// BLE interface. pub trait Api: Support { - /// Enables radio events. + /// Enables BLE events. fn enable(event: &Event) -> Result<(), Error>; - /// Disables radio events. + /// Disables BLE events. fn disable(event: &Event) -> Result<(), Error>; /// Reads the next advertisement packet, if any. diff --git a/crates/prelude/CHANGELOG.md b/crates/prelude/CHANGELOG.md index e96f4ec0d..61b760994 100644 --- a/crates/prelude/CHANGELOG.md +++ b/crates/prelude/CHANGELOG.md @@ -18,6 +18,7 @@ ### Patch +- Use `ENUM::to_result()` to convert errors - Only depend on `portable-atomic` through `wasefire-sync` - Use `wasefire-sync::executed!()` to ensure the allocator is initialized at most once diff --git a/crates/prelude/src/radio.rs b/crates/prelude/src/radio.rs index 42cd09377..b38caa331 100644 --- a/crates/prelude/src/radio.rs +++ b/crates/prelude/src/radio.rs @@ -20,8 +20,5 @@ pub use wasefire_applet_api::radio::Error; pub mod ble; fn convert(len: isize) -> Result { - if len < 0 { - return Err(Error::Unknown); - } - Ok(len as usize) + Error::to_result(len) } diff --git a/crates/prelude/src/usb.rs b/crates/prelude/src/usb.rs index 869d16d3b..cb90c0317 100644 --- a/crates/prelude/src/usb.rs +++ b/crates/prelude/src/usb.rs @@ -23,8 +23,5 @@ pub use wasefire_applet_api::usb::Error; pub mod serial; fn convert(len: isize) -> Result { - if len < 0 { - return Err(Error::Unknown); - } - Ok(len as usize) + Error::to_result(len) } diff --git a/crates/runner-nordic/src/tasks/radio/ble.rs b/crates/runner-nordic/src/tasks/radio/ble.rs index 54d6e9ce9..ec8208741 100644 --- a/crates/runner-nordic/src/tasks/radio/ble.rs +++ b/crates/runner-nordic/src/tasks/radio/ble.rs @@ -13,9 +13,7 @@ // limitations under the License. use alloc::collections::VecDeque; -use core::cell::RefCell; -use critical_section::Mutex; use nrf52840_hal::pac::TIMER0; use rubble::beacon::{BeaconScanner, ScanCallback}; use rubble::bytes::{ByteWriter, ToBytes}; @@ -29,6 +27,7 @@ use wasefire_applet_api::radio::ble::Advertisement; use wasefire_board_api::radio::ble::{Api, Event}; use wasefire_board_api::{Error, Supported}; use wasefire_logger as log; +use wasefire_sync::TakeCell; use crate::with_state; @@ -102,17 +101,15 @@ impl Ble { None => return, }; self.timer.configure_interrupt(next_update); - critical_section::with(|cs| { - if let Some(packet) = BLE_PACKET.take(cs) { - if self.packet_queue.len() < 50 { - self.packet_queue.push_back(packet); - push(Event::Advertisement); - log::debug!("BLE queue size: {}", self.packet_queue.len()); - } else { - log::warn!("BLE Packet dropped. Queue size: {}", self.packet_queue.len()); - } + if let Some(packet) = BLE_PACKET.get() { + if self.packet_queue.len() < 50 { + self.packet_queue.push_back(packet); + push(Event::Advertisement); + log::debug!("BLE queue size: {}", self.packet_queue.len()); + } else { + log::warn!("BLE Packet dropped. Queue size: {}", self.packet_queue.len()); } - }); + } } pub fn tick_timer(&mut self) { @@ -126,7 +123,7 @@ impl Ble { } } -static BLE_PACKET: Mutex>> = Mutex::new(RefCell::new(None)); +static BLE_PACKET: TakeCell = TakeCell::new(None); struct RadioMetadata { ticks: u32, @@ -175,6 +172,6 @@ impl ScanCallback for BleAdvScanCallback { metadata: metadata.clone().into(), data: buf[.. len].to_vec(), }; - assert!(critical_section::with(|cs| BLE_PACKET.replace(cs, Some(packet))).is_none()); + BLE_PACKET.put(packet); } } diff --git a/crates/sync/CHANGELOG.md b/crates/sync/CHANGELOG.md index 6faacf764..0225836ff 100644 --- a/crates/sync/CHANGELOG.md +++ b/crates/sync/CHANGELOG.md @@ -2,4 +2,4 @@ ## 0.1.0-git - + diff --git a/crates/sync/src/take.rs b/crates/sync/src/take.rs index 5214ce521..d38c5e8a1 100644 --- a/crates/sync/src/take.rs +++ b/crates/sync/src/take.rs @@ -30,7 +30,17 @@ impl TakeCell { /// Panics if the mutex is locked or the option is empty. #[track_caller] pub fn take(&self) -> T { - self.0.lock().take().unwrap() + self.get().unwrap() + } + + /// Takes the (possibly empty) content of the option. + /// + /// # Panics + /// + /// Panics if the mutex is locked. + #[track_caller] + pub fn get(&self) -> Option { + self.0.lock().take() } /// Replaces the content of the option. From 9e827cd98f2446fb2c4bb34d122dbf9abd950ad7 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Fri, 15 Dec 2023 14:20:33 +0100 Subject: [PATCH 16/16] Fix assemblyscript API --- examples/assemblyscript/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/assemblyscript/api.ts b/examples/assemblyscript/api.ts index fcc8cc9b4..9e86e35ba 100644 --- a/examples/assemblyscript/api.ts +++ b/examples/assemblyscript/api.ts @@ -808,7 +808,7 @@ ): void // Unregister handlers for radio events. - @external("env", "rd") + @external("env", "rld") export declare function radio_ble_unregister( // Radio [`super::Event`] to stop listening to. event: u32,