diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index c67614dd..6e45e595 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -1,16 +1,4 @@ -use core::cmp::{max, min}; - -use ch::driver::LinkState; -use embassy_net_driver_channel as ch; -use embassy_time::{Duration, Timer}; - -pub use crate::bus::SpiBusCyw43; -use crate::consts::*; -use crate::events::{Event, EventSubscriber, Events}; -use crate::fmt::Bytes; -use crate::ioctl::{IoctlState, IoctlType}; -use crate::structs::*; -use crate::{countries, events, PowerManagementMode}; +use crate::mac::runner::Runner; #[derive(Debug)] pub struct Error { @@ -18,437 +6,15 @@ pub struct Error { } pub struct Control<'a> { - state_ch: ch::StateRunner<'a>, - events: &'a Events, - ioctl_state: &'a IoctlState, + runner: &'a Runner, } impl<'a> Control<'a> { - pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { - Self { - state_ch, - events: event_sub, - ioctl_state, - } - } - - pub async fn init(&mut self, clm: &[u8]) { - const CHUNK_SIZE: usize = 1024; - - debug!("Downloading CLM..."); - - let mut offs = 0; - for chunk in clm.chunks(CHUNK_SIZE) { - let mut flag = DOWNLOAD_FLAG_HANDLER_VER; - if offs == 0 { - flag |= DOWNLOAD_FLAG_BEGIN; - } - offs += chunk.len(); - if offs == clm.len() { - flag |= DOWNLOAD_FLAG_END; - } - - let header = DownloadHeader { - flag, - dload_type: DOWNLOAD_TYPE_CLM, - len: chunk.len() as _, - crc: 0, - }; - let mut buf = [0; 8 + 12 + CHUNK_SIZE]; - buf[0..8].copy_from_slice(b"clmload\x00"); - buf[8..20].copy_from_slice(&header.to_bytes()); - buf[20..][..chunk.len()].copy_from_slice(&chunk); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) - .await; + pub(crate) fn new(runner: &'a Runner) -> Self { + Self { runner: runner } } - // check clmload ok - assert_eq!(self.get_iovar_u32("clmload_status").await, 0); - - debug!("Configuring misc stuff..."); - - // Disable tx gloming which transfers multiple packets in one request. - // 'glom' is short for "conglomerate" which means "gather together into - // a compact mass". - self.set_iovar_u32("bus:txglom", 0).await; - self.set_iovar_u32("apsta", 1).await; - - // read MAC addr. - let mut mac_addr = [0; 6]; - assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); - debug!("mac addr: {:02x}", Bytes(&mac_addr)); - - let country = countries::WORLD_WIDE_XX; - let country_info = CountryInfo { - country_abbrev: [country.code[0], country.code[1], 0, 0], - country_code: [country.code[0], country.code[1], 0, 0], - rev: if country.rev == 0 { -1 } else { country.rev as _ }, - }; - self.set_iovar("country", &country_info.to_bytes()).await; - - // set country takes some time, next ioctls fail if we don't wait. - Timer::after(Duration::from_millis(100)).await; - - // Set antenna to chip antenna - self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; - - self.set_iovar_u32("bus:txglom", 0).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? - //Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_mpdu", 4).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes - - //Timer::after(Duration::from_millis(100)).await; - - // evts - let mut evts = EventMask { - iface: 0, - events: [0xFF; 24], - }; - - // Disable spammy uninteresting events. - evts.unset(Event::RADIO); - evts.unset(Event::IF); - evts.unset(Event::PROBREQ_MSG); - evts.unset(Event::PROBREQ_MSG_RX); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::ROAM); - - self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; - - Timer::after(Duration::from_millis(100)).await; - - // set wifi up - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; - - Timer::after(Duration::from_millis(100)).await; - - self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto - self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any - - Timer::after(Duration::from_millis(100)).await; - - self.state_ch.set_ethernet_address(mac_addr); - - debug!("INIT DONE"); - } - - pub async fn set_power_management(&mut self, mode: PowerManagementMode) { - // power save mode - let mode_num = mode.mode(); - if mode_num == 2 { - self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; - self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; - self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; - self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; - } - self.ioctl_set_u32(86, 0, mode_num).await; - } - - pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - - self.ioctl_set_u32(134, 0, 0).await; // wsec = open - self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; - self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 - self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) - - let mut i = SsidInfo { - len: ssid.len() as _, - ssid: [0; 32], - }; - i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - - self.wait_for_join(i).await - } - - pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> { - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - - self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 - self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; - self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; - self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; - - Timer::after(Duration::from_millis(100)).await; - - let mut pfi = PassphraseInfo { - len: passphrase.len() as _, - flags: 1, - passphrase: [0; 64], - }; - pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) - .await; // WLC_SET_WSEC_PMK - - self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 - self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) - self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth - - let mut i = SsidInfo { - len: ssid.len() as _, - ssid: [0; 32], - }; - i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - - self.wait_for_join(i).await - } - - async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { - self.events.mask.enable(&[Event::SET_SSID, 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 - - // set_ssid - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) - .await; - - // to complete the join, we wait for a SET_SSID event - // we also save the AUTH status for the user, it may be interesting - let mut auth_status = 0; - let status = loop { - let msg = subscriber.next_message_pure().await; - if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { - auth_status = msg.header.status; - } else if msg.header.event_type == Event::SET_SSID { - // join operation ends with SET_SSID event - break msg.header.status; - } - }; - - self.events.mask.disable_all(); - if status == EStatus::SUCCESS { - // successful join - self.state_ch.set_link_state(LinkState::Up); - debug!("JOINED"); - Ok(()) - } else { - warn!("JOIN failed with status={} auth={}", status, auth_status); - Err(Error { status }) - } - } - - pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { - assert!(gpio_n < 3); - self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) - .await - } - - pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) { - self.start_ap(ssid, "", Security::OPEN, channel).await; - } - - pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) { - self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await; - } - - async fn start_ap(&mut self, ssid: &str, passphrase: &str, security: Security, channel: u8) { - if security != Security::OPEN - && (passphrase.as_bytes().len() < MIN_PSK_LEN || passphrase.as_bytes().len() > MAX_PSK_LEN) - { - panic!("Passphrase is too short or too long"); - } - - // Temporarily set wifi down - self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; - - // Turn off APSTA mode - self.set_iovar_u32("apsta", 0).await; - - // Set wifi up again - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; - - // Turn on AP mode - self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; - - // Set SSID - let mut i = SsidInfoWithIndex { - index: 0, - ssid_info: SsidInfo { - len: ssid.as_bytes().len() as _, - ssid: [0; 32], - }, - }; - i.ssid_info.ssid[..ssid.as_bytes().len()].copy_from_slice(ssid.as_bytes()); - self.set_iovar("bsscfg:ssid", &i.to_bytes()).await; - - // Set channel number - self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await; - - // Set security - self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await; - - if security != Security::OPEN { - self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK - - Timer::after(Duration::from_millis(100)).await; - - // Set passphrase - let mut pfi = PassphraseInfo { - len: passphrase.as_bytes().len() as _, - flags: 1, // WSEC_PASSPHRASE - passphrase: [0; 64], - }; - pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) - .await; - } - - // Change mutlicast rate from 1 Mbps to 11 Mbps - self.set_iovar_u32("2g_mrate", 11000000 / 500000).await; - - // Start AP - self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP - } - - async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { - let mut buf = [0; 8]; - buf[0..4].copy_from_slice(&val1.to_le_bytes()); - buf[4..8].copy_from_slice(&val2.to_le_bytes()); - self.set_iovar(name, &buf).await - } - - async fn set_iovar_u32(&mut self, name: &str, val: u32) { - self.set_iovar(name, &val.to_le_bytes()).await - } - - async fn get_iovar_u32(&mut self, name: &str) -> u32 { - let mut buf = [0; 4]; - let len = self.get_iovar(name, &mut buf).await; - assert_eq!(len, 4); - u32::from_le_bytes(buf) - } - - async fn set_iovar(&mut self, name: &str, val: &[u8]) { - self.set_iovar_v::<64>(name, val).await - } - - async fn set_iovar_v(&mut self, name: &str, val: &[u8]) { - debug!("set {} = {:02x}", name, Bytes(val)); - - let mut buf = [0; BUFSIZE]; - buf[..name.len()].copy_from_slice(name.as_bytes()); - buf[name.len()] = 0; - buf[name.len() + 1..][..val.len()].copy_from_slice(val); - - let total_len = name.len() + 1 + val.len(); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) - .await; - } - - // TODO this is not really working, it always returns all zeros. - async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { - debug!("get {}", name); - - let mut buf = [0; 64]; - buf[..name.len()].copy_from_slice(name.as_bytes()); - buf[name.len()] = 0; - - let total_len = max(name.len() + 1, res.len()); - let res_len = self - .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) - .await; - - let out_len = min(res.len(), res_len); - res[..out_len].copy_from_slice(&buf[..out_len]); - out_len - } - - async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { - let mut buf = val.to_le_bytes(); - self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; - } - - async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - struct CancelOnDrop<'a>(&'a IoctlState); - - impl CancelOnDrop<'_> { - fn defuse(self) { - core::mem::forget(self); - } - } - - impl Drop for CancelOnDrop<'_> { - fn drop(&mut self) { - self.0.cancel_ioctl(); - } - } - - let ioctl = CancelOnDrop(self.ioctl_state); - let resp_len = ioctl.0.do_ioctl(kind, cmd, iface, buf).await; - ioctl.defuse(); - - resp_len - } - - /// Start a wifi scan - /// - /// Returns a `Stream` of networks found by the device - /// - /// # Note - /// Device events are currently implemented using a bounded queue. - /// To not miss any events, you should make sure to always await the stream. - pub async fn scan(&mut self) -> Scanner<'_> { - const SCANTYPE_PASSIVE: u8 = 1; - - let scan_params = ScanParams { - version: 1, - action: 1, - sync_id: 1, - ssid_len: 0, - ssid: [0; 32], - bssid: [0xff; 6], - bss_type: 2, - scan_type: SCANTYPE_PASSIVE, - nprobes: !0, - active_time: !0, - passive_time: !0, - home_time: !0, - channel_num: 0, - channel_list: [0; 1], - }; - - self.events.mask.enable(&[Event::ESCAN_RESULT]); - let subscriber = self.events.queue.subscriber().unwrap(); - self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await; - - Scanner { - subscriber, - events: &self.events, - } - } -} - -pub struct Scanner<'a> { - subscriber: EventSubscriber<'a>, - events: &'a Events, -} - -impl Scanner<'_> { - /// wait for the next found network - pub async fn next(&mut self) -> Option { - let event = self.subscriber.next_message_pure().await; - if event.header.status != EStatus::PARTIAL { - self.events.mask.disable_all(); - return None; - } - - if let events::Payload::BssInfo(bss) = event.payload { - Some(bss) - } else { - None - } - } -} - -impl Drop for Scanner<'_> { - fn drop(&mut self) { - self.events.mask.disable_all(); + pub async fn init(&mut self) { + // TODO } } diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 3171d61f..00072749 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -1,102 +1,110 @@ -#![no_std] -#![no_main] #![allow(incomplete_features)] -#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)] #![deny(unused_must_use)] -use core::slice; +use core::task::Context; -use embassy_net_driver_channel as ch; -use embedded_hal_1::digital::OutputPin; -use events::Events; -use ioctl::IoctlState; +use embassy_net_driver::{Capabilities, LinkState, Medium}; -use crate::bus::Bus; -pub use crate::bus::SpiBusCyw43; -pub use crate::control::{Control, Error as ControlError}; -pub use crate::runner::Runner; -pub use crate::structs::BssInfo; +use crate::mac::runner::Runner; +use crate::mac::MTU; -const MTU: usize = 1514; - -pub struct State { - ioctl_state: IoctlState, - ch: ch::State, - events: Events, +pub struct Driver<'d> { + runner: &'d Runner, } -impl State { - pub fn new() -> Self { - Self { - ioctl_state: IoctlState::new(), - ch: ch::State::new(), - events: Events::new(), - } +impl<'d> Driver<'d> { + pub(crate) fn new(runner: &'d Runner) -> Self { + Self { runner: runner } } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PowerManagementMode { - /// Custom, officially unsupported mode. Use at your own risk. - /// All power-saving features set to their max at only a marginal decrease in power consumption - /// as oppposed to `Aggressive`. - SuperSave, +impl<'d> embassy_net_driver::Driver for Driver<'d> { + // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; + type RxToken<'a> = RxToken where Self: 'a; + type TxToken<'a> = TxToken where Self: 'a; - /// Aggressive power saving mode. - Aggressive, + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + // WAKER.register(cx.waker()); + // if self.rx.available().is_some() && self.tx.available().is_some() { + // Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + // } else { + // None + // } - /// The default mode. - PowerSave, + None + } - /// Performance is prefered over power consumption but still some power is conserved as opposed to - /// `None`. - Performance, + fn transmit(&mut self, cx: &mut Context) -> Option> { + // WAKER.register(cx.waker()); + // / if self.tx.available().is_some() { + // / Some(TxToken { tx: &mut self.tx }) + // / } else { + // / None + // / } - /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of - /// a much lower throughput. - ThroughputThrottling, + None + } - /// No power management is configured. This consumes the most power. - None, -} + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + // caps.max_burst_size = Some(self.tx.len()); -impl Default for PowerManagementMode { - fn default() -> Self { - Self::PowerSave + caps.medium = Medium::Ieee802154; + caps + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // if self.phy.poll_link(&mut self.station_management, cx) { + // LinkState::Up + // } else { + // LinkState::Down + // } + + LinkState::Down + } + + fn ethernet_address(&self) -> [u8; 6] { + // self.mac_addr + + [0; 6] } } -impl PowerManagementMode { - // TODO +pub struct RxToken { + // rx: &'a mut RDesRing<'d>, } -pub type NetDriver<'a> = ch::Device<'a, MTU>; +impl embassy_net_driver::RxToken for RxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + // let pkt = unwrap!(self.rx.available()); -pub async fn new<'a, PWR, SPI>( - state: &'a mut State, - pwr: PWR, - spi: SPI, - firmware: &[u8], -) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) -where - PWR: OutputPin, - SPI: SpiBusCyw43, -{ - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); - let state_ch = ch_runner.state_runner(); - - let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); - - runner.init(firmware).await; - - ( - device, - Control::new(state_ch, &state.events, &state.ioctl_state), - runner, - ) + let pkt = &[]; + let r = f(&mut pkt[0..]); + // self.rx.pop_packet(); + r + } } -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) } +pub struct TxToken { + // tx: &'a mut TDesRing<'d>, +} + +impl embassy_net_driver::TxToken for TxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + // let pkt = unwrap!(self.tx.available()); + let pkt = &[]; + let r = f(&mut pkt[..len]); + // self.tx.transmit(len); + r + } } diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 661c06ac..a2bb7922 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,9 +1,3 @@ -use core::cell::RefCell; - -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::pubsub::{PubSubChannel, Subscriber}; - -use super::helpers::to_u16; use core::mem; use super::indications::{ @@ -80,7 +74,6 @@ impl Event { } } -#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MacEvent<'a> { MlmeAssociateCnf(&'a AssociateConfirm), @@ -109,116 +102,3 @@ pub enum MacEvent<'a> { McpsDataInd(&'a DataIndication), MlmePollInd(&'a PollIndication), } - -// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. -pub type EventQueue = PubSubChannel; -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)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Status { - pub event_type: MacEvent, - pub status: u32, -} - -#[derive(Clone, Copy)] -pub enum Payload { - None, - // BssInfo(BssInfo), -} - -#[derive(Clone)] - -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: MacEvent) { - 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: MacEvent) { - 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: MacEvent) -> 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: &[MacEvent]) { - let mut mask = self.mask.borrow_mut(); - for event in events { - mask.enable(*event); - } - } - - #[allow(dead_code)] - pub fn disable(&self, events: &[MacEvent]) { - 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: MacEvent) -> bool { - let mask = self.mask.borrow(); - mask.is_enabled(event) - } -} diff --git a/embassy-stm32-wpan/src/mac/ioctl.rs b/embassy-stm32-wpan/src/mac/ioctl.rs deleted file mode 100644 index 0fe55cd6..00000000 --- a/embassy-stm32-wpan/src/mac/ioctl.rs +++ /dev/null @@ -1,124 +0,0 @@ -use core::cell::{Cell, RefCell}; -use core::future::poll_fn; -use core::task::{Poll, Waker}; - -use embassy_sync::waitqueue::WakerRegistration; - -#[derive(Clone, Copy)] -pub enum IoctlType { - Get = 0, - Set = 2, -} - -#[derive(Clone, Copy)] -pub struct PendingIoctl { - pub buf: *mut [u8], - pub kind: IoctlType, - pub cmd: u32, - pub iface: u32, -} - -#[derive(Clone, Copy)] -enum IoctlStateInner { - Pending(PendingIoctl), - Sent { buf: *mut [u8] }, - Done { resp_len: usize }, -} - -struct Wakers { - control: WakerRegistration, - runner: WakerRegistration, -} - -impl Default for Wakers { - fn default() -> Self { - Self { - control: WakerRegistration::new(), - runner: WakerRegistration::new(), - } - } -} - -pub struct IoctlState { - state: Cell, - wakers: RefCell, -} - -impl IoctlState { - pub fn new() -> Self { - Self { - state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), - wakers: Default::default(), - } - } - - fn wake_control(&self) { - self.wakers.borrow_mut().control.wake(); - } - - fn register_control(&self, waker: &Waker) { - self.wakers.borrow_mut().control.register(waker); - } - - fn wake_runner(&self) { - self.wakers.borrow_mut().runner.wake(); - } - - fn register_runner(&self, waker: &Waker) { - self.wakers.borrow_mut().runner.register(waker); - } - - pub async fn wait_complete(&self) -> usize { - poll_fn(|cx| { - if let IoctlStateInner::Done { resp_len } = self.state.get() { - Poll::Ready(resp_len) - } else { - self.register_control(cx.waker()); - Poll::Pending - } - }) - .await - } - - pub async fn wait_pending(&self) -> PendingIoctl { - let pending = poll_fn(|cx| { - if let IoctlStateInner::Pending(pending) = self.state.get() { - Poll::Ready(pending) - } else { - self.register_runner(cx.waker()); - Poll::Pending - } - }) - .await; - - self.state.set(IoctlStateInner::Sent { buf: pending.buf }); - pending - } - - pub fn cancel_ioctl(&self) { - self.state.set(IoctlStateInner::Done { resp_len: 0 }); - } - - pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - self.state - .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); - self.wake_runner(); - self.wait_complete().await - } - - pub fn ioctl_done(&self, response: &[u8]) { - if let IoctlStateInner::Sent { buf } = self.state.get() { - // trace!("IOCTL Response: {:02x}", Bytes(response)); - - // TODO fix this - (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); - - self.state.set(IoctlStateInner::Done { - resp_len: response.len(), - }); - self.wake_control(); - } else { - warn!("IOCTL Response but no pending Ioctl"); - } - } -} diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index df03e423..e024aeae 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -1,9 +1,9 @@ pub mod commands; mod consts; pub mod control; +mod driver; pub mod event; pub mod indications; -mod ioctl; mod macros; mod opcodes; pub mod responses; @@ -12,86 +12,19 @@ pub mod typedefs; use core::slice; -use embassy_net_driver_channel as ch; - pub use crate::mac::control::{Control, Error as ControlError}; -use crate::mac::event::Events; -use crate::mac::ioctl::IoctlState; +use crate::mac::driver::Driver; pub use crate::mac::runner::Runner; use crate::sub::mac::Mac; -const MTU: usize = 1514; +const MTU: usize = 127; -pub struct State { - ioctl_state: IoctlState, - ch: ch::State, - events: Events, -} +pub async fn new<'a>(mac: Mac) -> (Runner, Control<'a>, Driver<'a>) { + let runner = Runner::new(mac); + let control = Control::new(&runner); + let driver = Driver::new(&runner); -impl State { - pub fn new() -> Self { - Self { - ioctl_state: IoctlState::new(), - ch: ch::State::new(), - events: Events::new(), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PowerManagementMode { - /// Custom, officially unsupported mode. Use at your own risk. - /// All power-saving features set to their max at only a marginal decrease in power consumption - /// as oppposed to `Aggressive`. - SuperSave, - - /// Aggressive power saving mode. - Aggressive, - - /// The default mode. - PowerSave, - - /// Performance is prefered over power consumption but still some power is conserved as opposed to - /// `None`. - Performance, - - /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of - /// a much lower throughput. - ThroughputThrottling, - - /// No power management is configured. This consumes the most power. - None, -} - -impl Default for PowerManagementMode { - fn default() -> Self { - Self::PowerSave - } -} - -impl PowerManagementMode { - // TODO -} - -pub type NetDriver<'a> = ch::Device<'a, MTU>; - -pub async fn new<'a>( - state: &'a mut State, - mac_subsystem: Mac, - firmware: &[u8], -) -> (NetDriver<'a>, Control<'a>, Runner<'a>) { - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); - let state_ch = ch_runner.state_runner(); - - let mut runner = Runner::new(ch_runner, mac_subsystem, &state.ioctl_state, &state.events); - - runner.init(firmware).await; - - ( - device, - Control::new(state_ch, &state.events, &state.ioctl_state), - runner, - ) + (runner, control, driver) } fn slice8_mut(x: &mut [u32]) -> &mut [u8] { diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index fbb7cb74..e97c9c8e 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -1,342 +1,27 @@ use embassy_futures::select::{select3, Either3}; -use embassy_net_driver_channel as ch; -use embassy_sync::pubsub::PubSubBehavior; -use crate::mac::event::Events; -use crate::mac::ioctl::{IoctlState, PendingIoctl}; use crate::mac::MTU; use crate::sub::mac::Mac; -pub struct Runner<'a> { - ch: ch::Runner<'a, MTU>, +pub struct Runner { mac: Mac, - - ioctl_state: &'a IoctlState, - ioctl_id: u16, - sdpcm_seq: u8, - sdpcm_seq_max: u8, - - events: &'a Events, + // TODO: tx_ring + // TODO: rx_buf } -impl<'a> Runner<'a> { - pub(crate) fn new(ch: ch::Runner<'a, MTU>, mac: Mac, ioctl_state: &'a IoctlState, events: &'a Events) -> Self { - Self { - ch, - mac, - ioctl_state, - ioctl_id: 0, - sdpcm_seq: 0, - sdpcm_seq_max: 1, - events, - } +impl Runner { + pub(crate) fn new(mac: Mac) -> Self { + Self { mac } } pub(crate) async fn init(&mut self, firmware: &[u8]) { - self.bus.init().await; - - #[cfg(feature = "firmware-logs")] - self.log_init().await; - debug!("wifi init done"); } pub async fn run(mut self) -> ! { let mut buf = [0; 512]; loop { - #[cfg(feature = "firmware-logs")] - self.log_read().await; - - if self.has_credit() { - let ioctl = self.ioctl_state.wait_pending(); - let tx = self.ch.tx_buf(); - let ev = self.bus.wait_for_event(); - - match select3(ioctl, tx, ev).await { - Either3::First(PendingIoctl { - buf: iobuf, - kind, - cmd, - iface, - }) => { - self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; - self.check_status(&mut buf).await; - } - Either3::Second(packet) => { - trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); - - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); - - // There MUST be 2 bytes of padding between the SDPCM and BDC headers. - // And ONLY for data packets! - // No idea why, but the firmware will append two zero bytes to the tx'd packets - // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it - // be oversized and get dropped. - // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90 - // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597 - // ¯\_(ツ)_/¯ - const PADDING_SIZE: usize = 2; - let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len(); - - let seq = self.sdpcm_seq; - self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); - - let sdpcm_header = SdpcmHeader { - len: total_len as u16, // TODO does this len need to be rounded up to u32? - len_inv: !total_len as u16, - sequence: seq, - channel_and_flags: CHANNEL_TYPE_DATA, - next_length: 0, - header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _, - wireless_flow_control: 0, - bus_data_credit: 0, - reserved: [0, 0], - }; - - let bdc_header = BdcHeader { - flags: BDC_VERSION << BDC_VERSION_SHIFT, - priority: 0, - flags2: 0, - data_offset: 0, - }; - trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", bdc_header); - - buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE] - .copy_from_slice(&bdc_header.to_bytes()); - buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()] - .copy_from_slice(packet); - - let total_len = (total_len + 3) & !3; // round up to 4byte - - trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); - - self.bus.wlan_write(&buf[..(total_len / 4)]).await; - self.ch.tx_done(); - self.check_status(&mut buf).await; - } - Either3::Third(()) => { - self.handle_irq(&mut buf).await; - } - } - } else { - warn!("TX stalled"); - self.bus.wait_for_event().await; - self.handle_irq(&mut buf).await; - } + // TODO } } - - /// Wait for IRQ on F2 packet available - async fn handle_irq(&mut self, buf: &mut [u32; 512]) { - // Receive stuff - let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; - trace!("irq{}", FormatInterrupt(irq)); - - if irq & IRQ_F2_PACKET_AVAILABLE != 0 { - self.check_status(buf).await; - } - - if irq & IRQ_DATA_UNAVAILABLE != 0 { - // TODO what should we do here? - warn!("IRQ DATA_UNAVAILABLE, clearing..."); - self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await; - } - } - - /// Handle F2 events while status register is set - async fn check_status(&mut self, buf: &mut [u32; 512]) { - loop { - let status = self.bus.status(); - trace!("check status{}", FormatStatus(status)); - - if status & STATUS_F2_PKT_AVAILABLE != 0 { - let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(buf, len).await; - trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); - self.rx(&mut slice8_mut(buf)[..len as usize]); - } else { - break; - } - } - } - - fn rx(&mut self, packet: &mut [u8]) { - let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { - return; - }; - - self.update_credit(&sdpcm_header); - - let channel = sdpcm_header.channel_and_flags & 0x0f; - - match channel { - CHANNEL_TYPE_CONTROL => { - let Some((cdc_header, response)) = CdcHeader::parse(payload) else { - return; - }; - trace!(" {:?}", cdc_header); - - if cdc_header.id == self.ioctl_id { - if cdc_header.status != 0 { - // TODO: propagate error instead - panic!("IOCTL error {}", cdc_header.status as i32); - } - - self.ioctl_state.ioctl_done(response); - } - } - CHANNEL_TYPE_EVENT => { - let Some((_, bdc_packet)) = BdcHeader::parse(payload) else { - warn!("BDC event, incomplete header"); - return; - }; - - let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else { - warn!("BDC event, incomplete data"); - return; - }; - - const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h - if event_packet.eth.ether_type != ETH_P_LINK_CTL { - warn!( - "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", - event_packet.eth.ether_type, ETH_P_LINK_CTL - ); - return; - } - const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; - if event_packet.hdr.oui != BROADCOM_OUI { - warn!( - "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", - Bytes(&event_packet.hdr.oui), - Bytes(BROADCOM_OUI) - ); - return; - } - const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; - if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { - warn!("unexpected subtype {}", event_packet.hdr.subtype); - return; - } - - const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; - if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { - warn!("unexpected user_subtype {}", event_packet.hdr.subtype); - return; - } - - let evt_type = events::Event::from(event_packet.msg.event_type as u8); - debug!( - "=== EVENT {:?}: {:?} {:02x}", - evt_type, - event_packet.msg, - Bytes(evt_data) - ); - - if self.events.mask.is_enabled(evt_type) { - let status = event_packet.msg.status; - let event_payload = match evt_type { - Event::ESCAN_RESULT if status == EStatus::PARTIAL => { - let Some((_, bss_info)) = ScanResults::parse(evt_data) else { - return; - }; - let Some(bss_info) = BssInfo::parse(bss_info) else { - return; - }; - events::Payload::BssInfo(*bss_info) - } - Event::ESCAN_RESULT => events::Payload::None, - _ => 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 => { - let Some((_, packet)) = BdcHeader::parse(payload) else { - return; - }; - trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); - - match self.ch.try_rx_buf() { - Some(buf) => { - buf[..packet.len()].copy_from_slice(packet); - self.ch.rx_done(packet.len()) - } - None => warn!("failed to push rxd packet to the channel."), - } - } - _ => {} - } - } - - fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { - if sdpcm_header.channel_and_flags & 0xf < 3 { - let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; - if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { - sdpcm_seq_max = self.sdpcm_seq + 2; - } - self.sdpcm_seq_max = sdpcm_seq_max; - } - } - - fn has_credit(&self) -> bool { - self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 - } - - async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); - - let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); - - let sdpcm_seq = self.sdpcm_seq; - self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); - self.ioctl_id = self.ioctl_id.wrapping_add(1); - - let sdpcm_header = SdpcmHeader { - len: total_len as u16, // TODO does this len need to be rounded up to u32? - len_inv: !total_len as u16, - sequence: sdpcm_seq, - channel_and_flags: CHANNEL_TYPE_CONTROL, - next_length: 0, - header_length: SdpcmHeader::SIZE as _, - wireless_flow_control: 0, - bus_data_credit: 0, - reserved: [0, 0], - }; - - let cdc_header = CdcHeader { - cmd: cmd, - len: data.len() as _, - flags: kind as u16 | (iface as u16) << 12, - id: self.ioctl_id, - status: 0, - }; - trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", cdc_header); - - buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); - buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); - - let total_len = (total_len + 3) & !3; // round up to 4byte - - trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); - - self.bus.wlan_write(&buf[..total_len / 4]).await; - } }