Merge pull request #70 from kbleeke/wifi-scanning-ioctl
Wifi scanning ioctl
This commit is contained in:
		@@ -143,4 +143,3 @@ async fn main(spawner: Spawner) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,11 @@ use embassy_time::{Duration, Timer};
 | 
			
		||||
 | 
			
		||||
pub use crate::bus::SpiBusCyw43;
 | 
			
		||||
use crate::consts::*;
 | 
			
		||||
use crate::events::{Event, Events};
 | 
			
		||||
use crate::events::{Event, EventSubscriber, Events};
 | 
			
		||||
use crate::fmt::Bytes;
 | 
			
		||||
use crate::ioctl::{IoctlState, IoctlType};
 | 
			
		||||
use crate::structs::*;
 | 
			
		||||
use crate::{countries, PowerManagementMode};
 | 
			
		||||
use crate::{countries, events, PowerManagementMode};
 | 
			
		||||
 | 
			
		||||
pub struct Control<'a> {
 | 
			
		||||
    state_ch: ch::StateRunner<'a>,
 | 
			
		||||
@@ -245,9 +245,13 @@ impl<'a> Control<'a> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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));
 | 
			
		||||
 | 
			
		||||
        let mut buf = [0; 64];
 | 
			
		||||
        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);
 | 
			
		||||
@@ -304,4 +308,69 @@ impl<'a> Control<'a> {
 | 
			
		||||
 | 
			
		||||
        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,10 +1,12 @@
 | 
			
		||||
#![allow(unused)]
 | 
			
		||||
#![allow(dead_code)]
 | 
			
		||||
#![allow(non_camel_case_types)]
 | 
			
		||||
 | 
			
		||||
use core::cell::RefCell;
 | 
			
		||||
 | 
			
		||||
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)]
 | 
			
		||||
#[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.
 | 
			
		||||
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 struct Events {
 | 
			
		||||
@@ -313,6 +314,7 @@ pub struct Status {
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum Payload {
 | 
			
		||||
    None,
 | 
			
		||||
    BssInfo(BssInfo),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
@@ -344,7 +346,7 @@ impl EventMask {
 | 
			
		||||
        let word = 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) {
 | 
			
		||||
@@ -378,6 +380,7 @@ impl SharedEventMask {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub fn disable(&self, events: &[Event]) {
 | 
			
		||||
        let mut mask = self.mask.borrow_mut();
 | 
			
		||||
        for event in events {
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ use crate::bus::Bus;
 | 
			
		||||
pub use crate::bus::SpiBusCyw43;
 | 
			
		||||
pub use crate::control::Control;
 | 
			
		||||
pub use crate::runner::Runner;
 | 
			
		||||
pub use crate::structs::BssInfo;
 | 
			
		||||
 | 
			
		||||
const MTU: usize = 1514;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ use embedded_hal_1::digital::OutputPin;
 | 
			
		||||
use crate::bus::Bus;
 | 
			
		||||
pub use crate::bus::SpiBusCyw43;
 | 
			
		||||
use crate::consts::*;
 | 
			
		||||
use crate::events::{Events, Status};
 | 
			
		||||
use crate::events::{Event, Events, Status};
 | 
			
		||||
use crate::fmt::Bytes;
 | 
			
		||||
use crate::ioctl::{IoctlState, IoctlType, PendingIoctl};
 | 
			
		||||
use crate::nvram::NVRAM;
 | 
			
		||||
@@ -351,6 +351,8 @@ where
 | 
			
		||||
                        panic!("IOCTL error {}", cdc_header.status as i32);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    info!("IOCTL Response: {:02x}", Bytes(response));
 | 
			
		||||
 | 
			
		||||
                    self.ioctl_state.ioctl_done(response);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -404,7 +406,15 @@ where
 | 
			
		||||
 | 
			
		||||
                if self.events.mask.is_enabled(evt_type) {
 | 
			
		||||
                    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
 | 
			
		||||
                    // publish() is a deadlock risk in the current design as awaiting here prevents ioctls
 | 
			
		||||
 
 | 
			
		||||
@@ -404,3 +404,84 @@ impl EventMask {
 | 
			
		||||
        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(),
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user