Merge pull request #70 from kbleeke/wifi-scanning-ioctl

Wifi scanning ioctl
This commit is contained in:
Dario Nieuwenhuis 2023-04-30 15:05:16 +00:00 committed by GitHub
commit 5659269c8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 173 additions and 10 deletions

View File

@ -143,4 +143,3 @@ async fn main(spawner: Spawner) {
} }
} }
} }

View File

@ -6,11 +6,11 @@ use embassy_time::{Duration, Timer};
pub use crate::bus::SpiBusCyw43; pub use crate::bus::SpiBusCyw43;
use crate::consts::*; use crate::consts::*;
use crate::events::{Event, Events}; use crate::events::{Event, EventSubscriber, Events};
use crate::fmt::Bytes; use crate::fmt::Bytes;
use crate::ioctl::{IoctlState, IoctlType}; use crate::ioctl::{IoctlState, IoctlType};
use crate::structs::*; use crate::structs::*;
use crate::{countries, PowerManagementMode}; use crate::{countries, events, PowerManagementMode};
pub struct Control<'a> { pub struct Control<'a> {
state_ch: ch::StateRunner<'a>, state_ch: ch::StateRunner<'a>,
@ -245,9 +245,13 @@ impl<'a> Control<'a> {
} }
async fn set_iovar(&mut self, name: &str, val: &[u8]) { 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]) {
info!("set {} = {:02x}", name, Bytes(val)); info!("set {} = {:02x}", name, Bytes(val));
let mut buf = [0; 64]; let mut buf = [0; BUFSIZE];
buf[..name.len()].copy_from_slice(name.as_bytes()); buf[..name.len()].copy_from_slice(name.as_bytes());
buf[name.len()] = 0; buf[name.len()] = 0;
buf[name.len() + 1..][..val.len()].copy_from_slice(val); buf[name.len() + 1..][..val.len()].copy_from_slice(val);
@ -304,4 +308,69 @@ impl<'a> Control<'a> {
resp_len 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();
}
} }

View File

@ -1,10 +1,12 @@
#![allow(unused)] #![allow(dead_code)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
use core::cell::RefCell; use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::pubsub::{PubSubChannel, Publisher, Subscriber}; use embassy_sync::pubsub::{PubSubChannel, Subscriber};
use crate::structs::BssInfo;
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] #[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -286,7 +288,6 @@ pub enum Event {
// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. // 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 EventQueue = PubSubChannel<NoopRawMutex, Message, 2, 1, 1>;
pub type EventPublisher<'a> = Publisher<'a, NoopRawMutex, Message, 2, 1, 1>;
pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>;
pub struct Events { pub struct Events {
@ -313,6 +314,7 @@ pub struct Status {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum Payload { pub enum Payload {
None, None,
BssInfo(BssInfo),
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -344,7 +346,7 @@ impl EventMask {
let word = n / u32::BITS; let word = n / u32::BITS;
let bit = n % u32::BITS; let bit = n % u32::BITS;
self.mask[word as usize] |= (1 << bit); self.mask[word as usize] |= 1 << bit;
} }
fn disable(&mut self, event: Event) { fn disable(&mut self, event: Event) {
@ -378,6 +380,7 @@ impl SharedEventMask {
} }
} }
#[allow(dead_code)]
pub fn disable(&self, events: &[Event]) { pub fn disable(&self, events: &[Event]) {
let mut mask = self.mask.borrow_mut(); let mut mask = self.mask.borrow_mut();
for event in events { for event in events {

View File

@ -29,6 +29,7 @@ use crate::bus::Bus;
pub use crate::bus::SpiBusCyw43; pub use crate::bus::SpiBusCyw43;
pub use crate::control::Control; pub use crate::control::Control;
pub use crate::runner::Runner; pub use crate::runner::Runner;
pub use crate::structs::BssInfo;
const MTU: usize = 1514; const MTU: usize = 1514;

View File

@ -7,7 +7,7 @@ use embedded_hal_1::digital::OutputPin;
use crate::bus::Bus; use crate::bus::Bus;
pub use crate::bus::SpiBusCyw43; pub use crate::bus::SpiBusCyw43;
use crate::consts::*; use crate::consts::*;
use crate::events::{Events, Status}; use crate::events::{Event, Events, Status};
use crate::fmt::Bytes; use crate::fmt::Bytes;
use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; use crate::ioctl::{IoctlState, IoctlType, PendingIoctl};
use crate::nvram::NVRAM; use crate::nvram::NVRAM;
@ -351,6 +351,8 @@ where
panic!("IOCTL error {}", cdc_header.status as i32); panic!("IOCTL error {}", cdc_header.status as i32);
} }
info!("IOCTL Response: {:02x}", Bytes(response));
self.ioctl_state.ioctl_done(response); self.ioctl_state.ioctl_done(response);
} }
} }
@ -404,7 +406,15 @@ where
if self.events.mask.is_enabled(evt_type) { if self.events.mask.is_enabled(evt_type) {
let status = event_packet.msg.status; let status = event_packet.msg.status;
let event_payload = events::Payload::None; 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 // this intentionally uses the non-blocking publish immediate
// publish() is a deadlock risk in the current design as awaiting here prevents ioctls // publish() is a deadlock risk in the current design as awaiting here prevents ioctls

View File

@ -404,3 +404,84 @@ impl EventMask {
self.events[evt / 8] &= !(1 << (evt % 8)); self.events[evt / 8] &= !(1 << (evt % 8));
} }
} }
/// Parameters for a wifi scan
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C)]
pub struct ScanParams {
pub version: u32,
pub action: u16,
pub sync_id: u16,
pub ssid_len: u32,
pub ssid: [u8; 32],
pub bssid: [u8; 6],
pub bss_type: u8,
pub scan_type: u8,
pub nprobes: u32,
pub active_time: u32,
pub passive_time: u32,
pub home_time: u32,
pub channel_num: u32,
pub channel_list: [u16; 1],
}
impl_bytes!(ScanParams);
/// Wifi Scan Results Header, followed by `bss_count` `BssInfo`
#[derive(Clone, Copy)]
// #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C, packed(2))]
pub struct ScanResults {
pub buflen: u32,
pub version: u32,
pub sync_id: u16,
pub bss_count: u16,
}
impl_bytes!(ScanResults);
impl ScanResults {
pub fn parse(packet: &mut [u8]) -> Option<(&mut ScanResults, &mut [u8])> {
if packet.len() < ScanResults::SIZE {
return None;
}
let (scan_results, bssinfo) = packet.split_at_mut(ScanResults::SIZE);
let scan_results = ScanResults::from_bytes_mut(scan_results.try_into().unwrap());
if scan_results.bss_count > 0 && bssinfo.len() < BssInfo::SIZE {
warn!("Scan result, incomplete BssInfo");
return None;
}
Some((scan_results, bssinfo))
}
}
/// Wifi Scan Result
#[derive(Clone, Copy)]
// #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C, packed(2))]
#[non_exhaustive]
pub struct BssInfo {
pub version: u32,
pub length: u32,
pub bssid: [u8; 6],
pub beacon_period: u16,
pub capability: u16,
pub ssid_len: u8,
pub ssid: [u8; 32],
// there will be more stuff here
}
impl_bytes!(BssInfo);
impl BssInfo {
pub fn parse(packet: &mut [u8]) -> Option<&mut Self> {
if packet.len() < BssInfo::SIZE {
return None;
}
Some(BssInfo::from_bytes_mut(
packet[..BssInfo::SIZE].as_mut().try_into().unwrap(),
))
}
}