diff --git a/src/bus.rs b/src/bus.rs index add346b2f..e26f11120 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,11 +1,10 @@ -use core::slice; - use embassy_futures::yield_now; use embassy_time::{Duration, Timer}; use embedded_hal_1::digital::OutputPin; use futures::FutureExt; use crate::consts::*; +use crate::slice8_mut; /// Custom Spi Trait that _only_ supports the bus operation of the cyw43 /// Implementors are expected to hold the CS pin low during an operation. @@ -327,8 +326,3 @@ fn swap16(x: u32) -> u32 { fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) } - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } -} diff --git a/src/consts.rs b/src/consts.rs index fee2d01ab..18502bd1a 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -109,6 +109,50 @@ pub(crate) const READ: bool = false; pub(crate) const INC_ADDR: bool = true; pub(crate) const FIXED_ADDR: bool = false; +#[allow(non_camel_case_types)] +#[derive(Copy, Clone)] +#[repr(u8)] +pub enum EStatus { + /// operation was successful + SUCCESS = 0, + /// operation failed + FAIL = 1, + /// operation timed out + TIMEOUT = 2, + /// failed due to no matching network found + NO_NETWORKS = 3, + /// operation was aborted + ABORT = 4, + /// protocol failure: packet not ack'd + NO_ACK = 5, + /// AUTH or ASSOC packet was unsolicited + UNSOLICITED = 6, + /// attempt to assoc to an auto auth configuration + ATTEMPT = 7, + /// scan results are incomplete + PARTIAL = 8, + /// scan aborted by another scan + NEWSCAN = 9, + /// scan aborted due to assoc in progress + NEWASSOC = 10, + /// 802.11h quiet period started + _11HQUIET = 11, + /// user disabled scanning (WLC_SET_SCANSUPPRESS) + SUPPRESS = 12, + /// no allowable channels to scan + NOCHANS = 13, + /// scan aborted due to CCX fast roam + CCXFASTRM = 14, + /// abort channel select + CS_ABORT = 15, +} + +impl PartialEq for u32 { + fn eq(&self, other: &EStatus) -> bool { + *self == *other as Self + } +} + #[allow(dead_code)] pub(crate) struct FormatStatus(pub u32); diff --git a/src/control.rs b/src/control.rs index 30d5d0924..0c06009b9 100644 --- a/src/control.rs +++ b/src/control.rs @@ -6,7 +6,7 @@ use embassy_time::{Duration, Timer}; pub use crate::bus::SpiBusCyw43; use crate::consts::*; -use crate::events::{Event, EventQueue}; +use crate::events::{Event, Events}; use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType}; use crate::structs::*; @@ -14,15 +14,15 @@ use crate::{countries, PowerManagementMode}; pub struct Control<'a> { state_ch: ch::StateRunner<'a>, - event_sub: &'a EventQueue, + events: &'a Events, ioctl_state: &'a IoctlState, } impl<'a> Control<'a> { - pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a EventQueue, ioctl_state: &'a IoctlState) -> Self { + pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { Self { state_ch, - event_sub, + events: event_sub, ioctl_state, } } @@ -195,24 +195,27 @@ impl<'a> Control<'a> { } async fn wait_for_join(&mut self, i: SsidInfo) { - let mut subscriber = self.event_sub.subscriber().unwrap(); + self.events.mask.enable(&[Event::JOIN, Event::AUTH]); + let mut subscriber = self.events.queue.subscriber().unwrap(); + // the actual join operation starts here + // we make sure to enable events before so we don't miss any self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) .await; // set_ssid loop { let msg = subscriber.next_message_pure().await; - if msg.event_type == Event::AUTH && msg.status != 0 { + if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { // retry - warn!("JOIN failed with status={}", msg.status); + warn!("JOIN failed with status={}", msg.header.status); self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) .await; - } else if msg.event_type == Event::JOIN && msg.status == 0 { + } else if msg.header.event_type == Event::JOIN && msg.header.status == EStatus::SUCCESS { // successful join break; } } - + self.events.mask.disable_all(); self.state_ch.set_link_state(LinkState::Up); info!("JOINED"); } diff --git a/src/events.rs b/src/events.rs index 87f6c01a3..d6f114ed9 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![allow(non_camel_case_types)] -use core::num; +use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; @@ -284,13 +284,114 @@ pub enum Event { LAST = 190, } -pub type EventQueue = PubSubChannel; -pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, EventStatus, 2, 1, 1>; -pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, EventStatus, 2, 1, 1>; +// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. +pub type EventQueue = PubSubChannel; +pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, Message, 2, 1, 1>; +pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; + +pub struct Events { + pub queue: EventQueue, + pub mask: SharedEventMask, +} + +impl Events { + pub fn new() -> Self { + Self { + queue: EventQueue::new(), + mask: SharedEventMask::default(), + } + } +} #[derive(Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EventStatus { +pub struct Status { pub event_type: Event, pub status: u32, } + +#[derive(Clone, Copy)] +pub enum Payload { + None, +} + +#[derive(Clone, Copy)] + +pub struct Message { + pub header: Status, + pub payload: Payload, +} + +impl Message { + pub fn new(status: Status, payload: Payload) -> Self { + Self { + header: status, + payload, + } + } +} + +#[derive(Default)] +struct EventMask { + mask: [u32; Self::WORD_COUNT], +} + +impl EventMask { + const WORD_COUNT: usize = ((Event::LAST as u32 + (u32::BITS - 1)) / u32::BITS) as usize; + + fn enable(&mut self, event: Event) { + let n = event as u32; + let word = n / u32::BITS; + let bit = n % u32::BITS; + + self.mask[word as usize] |= (1 << bit); + } + + fn disable(&mut self, event: Event) { + let n = event as u32; + let word = n / u32::BITS; + let bit = n % u32::BITS; + + self.mask[word as usize] &= !(1 << bit); + } + + fn is_enabled(&self, event: Event) -> bool { + let n = event as u32; + let word = n / u32::BITS; + let bit = n % u32::BITS; + + self.mask[word as usize] & (1 << bit) > 0 + } +} + +#[derive(Default)] + +pub struct SharedEventMask { + mask: RefCell, +} + +impl SharedEventMask { + pub fn enable(&self, events: &[Event]) { + let mut mask = self.mask.borrow_mut(); + for event in events { + mask.enable(*event); + } + } + + pub fn disable(&self, events: &[Event]) { + let mut mask = self.mask.borrow_mut(); + for event in events { + mask.disable(*event); + } + } + + pub fn disable_all(&self) { + let mut mask = self.mask.borrow_mut(); + mask.mask = Default::default(); + } + + pub fn is_enabled(&self, event: Event) -> bool { + let mask = self.mask.borrow(); + mask.is_enabled(event) + } +} diff --git a/src/ioctl.rs b/src/ioctl.rs index 0fee1ad19..66c6a10e5 100644 --- a/src/ioctl.rs +++ b/src/ioctl.rs @@ -4,6 +4,8 @@ use core::task::{Poll, Waker}; use embassy_sync::waitqueue::WakerRegistration; +use crate::fmt::Bytes; + #[derive(Clone, Copy)] pub enum IoctlType { Get = 0, @@ -108,6 +110,8 @@ impl IoctlState { pub fn ioctl_done(&self, response: &[u8]) { if let IoctlStateInner::Sent { buf } = self.state.get() { + info!("IOCTL Response: {:02x}", Bytes(response)); + // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); @@ -115,6 +119,8 @@ impl IoctlState { resp_len: response.len(), }); self.wake_control(); + } else { + warn!("IOCTL Response but no pending Ioctl"); } } } diff --git a/src/lib.rs b/src/lib.rs index 069ca40f4..f9244bddb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,9 +18,11 @@ mod control; mod nvram; mod runner; +use core::slice; + use embassy_net_driver_channel as ch; use embedded_hal_1::digital::OutputPin; -use events::EventQueue; +use events::Events; use ioctl::IoctlState; use crate::bus::Bus; @@ -103,7 +105,7 @@ const CHIP: Chip = Chip { pub struct State { ioctl_state: IoctlState, ch: ch::State, - events: EventQueue, + events: Events, } impl State { @@ -111,7 +113,7 @@ impl State { Self { ioctl_state: IoctlState::new(), ch: ch::State::new(), - events: EventQueue::new(), + events: Events::new(), } } } @@ -225,3 +227,8 @@ where runner, ) } + +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +} diff --git a/src/runner.rs b/src/runner.rs index f0f6fceeb..806ddfc49 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,5 +1,3 @@ -use core::slice; - use embassy_futures::select::{select3, Either3}; use embassy_net_driver_channel as ch; use embassy_sync::pubsub::PubSubBehavior; @@ -9,12 +7,12 @@ use embedded_hal_1::digital::OutputPin; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; use crate::consts::*; -use crate::events::{EventQueue, EventStatus}; +use crate::events::{Events, Status}; use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; use crate::nvram::NVRAM; use crate::structs::*; -use crate::{events, Core, CHIP, MTU}; +use crate::{events, slice8_mut, Core, CHIP, MTU}; #[cfg(feature = "firmware-logs")] struct LogState { @@ -45,7 +43,7 @@ pub struct Runner<'a, PWR, SPI> { sdpcm_seq: u8, sdpcm_seq_max: u8, - events: &'a EventQueue, + events: &'a Events, #[cfg(feature = "firmware-logs")] log: LogState, @@ -60,7 +58,7 @@ where ch: ch::Runner<'a, MTU>, bus: Bus, ioctl_state: &'a IoctlState, - events: &'a EventQueue, + events: &'a Events, ) -> Self { Self { ch, @@ -353,8 +351,6 @@ where panic!("IOCTL error {}", cdc_header.status as i32); } - info!("IOCTL Response: {:02x}", Bytes(response)); - self.ioctl_state.ioctl_done(response); } } @@ -406,11 +402,21 @@ where Bytes(evt_data) ); - if evt_type == events::Event::AUTH || evt_type == events::Event::JOIN { - self.events.publish_immediate(EventStatus { - status: event_packet.msg.status, - event_type: evt_type, - }); + if self.events.mask.is_enabled(evt_type) { + let status = event_packet.msg.status; + let event_payload = events::Payload::None; + + // this intentionally uses the non-blocking publish immediate + // publish() is a deadlock risk in the current design as awaiting here prevents ioctls + // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event + // (if they are actively awaiting the queue) + self.events.queue.publish_immediate(events::Message::new( + Status { + event_type: evt_type, + status, + }, + event_payload, + )); } } CHANNEL_TYPE_DATA => { @@ -548,8 +554,3 @@ where true } } - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } -}