wpan: refactor control, driver
This commit is contained in:
parent
1d2c47273d
commit
8f23b6faa6
6 changed files with 105 additions and 1157 deletions
|
@ -1,16 +1,4 @@
|
||||||
use core::cmp::{max, min};
|
use crate::mac::runner::Runner;
|
||||||
|
|
||||||
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};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
|
@ -18,437 +6,15 @@ pub struct Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Control<'a> {
|
pub struct Control<'a> {
|
||||||
state_ch: ch::StateRunner<'a>,
|
runner: &'a Runner,
|
||||||
events: &'a Events,
|
|
||||||
ioctl_state: &'a IoctlState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Control<'a> {
|
impl<'a> Control<'a> {
|
||||||
pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
|
pub(crate) fn new(runner: &'a Runner) -> Self {
|
||||||
Self {
|
Self { runner: runner }
|
||||||
state_ch,
|
|
||||||
events: event_sub,
|
|
||||||
ioctl_state,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init(&mut self, clm: &[u8]) {
|
pub async fn init(&mut self) {
|
||||||
const CHUNK_SIZE: usize = 1024;
|
// TODO
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<const BUFSIZE: usize>(&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<BssInfo> {
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,102 +1,110 @@
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
|
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
use core::slice;
|
use core::task::Context;
|
||||||
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
||||||
use embedded_hal_1::digital::OutputPin;
|
|
||||||
use events::Events;
|
|
||||||
use ioctl::IoctlState;
|
|
||||||
|
|
||||||
use crate::bus::Bus;
|
use crate::mac::runner::Runner;
|
||||||
pub use crate::bus::SpiBusCyw43;
|
use crate::mac::MTU;
|
||||||
pub use crate::control::{Control, Error as ControlError};
|
|
||||||
pub use crate::runner::Runner;
|
|
||||||
pub use crate::structs::BssInfo;
|
|
||||||
|
|
||||||
const MTU: usize = 1514;
|
pub struct Driver<'d> {
|
||||||
|
runner: &'d Runner,
|
||||||
pub struct State {
|
|
||||||
ioctl_state: IoctlState,
|
|
||||||
ch: ch::State<MTU, 4, 4>,
|
|
||||||
events: Events,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl<'d> Driver<'d> {
|
||||||
pub fn new() -> Self {
|
pub(crate) fn new(runner: &'d Runner) -> Self {
|
||||||
Self {
|
Self { runner: runner }
|
||||||
ioctl_state: IoctlState::new(),
|
|
||||||
ch: ch::State::new(),
|
|
||||||
events: Events::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
impl<'d> embassy_net_driver::Driver for Driver<'d> {
|
||||||
pub enum PowerManagementMode {
|
// type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
|
||||||
/// Custom, officially unsupported mode. Use at your own risk.
|
// type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
|
||||||
/// All power-saving features set to their max at only a marginal decrease in power consumption
|
type RxToken<'a> = RxToken where Self: 'a;
|
||||||
/// as oppposed to `Aggressive`.
|
type TxToken<'a> = TxToken where Self: 'a;
|
||||||
SuperSave,
|
|
||||||
|
|
||||||
/// Aggressive power saving mode.
|
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||||
Aggressive,
|
// 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.
|
None
|
||||||
PowerSave,
|
}
|
||||||
|
|
||||||
/// Performance is prefered over power consumption but still some power is conserved as opposed to
|
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
|
||||||
/// `None`.
|
// WAKER.register(cx.waker());
|
||||||
Performance,
|
// / 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
|
None
|
||||||
/// a much lower throughput.
|
}
|
||||||
ThroughputThrottling,
|
|
||||||
|
|
||||||
/// No power management is configured. This consumes the most power.
|
fn capabilities(&self) -> Capabilities {
|
||||||
None,
|
let mut caps = Capabilities::default();
|
||||||
}
|
caps.max_transmission_unit = MTU;
|
||||||
|
// caps.max_burst_size = Some(self.tx.len());
|
||||||
|
|
||||||
impl Default for PowerManagementMode {
|
caps.medium = Medium::Ieee802154;
|
||||||
fn default() -> Self {
|
caps
|
||||||
Self::PowerSave
|
}
|
||||||
|
|
||||||
|
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 {
|
pub struct RxToken {
|
||||||
// TODO
|
// rx: &'a mut RDesRing<'d>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type NetDriver<'a> = ch::Device<'a, MTU>;
|
impl embassy_net_driver::RxToken for RxToken {
|
||||||
|
fn consume<R, F>(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>(
|
let pkt = &[];
|
||||||
state: &'a mut State,
|
let r = f(&mut pkt[0..]);
|
||||||
pwr: PWR,
|
// self.rx.pop_packet();
|
||||||
spi: SPI,
|
r
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
|
pub struct TxToken {
|
||||||
let len = x.len() * 4;
|
// tx: &'a mut TDesRing<'d>,
|
||||||
unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
|
}
|
||||||
|
|
||||||
|
impl embassy_net_driver::TxToken for TxToken {
|
||||||
|
fn consume<R, F>(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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 core::mem;
|
||||||
|
|
||||||
use super::indications::{
|
use super::indications::{
|
||||||
|
@ -80,7 +74,6 @@ impl Event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum MacEvent<'a> {
|
pub enum MacEvent<'a> {
|
||||||
MlmeAssociateCnf(&'a AssociateConfirm),
|
MlmeAssociateCnf(&'a AssociateConfirm),
|
||||||
|
@ -109,116 +102,3 @@ pub enum MacEvent<'a> {
|
||||||
McpsDataInd(&'a DataIndication),
|
McpsDataInd(&'a DataIndication),
|
||||||
MlmePollInd(&'a PollIndication),
|
MlmePollInd(&'a PollIndication),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient.
|
|
||||||
pub type EventQueue = PubSubChannel<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)]
|
|
||||||
#[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<EventMask>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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<IoctlStateInner>,
|
|
||||||
wakers: RefCell<Wakers>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
mod consts;
|
mod consts;
|
||||||
pub mod control;
|
pub mod control;
|
||||||
|
mod driver;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod indications;
|
pub mod indications;
|
||||||
mod ioctl;
|
|
||||||
mod macros;
|
mod macros;
|
||||||
mod opcodes;
|
mod opcodes;
|
||||||
pub mod responses;
|
pub mod responses;
|
||||||
|
@ -12,86 +12,19 @@ pub mod typedefs;
|
||||||
|
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
|
||||||
use embassy_net_driver_channel as ch;
|
|
||||||
|
|
||||||
pub use crate::mac::control::{Control, Error as ControlError};
|
pub use crate::mac::control::{Control, Error as ControlError};
|
||||||
use crate::mac::event::Events;
|
use crate::mac::driver::Driver;
|
||||||
use crate::mac::ioctl::IoctlState;
|
|
||||||
pub use crate::mac::runner::Runner;
|
pub use crate::mac::runner::Runner;
|
||||||
use crate::sub::mac::Mac;
|
use crate::sub::mac::Mac;
|
||||||
|
|
||||||
const MTU: usize = 1514;
|
const MTU: usize = 127;
|
||||||
|
|
||||||
pub struct State {
|
pub async fn new<'a>(mac: Mac) -> (Runner, Control<'a>, Driver<'a>) {
|
||||||
ioctl_state: IoctlState,
|
let runner = Runner::new(mac);
|
||||||
ch: ch::State<MTU, 4, 4>,
|
let control = Control::new(&runner);
|
||||||
events: Events,
|
let driver = Driver::new(&runner);
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
(runner, control, driver)
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
|
fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
|
||||||
|
|
|
@ -1,342 +1,27 @@
|
||||||
use embassy_futures::select::{select3, Either3};
|
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::mac::MTU;
|
||||||
use crate::sub::mac::Mac;
|
use crate::sub::mac::Mac;
|
||||||
|
|
||||||
pub struct Runner<'a> {
|
pub struct Runner {
|
||||||
ch: ch::Runner<'a, MTU>,
|
|
||||||
mac: Mac,
|
mac: Mac,
|
||||||
|
// TODO: tx_ring
|
||||||
ioctl_state: &'a IoctlState,
|
// TODO: rx_buf
|
||||||
ioctl_id: u16,
|
|
||||||
sdpcm_seq: u8,
|
|
||||||
sdpcm_seq_max: u8,
|
|
||||||
|
|
||||||
events: &'a Events,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Runner<'a> {
|
impl Runner {
|
||||||
pub(crate) fn new(ch: ch::Runner<'a, MTU>, mac: Mac, ioctl_state: &'a IoctlState, events: &'a Events) -> Self {
|
pub(crate) fn new(mac: Mac) -> Self {
|
||||||
Self {
|
Self { mac }
|
||||||
ch,
|
|
||||||
mac,
|
|
||||||
ioctl_state,
|
|
||||||
ioctl_id: 0,
|
|
||||||
sdpcm_seq: 0,
|
|
||||||
sdpcm_seq_max: 1,
|
|
||||||
events,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn init(&mut self, firmware: &[u8]) {
|
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");
|
debug!("wifi init done");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(mut self) -> ! {
|
pub async fn run(mut self) -> ! {
|
||||||
let mut buf = [0; 512];
|
let mut buf = [0; 512];
|
||||||
loop {
|
loop {
|
||||||
#[cfg(feature = "firmware-logs")]
|
// TODO
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue