esp_hosted/
wifi.rs

1//! This module contains Wi-Fi and BLE-specific functionality.
2
3use defmt::{Format, println};
4use heapless::Vec;
5use num_enum::TryFromPrimitive;
6
7use crate::{
8    EspError,
9    WireType::{Len, Varint},
10    proto_data::{RpcId, RpcReqWifiInit, RpcReqWifiScanStart},
11    rpc::{Rpc, WireType, decode_tag, decode_varint, setup_rpc, write_rpc},
12    util::write_empty_msg,
13};
14// todo: Macros may help.
15
16const MAX_AP_RECORDS: usize = 30; // todo: A/R.
17
18// /// Information about one BLE advertisement
19// #[derive(Debug)]
20// pub struct BleDevice {
21//     pub addr: [u8; 6],
22//     pub data: Vec<u8, 31>,
23//     pub rssi: i8,
24// }
25
26/// From a data buffer (e.g. as part of the Rpc struct), parse into Access Point records.
27/// The data buffer passed starts post the RPC "header"; the same data we include in the `Rpc` struct.
28pub fn parse_ap_records(data: &[u8]) -> Result<Vec<WifiApRecord, MAX_AP_RECORDS>, EspError> {
29    let mut result = Vec::new();
30
31    let mut i = 1; // todo: Not robust!
32    if data.len() == 0 {
33        println!("Empty data on parsing AP records.");
34        return Err(EspError::InvalidData);
35    }
36
37    let (num_records, nr_len) = decode_varint(&data[i..])?;
38    i += nr_len;
39
40    for _ in 0..num_records {
41        i += 1; // todo: Skipping over the tag for the records struct.
42
43        if i >= data.len() {
44            return Err(EspError::InvalidData);
45        }
46
47        let (record_len, record_len_len) = decode_varint(&data[i..])?;
48        i += record_len_len;
49
50        // todo: This won't work; you need to get the varint size of each field etc!
51        let (record, _record_size) = WifiApRecord::from_bytes(&data[i..i + record_len as usize])?;
52        i += record_len as usize;
53
54        result.push(record).map_err(|_| EspError::Capacity)?;
55    }
56
57    Ok(result)
58}
59
60#[derive(Clone, Format)]
61pub struct InitConfig {
62    pub static_rx_buf_num: i32,
63    pub dynamic_rx_buf_num: i32,
64    pub tx_buf_type: i32,
65    pub static_tx_buf_num: i32,
66    pub dynamic_tx_buf_num: i32,
67    pub cache_tx_buf_num: i32,
68    //
69    pub csi_enable: i32,
70    pub ampdu_rx_enable: i32,
71    pub ampdu_tx_enable: i32,
72    pub amsdu_tx_enable: i32,
73    //
74    pub nvs_enable: i32,
75    pub nano_enable: i32,
76    pub rx_ba_win: i32,
77    //
78    pub wifi_task_core_id: i32,
79    pub beacon_max_len: i32,
80    pub mgmt_sbuf_num: i32,
81    pub feature_caps: u64,
82    pub sta_disconnected_pm: bool,
83    pub espnow_max_encrypt_num: i32,
84    pub magic: i32,
85}
86
87impl Default for InitConfig {
88    /// Suitable for use as an AP (Station)
89    fn default() -> Self {
90        Self {
91            static_rx_buf_num: 10,
92            dynamic_rx_buf_num: 32,
93            tx_buf_type: 3, // dynamics
94            static_tx_buf_num: 0,
95            dynamic_tx_buf_num: 32,
96            cache_tx_buf_num: 32,
97            //
98            csi_enable: 0,
99            ampdu_rx_enable: 1,
100            ampdu_tx_enable: 1,
101            amsdu_tx_enable: 1,
102            //
103            nvs_enable: 1, // enable if using WiFi persistence
104            nano_enable: 0,
105            rx_ba_win: 6, // default block ack window
106            //
107            wifi_task_core_id: 0,
108            beacon_max_len: 752, // AP beacon max len
109            mgmt_sbuf_num: 32,
110            feature_caps: 0,
111            sta_disconnected_pm: false,
112            espnow_max_encrypt_num: 7,
113            magic: 0x1F2F3F4F, // todo: QC this.
114        }
115    }
116}
117
118impl InitConfig {
119    /// Suitable for passive use
120    pub fn new_promiscuous() -> Self {
121        Self {
122            static_rx_buf_num: 10,
123            dynamic_rx_buf_num: 64,
124            tx_buf_type: 1,
125            static_tx_buf_num: 0,
126            dynamic_tx_buf_num: 32, // todo? 0 is giving out of range error
127            cache_tx_buf_num: 0,
128            //
129            csi_enable: 0,
130            ampdu_rx_enable: 0,
131            ampdu_tx_enable: 0,
132            amsdu_tx_enable: 0,
133            //
134            nvs_enable: 0, // enable if using WiFi persistence
135            nano_enable: 0,
136            rx_ba_win: 6, // default block ack window
137            //
138            wifi_task_core_id: 0,
139            beacon_max_len: 752, // AP beacon max len
140            mgmt_sbuf_num: 32,
141            feature_caps: 0,
142            sta_disconnected_pm: false,
143            espnow_max_encrypt_num: 0,
144            magic: 0x1F2F3F4F, // todo: QC this.
145        }
146    }
147
148    pub fn to_bytes(&self, buf: &mut [u8]) -> usize {
149        let c = &self;
150        let v = WireType::Varint;
151
152        let mut i = 0;
153
154        write_rpc(buf, 1, v, c.static_rx_buf_num as u64, &mut i);
155        write_rpc(buf, 2, v, c.dynamic_rx_buf_num as u64, &mut i);
156        write_rpc(buf, 3, v, c.tx_buf_type as u64, &mut i);
157        write_rpc(buf, 4, v, c.static_tx_buf_num as u64, &mut i);
158        write_rpc(buf, 5, v, c.dynamic_tx_buf_num as u64, &mut i);
159        write_rpc(buf, 6, v, c.cache_tx_buf_num as u64, &mut i);
160        write_rpc(buf, 7, v, c.csi_enable as u64, &mut i);
161        write_rpc(buf, 8, v, c.ampdu_rx_enable as u64, &mut i);
162        write_rpc(buf, 9, v, c.ampdu_tx_enable as u64, &mut i);
163        write_rpc(buf, 10, v, c.amsdu_tx_enable as u64, &mut i);
164        write_rpc(buf, 11, v, c.nvs_enable as u64, &mut i);
165        write_rpc(buf, 12, v, c.nano_enable as u64, &mut i);
166        write_rpc(buf, 13, v, c.rx_ba_win as u64, &mut i);
167        write_rpc(buf, 14, v, c.wifi_task_core_id as u64, &mut i);
168        write_rpc(buf, 15, v, c.beacon_max_len as u64, &mut i);
169        write_rpc(buf, 16, v, c.mgmt_sbuf_num as u64, &mut i);
170        write_rpc(buf, 17, v, c.feature_caps, &mut i);
171        write_rpc(buf, 18, v, c.sta_disconnected_pm as u64, &mut i);
172        write_rpc(buf, 19, v, c.espnow_max_encrypt_num as u64, &mut i);
173        write_rpc(buf, 20, v, c.magic as u64, &mut i);
174
175        i
176    }
177}
178
179#[derive(Clone, Default, Format)]
180/// Range of active scan times per channel.
181/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv423wifi_active_scan_time_t)
182pub struct ActiveScanTime {
183    /// Minimum active scan time per channel, units: millisecond. 0 means use built-ins.
184    pub min: u32,
185    /// Maximum active scan time per channel, units: millisecond, values above 1500 ms may cause
186    /// station to disconnect from AP and are not recommended.
187    pub max: u32,
188}
189
190impl ActiveScanTime {
191    pub fn to_bytes(&self, buf: &mut [u8]) -> usize {
192        let mut i = 0;
193
194        write_rpc(buf, 1, Varint, self.min as u64, &mut i);
195        write_rpc(buf, 2, Varint, self.max as u64, &mut i);
196
197        i
198    }
199}
200
201/// Aggregate of active & passive scan time per channel.
202/// [docs][https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv416wifi_scan_time_t)
203#[derive(Clone, Default)]
204pub struct ScanTime {
205    pub active: ActiveScanTime,
206    /// Passive scan time per channel, units: millisecond, values above 1500 ms may
207    /// cause station to disconnect from AP and are not recommended.
208    pub passive: u32,
209}
210
211impl ScanTime {
212    pub fn to_bytes(&self, buf: &mut [u8]) -> usize {
213        let mut i = 0;
214
215        // todo size?
216        let mut scan_time_buf = [0; 8]; // Measured at 5 with u16 values.
217        let active_size = self.active.to_bytes(&mut scan_time_buf);
218
219        write_rpc(buf, 1, Len, active_size as u64, &mut i);
220        buf[i..i + active_size].copy_from_slice(&scan_time_buf[..active_size]);
221        i += active_size;
222
223        write_rpc(buf, 2, Varint, self.passive as u64, &mut i);
224
225        i
226    }
227}
228
229#[derive(Clone, Copy, PartialEq, Default, TryFromPrimitive, Format)]
230#[repr(u8)]
231pub enum ScanType {
232    #[default]
233    Active = 0,
234    Passive = 1,
235}
236
237/// Parameters for an SSID scan.
238/// Note: If setting most of these values to 0 or empty Vecs, ESP will use its default settings.
239/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_scan_config_t)
240#[derive(Default)]
241pub struct ScanConfig {
242    /// Can limit to a specific SSID or MAC. Empty means no filter.
243    pub ssid: Vec<u8, 33>,
244    pub bssid: Vec<u8, 6>,
245    /// Channel, scan the specific channel. 0 means no filter.
246    pub channel: u8,
247    /// Enable it to scan AP whose SSID is hidden
248    pub show_hidden: bool,
249    /// Scan type, active or passive. 0 means active. 1 is passive. 2 is follow.
250    pub scan_type: ScanType,
251    pub scan_time: ScanTime,
252    pub home_chan_dwell_time: u8,
253}
254
255impl ScanConfig {
256    pub fn to_bytes(&self, buf: &mut [u8]) -> usize {
257        let mut i = 0;
258
259        write_rpc(buf, 1, Len, self.ssid.len() as u64, &mut i);
260        buf[i..i + self.ssid.len()].copy_from_slice(&self.ssid);
261        i += self.ssid.len();
262
263        write_rpc(buf, 2, Len, self.bssid.len() as u64, &mut i);
264        buf[i..i + self.bssid.len()].copy_from_slice(&self.bssid);
265        i += self.bssid.len();
266
267        write_rpc(buf, 3, Varint, self.channel as u64, &mut i);
268        write_rpc(buf, 4, Varint, self.show_hidden as u64, &mut i);
269        write_rpc(buf, 5, Varint, self.scan_type as u64, &mut i);
270
271        // todo size?
272        let mut scan_time_buf = [0; 14]; // Measured at 10 with all fields configured as u16.
273        let scan_time_size = self.scan_time.to_bytes(&mut scan_time_buf);
274
275        write_rpc(buf, 6, Len, scan_time_size as u64, &mut i);
276        buf[i..i + scan_time_size].copy_from_slice(&scan_time_buf[..scan_time_size]);
277        i += scan_time_size;
278
279        write_rpc(buf, 7, Varint, self.home_chan_dwell_time as u64, &mut i);
280
281        i
282    }
283}
284
285/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_second_chan_t)
286#[derive(Clone, Copy, PartialEq, Default, Format, TryFromPrimitive)]
287#[repr(u8)]
288pub enum WifiSecondChan {
289    #[default]
290    None = 0,
291    Above = 1,
292    Below = 2,
293}
294
295/// Wi-Fi authmode type Strength of authmodes Personal Networks : OPEN < WEP < WPA_PSK < OWE < WPA2_PSK =
296/// WPA_WPA2_PSK < WAPI_PSK < WPA3_PSK = WPA2_WPA3_PSK = DPP Enterprise Networks : WIFI_AUTH_WPA2_ENTERPRISE
297/// < WIFI_AUTH_WPA3_ENTERPRISE = WIFI_AUTH_WPA2_WPA3_ENTERPRISE < WIFI_AUTH_WPA3_ENT_192.
298/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv416wifi_auth_mode_t)
299#[derive(Clone, Copy, PartialEq, Default, Format, TryFromPrimitive)]
300#[repr(u8)]
301pub enum WifiAuthMode {
302    #[default]
303    Open = 0,
304    WEP = 1,
305    WPA_PSK = 2,
306    WPA2_PSK = 3,
307    WPA_WPA2_PSK = 4,
308    ENTERPRISE = 5,
309    WPA2_ENTERPRISE = 6,
310    WPA3_PSK = 7,
311    // todo: MOre A/R.
312}
313
314/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_cipher_type_t)
315#[derive(Clone, Copy, PartialEq, Default, Format, TryFromPrimitive)]
316#[repr(u8)]
317pub enum WifiCipher {
318    #[default]
319    None = 0,
320    WEP40 = 1,
321    WEP104 = 2,
322    TKIP = 3,
323    CCMP = 4,
324    TKIP_CCMP = 5,
325    AES_CMAC128 = 6,
326    SMS4 = 7,
327    GCMP = 8,
328    GCMP256 = 9,
329    AES_GMAC128 = 10,
330    AES_GMAC256 = 11,
331    UNKNOWN = 12, // todo: MOre A/R.
332}
333
334/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv410wifi_ant_t)
335#[derive(Clone, Copy, PartialEq, Default, Format, TryFromPrimitive)]
336#[repr(u8)]
337pub enum WifiAnt {
338    #[default]
339    Ant0 = 0,
340    Ant1 = 1,
341    /// Invalid
342    Max = 2,
343}
344
345/// Structure describing Wi-Fi country-based regional restrictions.
346/// [docs][https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv414wifi_country_t]
347#[derive(Default, Format)]
348pub struct WifiCountry {
349    /// Country code string.
350    pub cc: [u8; 3],
351    /// Start channel of the allowed 2.4GHz Wi-Fi channels
352    pub schan: u8,
353    /// Total channel number of the allowed 2.4GHz Wi-Fi channels
354    pub nchan: u8,
355    pub max_tx_power: i8,
356    /// Enum. Auto for 0, Manual for 1
357    pub policy: u8,
358}
359
360/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv416wifi_bandwidth_t)
361#[derive(Clone, Copy, PartialEq, Default, Format, TryFromPrimitive)]
362#[repr(u8)]
363pub enum WifiBandwidth {
364    #[default]
365    HT20 = 0,
366    /// 20 Mhz
367    BW20 = 1,
368    BW_HT40 = 2,
369    /// 40 Mhz
370    BW40 = 3,
371    BW80 = 4,
372    BW160 = 5,
373    /// 80 + 80 Mhz
374    BW80_BW80 = 6,
375}
376
377/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_he_ap_info_t)
378#[derive(Default, Format)]
379// todo: The protobuf here doesn't match teh normal docs version
380pub struct WifiHeApInfo {
381    pub bitmask: u32,
382    pub bssid_index: u32,
383}
384
385///Description of a Wi-Fi AP.
386/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv416wifi_ap_record_t)
387// #[derive(Format)]
388#[derive(Default)]
389pub struct WifiApRecord {
390    pub bssid: [u8; 6],
391    pub ssid: Vec<u8, 33>,
392    /// Channel of AP
393    pub primary: u8,
394    pub second: WifiSecondChan,
395    pub rssi: i8,
396    pub authmode: WifiAuthMode,
397    pub pairwise_cipher: WifiCipher,
398    pub group_cipher: WifiCipher,
399    /// Antenna used to receive beacon from AP
400    pub ant: WifiAnt,
401    /// Bit 0, 11b. 1: 11g. 2: 11n. 3: low rate. 4-6: 11ax mode.
402    pub bitmask: u32,
403    pub country: WifiCountry,
404    pub he_ap: WifiHeApInfo,
405    ///For AP 20 MHz this value is set to 1. For AP 40 MHz this value is set to 2.
406    ///  For AP 80 MHz this value is set to 3. For AP 160MHz this value is set to 4.
407    ///     For AP 80+80MHz this value is set to 5
408    pub bandwidth: WifiBandwidth,
409    ///This fields are used only AP bandwidth is 80 and 160 MHz, to transmit the center channel
410    ///   frequency of the BSS. For AP bandwidth is 80 + 80 MHz, it is the center channel frequency
411    ///    of the lower frequency segment.
412    pub vht_ch_freq1: u8,
413    ///This fields are used only AP bandwidth is 80 + 80 MHz, and is used to transmit the center
414    ///   channel frequency of the second segment.
415    pub vht_ch_freq2: u8,
416}
417
418impl WifiApRecord {
419    pub fn from_bytes(buf: &[u8]) -> Result<(Self, usize), EspError> {
420        let mut i = 0;
421        let mut result = Self::default();
422
423        loop {
424            if i >= buf.len() {
425                break;
426            }
427            let (tag, tag_len) = decode_varint(&buf[i..])?;
428            i += tag_len;
429
430            let (field, _wire_type) = decode_tag(tag as u16);
431
432            match field {
433                1 => {
434                    let (field_len, field_len_len) = decode_varint(&buf[i..])?;
435                    i += field_len_len;
436                    // println!("BSSID buf: {:?}", &buf[i..i + field_len as usize]);
437
438                    if i + field_len as usize >= buf.len() {
439                        return Err(EspError::Capacity);
440                    }
441
442                    result
443                        .bssid
444                        .copy_from_slice(&buf[i..i + field_len as usize]);
445                    i += field_len as usize;
446                }
447                2 => {
448                    let (field_len, field_len_len) = decode_varint(&buf[i..])?;
449                    i += field_len_len;
450                    // println!("SSID buf: {:?}", &buf[i..i + field_len as usize]);
451
452                    if i + field_len as usize >= buf.len() {
453                        return Err(EspError::Capacity);
454                    }
455
456                    result.ssid = Vec::<_, 33>::from_slice(&buf[i..i + field_len as usize])
457                        .map_err(|_| EspError::InvalidData)?;
458                    i += field_len as usize;
459                }
460                3 => {
461                    result.primary = buf[i];
462                    i += 1;
463                }
464                4 => {
465                    result.second = buf[i].try_into().unwrap_or_default();
466                    i += 1;
467                }
468                5 => {
469                    result.rssi = buf[i] as i8;
470                    i += 10; // Non-zigzag protobuf negative val encoding... yikes.
471                }
472                6 => {
473                    result.authmode = buf[i].try_into().unwrap_or_default();
474                    i += 1;
475                }
476                7 => {
477                    result.pairwise_cipher = buf[i].try_into().unwrap_or_default();
478                    i += 1;
479                }
480                8 => {
481                    result.group_cipher = buf[i].try_into().unwrap_or_default();
482                    i += 1;
483                }
484                9 => {
485                    result.ant = buf[i].try_into().unwrap_or_default();
486                    i += 1;
487                }
488                10 => {
489                    let (val, len) = decode_varint(&buf[i..])?;
490                    result.bitmask = val as u32;
491                    i += len;
492                }
493                11 => {
494                    let (country_len, country_len_len) = decode_varint(&buf[i..])?;
495                    i += country_len_len;
496                    // todo: Parse here.
497                    i += country_len as usize;
498                }
499                12 => {
500                    let (he_ap_len, he_ap_len_len) = decode_varint(&buf[i..])?;
501                    i += he_ap_len_len;
502                    // todo: Parse here.
503                    i += he_ap_len as usize;
504                }
505                13 => {
506                    result.bandwidth = buf[i].try_into().unwrap_or_default();
507                    i += 1;
508                }
509                14 => {
510                    result.vht_ch_freq1 = buf[i];
511                    i += 1;
512                }
513                15 => {
514                    result.vht_ch_freq2 = buf[i];
515                    i += 1;
516                }
517                _ => {
518                    println!("Unparsed field: {:?}", field);
519                }
520            }
521        }
522
523        Ok((result, i))
524    }
525}
526
527/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv416wifi_interface_t)
528#[derive(Clone, Copy, Format)]
529#[repr(u8)]
530pub enum InterfaceType {
531    Station = 0,
532    Ap = 1,
533}
534
535/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv411wifi_mode_t)
536#[derive(Clone, Copy, Format)]
537#[repr(u8)]
538pub enum WifiMode {
539    Null = 0,
540    /// Wi-Fi station mode
541    Station = 1,
542    /// Wi-Fi soft-AP mode
543    SoftAp = 2,
544    /// Wi-Fi station + soft-AP mode
545    ApStation = 3,
546}
547
548/// Start WiFi according to current configuration If mode is WIFI_MODE_STA, it creates station control block and starts station If mode is
549/// WIFI_MODE_AP, it creates soft-AP control block and starts soft-AP If mode is WIFI_MODE_APSTA, it creates soft-AP and station control
550/// block and starts soft-AP and station If mode is WIFI_MODE_NAN, it creates NAN control block and starts NAN.
551/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv414esp_wifi_startv))
552pub fn start<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
553where
554    W: FnMut(&[u8]) -> Result<(), EspError>,
555{
556    write_empty_msg(buf, write, uid, RpcId::ReqWifiStart)
557}
558
559/// Stop WiFi If mode is WIFI_MODE_STA, it stops station and frees station control block If mode is WIFI_MODE_AP,
560/// it stops soft-AP and frees soft-AP control block If mode is WIFI_MODE_APSTA, it stops station/soft-AP and frees
561/// station/soft-AP control block If mode is WIFI_MODE_NAN, it stops NAN and frees NAN control block.
562/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv413esp_wifi_stopv)
563pub fn stop<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
564where
565    W: FnMut(&[u8]) -> Result<(), EspError>,
566{
567    write_empty_msg(buf, write, uid, RpcId::ReqWifiStop)
568}
569
570/// Get number of APs found in last scan.
571/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv424esp_wifi_scan_get_ap_numP8uint16_t)
572pub fn scan_get_ap_num<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
573where
574    W: FnMut(&[u8]) -> Result<(), EspError>,
575{
576    write_empty_msg(buf, write, uid, RpcId::ReqWifiScanGetApNum)
577}
578
579/// Get one AP record from the scanned AP list.
580/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv428esp_wifi_scan_get_ap_recordsP8uint16_tP16wifi_ap_record_t)
581pub fn scan_get_ap_record<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
582where
583    W: FnMut(&[u8]) -> Result<(), EspError>,
584{
585    write_empty_msg(buf, write, uid, RpcId::ReqWifiScanGetApRecord)
586}
587
588/// Clear AP list found in last scan.
589/// This API will free all memory occupied by scanned AP list.
590/// When the obtained AP list fails, AP records must be cleared,otherwise it may cause memory leakage.
591/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv422esp_wifi_clear_ap_listv)
592pub fn clear_ap_list<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
593where
594    W: FnMut(&[u8]) -> Result<(), EspError>,
595{
596    write_empty_msg(buf, write, uid, RpcId::ReqWifiClearApList)
597}
598
599/// Retrieve the list of APs found during the last scan. The returned AP list is sorted in descending order based on RSSI.
600/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv428esp_wifi_scan_get_ap_recordsP8uint16_tP16wifi_ap_record_t)
601pub fn scan_get_ap_records<W>(
602    buf: &mut [u8],
603    mut write: W,
604    uid: u32,
605    max_number: u8,
606) -> Result<(), EspError>
607where
608    W: FnMut(&[u8]) -> Result<(), EspError>,
609{
610    let rpc = Rpc::new_req(RpcId::ReqWifiScanGetApRecords, uid);
611
612    let mut data = [0; 2];
613
614    let mut i = 0;
615    write_rpc(&mut data, 1, WireType::Varint, max_number as u64, &mut i);
616
617    let frame_len = setup_rpc(buf, &rpc, &data[..i]);
618    write(&buf[..frame_len])?;
619
620    Ok(())
621}
622
623pub fn ap_get_sta_list<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
624where
625    W: FnMut(&[u8]) -> Result<(), EspError>,
626{
627    write_empty_msg(buf, write, uid, RpcId::ReqWifiApGetStaList)
628}
629
630pub fn get_mode<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
631where
632    W: FnMut(&[u8]) -> Result<(), EspError>,
633{
634    write_empty_msg(buf, write, uid, RpcId::ReqGetWifiMode)
635}
636
637/// Options:
638/// 0: Radio off
639/// 1: Station/client: Can scan and connect
640/// 2: Soft AP; cannot scan.
641/// 3: Soft-AP and Sta (slower scan)
642/// 4: Wi-Fi aware. (Not relevant to normal scanning)
643pub fn set_mode<W>(buf: &mut [u8], mut write: W, uid: u32, mode: WifiMode) -> Result<(), EspError>
644where
645    W: FnMut(&[u8]) -> Result<(), EspError>,
646{
647    let rpc = Rpc::new_req(RpcId::ReqSetWifiMode, uid);
648
649    let mut data = [0; 2];
650
651    let mut i = 0;
652    write_rpc(&mut data, 1, WireType::Varint, mode as u64, &mut i);
653
654    let frame_len = setup_rpc(buf, &rpc, &data[..i]);
655    write(&buf[..frame_len])?;
656
657    Ok(())
658}
659
660/// Initialize WiFi Allocate resource for WiFi driver, such as WiFi control structure, RX/TX buffer,
661/// WiFi NVS structure etc. This WiFi also starts WiFi task.
662/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv413esp_wifi_initPK18wifi_init_config_t)
663pub fn init<W>(buf: &mut [u8], mut write: W, uid: u32, cfg: &InitConfig) -> Result<(), EspError>
664where
665    W: FnMut(&[u8]) -> Result<(), EspError>,
666{
667    let rpc = Rpc::new_req(RpcId::ReqWifiInit, uid);
668
669    // todo: A/R.
670    let mut data = [0; 85]; // cfg size is ~50.
671
672    let pl = RpcReqWifiInit {
673        cfg: cfg.clone(), // todo: Don't clone
674    };
675
676    let data_len = pl.to_bytes(&mut data);
677
678    let frame_len = setup_rpc(buf, &rpc, &data[..data_len]);
679    write(&buf[..frame_len])?;
680
681    Ok(())
682}
683
684/// Deinit WiFi Free all resource allocated in esp_wifi_init and stop WiFi task.
685/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv415esp_wifi_deinitv))
686pub fn deinit<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
687where
688    W: FnMut(&[u8]) -> Result<(), EspError>,
689{
690    write_empty_msg(buf, write, uid, RpcId::ReqWifiDeinit)
691}
692
693/// Promiscuous frame type.
694///
695/// Passed to promiscuous mode RX callback to indicate the type of parameter in the buffer.
696/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv427wifi_promiscuous_pkt_type_t)
697#[derive(Clone, Copy, PartialEq, Format)]
698#[repr(u8)]
699pub enum PromiscuousPktType {
700    /// Management frame
701    Mgmt = 0,
702    /// Control frame
703    Ctrl = 1,
704    /// Data frame
705    Data = 2,
706    /// Other type, such as MIMO etc.
707    Misc = 3,
708}
709
710#[derive(Format, Default)]
711/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#c.WIFI_PROMIS_FILTER_MASK_ALL)
712pub struct PromiscuousFilter {
713    pub mgmt: bool,
714    pub ctrl: bool,
715    pub data: bool,
716    pub misc: bool,
717    pub data_mpdu: bool,
718    pub data_ampdu: bool,
719    pub fcsfail: bool,
720}
721
722impl PromiscuousFilter {
723    pub fn val(&self) -> u32 {
724        let mut result = 0;
725        if self.mgmt {
726            result |= 1 << 0;
727        }
728        if self.ctrl {
729            result |= 1 << 1;
730        }
731        if self.data {
732            result |= 1 << 2;
733        }
734        if self.misc {
735            result |= 1 << 3;
736        }
737        if self.data_mpdu {
738            result |= 1 << 4;
739        }
740        if self.data_ampdu {
741            result |= 1 << 5;
742        }
743        if self.fcsfail {
744            result |= 1 << 6;
745        }
746        result
747    }
748}
749
750#[derive(Format, Default)]
751/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#c.WIFI_PROMIS_CTRL_FILTER_MASK_ALL)
752pub struct PromiscuousCtrlFilter {
753    pub wrapper: bool,
754    pub bar: bool,
755    pub ba: bool,
756    pub pspoll: bool,
757    pub rts: bool,
758    pub cts: bool,
759    pub ack: bool,
760    pub cfend: bool,
761    pub cfendack: bool,
762}
763
764impl PromiscuousCtrlFilter {
765    pub fn val(&self) -> u32 {
766        let mut result = 0;
767        if self.wrapper {
768            result |= 1 << 0;
769        }
770        if self.bar {
771            result |= 1 << 1;
772        }
773        if self.ba {
774            result |= 1 << 2;
775        }
776        if self.pspoll {
777            result |= 1 << 3;
778        }
779        if self.rts {
780            result |= 1 << 4;
781        }
782        if self.cts {
783            result |= 1 << 5;
784        }
785        if self.ack {
786            result |= 1 << 6;
787        }
788        if self.cfend {
789            result |= 1 << 7;
790        }
791        if self.cfendack {
792            result |= 1 << 8;
793        }
794        result
795    }
796}
797
798/// Enable the promiscuous mode, and set its filter.
799/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv424esp_wifi_set_promiscuousb)
800pub fn set_promiscuous<W>(
801    buf: &mut [u8],
802    mut write: W,
803    uid: u32,
804    enabled: bool,
805    filter: &PromiscuousFilter,
806    ctrl_filter: Option<&PromiscuousCtrlFilter>,
807) -> Result<(), EspError>
808where
809    W: FnMut(&[u8]) -> Result<(), EspError>,
810{
811    // todo: Where to handle setting the CB? Doesn't map neatly to RPC.
812
813    // Enable or disable
814    let rpc = Rpc::new_req(RpcId::ReqWifiSetPromiscuous, uid);
815
816    let mut data = [0; 6];
817
818    let mut i = 0;
819    write_rpc(&mut data, 1, WireType::Varint, enabled as u64, &mut i);
820
821    let frame_len = setup_rpc(buf, &rpc, &data[..i]);
822    write(&buf[..frame_len])?;
823
824    // Set its filter. This, and the ctrl filter, and single-field structs.
825    let rpc = Rpc::new_req(RpcId::ReqWifiSetPromiscuousFilter, uid);
826
827    let mut i = 0;
828    // Same buf as enable/disable.
829
830    // todo: We assume 1 byte now for each filter. This is temp.
831    write_rpc(&mut data, 1, WireType::Len, 1, &mut i);
832    write_rpc(&mut data, 1, WireType::Varint, 1, &mut i); // Len of the struct.
833
834    write_rpc(&mut data, 1, WireType::Varint, filter.val() as u64, &mut i);
835
836    let frame_len = setup_rpc(buf, &rpc, &data[..i]);
837    write(&buf[..frame_len])?;
838
839    // Set its ctrl-mode filter A/R
840    if let Some(f) = ctrl_filter {
841        let rpc = Rpc::new_req(RpcId::ReqWifiSetPromiscuousCtrlFilter, uid);
842
843        let mut i = 0;
844
845        write_rpc(&mut data, 1, WireType::Len, 1, &mut i);
846        write_rpc(&mut data, 1, WireType::Varint, 1, &mut i); // Len of the struct.
847
848        write_rpc(&mut data, 1, WireType::Varint, f.val() as u64, &mut i);
849
850        let frame_len = setup_rpc(buf, &rpc, &data[..i]);
851        write(&buf[..frame_len])?;
852    }
853
854    Ok(())
855}
856
857/// Get the promiscuous mode.
858/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv424esp_wifi_get_promiscuousPb)
859pub fn get_promiscuous<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
860where
861    W: FnMut(&[u8]) -> Result<(), EspError>,
862{
863    write_empty_msg(buf, write, uid, RpcId::ReqWifiGetPromiscuous)
864}
865
866/// Get the promiscuous filter.
867/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv431esp_wifi_get_promiscuous_filterP25wifi_promiscuous_filter_t)
868pub fn get_promiscuous_filter<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
869where
870    W: FnMut(&[u8]) -> Result<(), EspError>,
871{
872    write_empty_msg(buf, write, uid, RpcId::ReqWifiGetPromiscuousFilter)
873}
874
875/// Get the subtype filter of the control packet in promiscuous mode.
876/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv436esp_wifi_get_promiscuous_ctrl_filterP25wifi_promiscuous_filter_t)
877pub fn get_promiscuous_ctrl_filter<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
878where
879    W: FnMut(&[u8]) -> Result<(), EspError>,
880{
881    write_empty_msg(buf, write, uid, RpcId::ReqWifiGetPromiscuousCtrlFilter)
882}
883
884/// Scan all available APs.
885/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv419esp_wifi_scan_startPK18wifi_scan_config_tb)
886pub fn scan_start<W>(
887    buf: &mut [u8],
888    mut write: W,
889    uid: u32,
890    scan_start: &RpcReqWifiScanStart,
891) -> Result<(), EspError>
892where
893    W: FnMut(&[u8]) -> Result<(), EspError>,
894{
895    let rpc = Rpc::new_req(RpcId::ReqWifiScanStart, uid);
896
897    let mut data = [0; 100];
898    let data_size = scan_start.to_bytes(&mut data);
899
900    let frame_len = setup_rpc(buf, &rpc, &data[..data_size]);
901    write(&buf[..frame_len])?;
902
903    Ok(())
904}
905
906/// Stop the scan in process.
907/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv418esp_wifi_scan_stopv)
908pub fn scan_stop<W>(buf: &mut [u8], write: W, uid: u32) -> Result<(), EspError>
909where
910    W: FnMut(&[u8]) -> Result<(), EspError>,
911{
912    write_empty_msg(buf, write, uid, RpcId::ReqWifiScanStop)
913}
914
915#[derive(Format)]
916pub struct Protocols {
917    /// 802.11b
918    pub p_11b: bool,
919    /// 802.11g
920    pub p_11g: bool,
921    /// 802.11n
922    pub p_11n: bool,
923    /// Long range
924    pub p_lr: bool,
925    /// 802.11ax
926    pub p_11ax: bool,
927    /// 802.11ax
928    pub wps: bool,
929    /// 802.11a
930    pub p_11a: bool,
931    pub p_11ac: bool,
932}
933
934impl Default for Protocols {
935    /// This matches the ESP default.
936    fn default() -> Self {
937        Self {
938            p_11b: true,
939            p_11g: true,
940            p_11n: true,
941            p_lr: false,
942            p_11ax: false,
943            wps: false,
944            p_11a: false,
945            p_11ac: false,
946            // todo: 802.1ac and ax A/R. 0x20 and 0x40 rep
947        }
948    }
949}
950
951impl Protocols {
952    pub fn to_byte(&self) -> u8 {
953        (self.p_11b as u8)
954            | ((self.p_11g as u8) << 1)
955            | ((self.p_11n as u8) << 2)
956            | ((self.p_lr as u8) << 3)
957            | ((self.p_11ac as u8) << 5)
958            | ((self.p_11ax as u8) << 6)
959    }
960
961    pub fn from_byte(b: u8) -> Self {
962        Self {
963            p_11b: b & 1 != 0,
964            p_11g: (b >> 1) & 1 != 0,
965            p_11n: (b >> 2) & 1 != 0,
966            p_lr: (b >> 3) & 1 != 0,
967            p_11ax: (b >> 4) & 1 != 0,
968            wps: (b >> 5) & 1 != 0,
969            p_11a: false,
970            p_11ac: false,
971            // todo?
972            // p_11a: (b >> 8) & 1 != 0,
973            // p_11ac: (b >> 9) & 1 != 0,
974        }
975    }
976}
977
978/// Set the supported WiFi protocols for the specified interface. The default protocol is
979/// (WIFI_PROTOCOL_11B|WIFI_PROTOCOL_11G|WIFI_PROTOCOL_11N).
980/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv422esp_wifi_set_protocols16wifi_interface_tP16wifi_protocols_t)
981/// interface (ifx) should be 0 for Station, and 1 for Ap.
982/// Bitmap: e.g 1 | 2 | 4; = 11B | 11G | 11N. Note that this is the default.
983pub fn set_protocol<W>(
984    buf: &mut [u8],
985    mut write: W,
986    uid: u32,
987    ifx: InterfaceType,
988    protocols: &Protocols,
989) -> Result<(), EspError>
990where
991    W: FnMut(&[u8]) -> Result<(), EspError>,
992{
993    let rpc = Rpc::new_req(RpcId::ReqWifiSetProtocol, uid);
994
995    let mut data = [0; 4];
996    let mut i = 0;
997
998    write_rpc(&mut data, 1, WireType::Varint, ifx as u64, &mut i);
999    write_rpc(
1000        &mut data,
1001        2,
1002        WireType::Varint,
1003        protocols.to_byte() as u64,
1004        &mut i,
1005    );
1006
1007    let frame_len = setup_rpc(buf, &rpc, &data[0..i]);
1008    write(&buf[..frame_len])?;
1009
1010    Ok(())
1011}
1012
1013/// Get the current protocol bitmap of the specified interface.
1014/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv421esp_wifi_get_protocol16wifi_interface_tP7uint8_t)
1015pub fn get_protocol<W>(buf: &mut [u8], mut write: W, uid: u32) -> Result<(), EspError>
1016where
1017    W: FnMut(&[u8]) -> Result<(), EspError>,
1018{
1019    let rpc = Rpc::new_req(RpcId::ReqWifiGetProtocol, uid);
1020
1021    let mut data = [0; 4];
1022    let mut i = 0;
1023
1024    let interface_num = 0; // todo?
1025    write_rpc(&mut data, 1, WireType::Varint, interface_num, &mut i);
1026
1027    let frame_len = setup_rpc(buf, &rpc, &data[..i]);
1028    write(&buf[..frame_len])?;
1029
1030    Ok(())
1031}
1032
1033/// Set current WiFi power save type.
1034/// [docs](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_wifi.html#_CPPv415esp_wifi_set_ps14wifi_ps_type_t)
1035pub fn set_ps<W>(buf: &mut [u8], mut write: W, uid: u32) -> Result<(), EspError>
1036where
1037    W: FnMut(&[u8]) -> Result<(), EspError>,
1038{
1039    let rpc = Rpc::new_req(RpcId::ReqWifiGetProtocol, uid);
1040
1041    let mut data = [0; 4];
1042    let mut i = 0;
1043
1044    let interface_num = 0; // todo?
1045    write_rpc(&mut data, 1, WireType::Varint, interface_num, &mut i);
1046
1047    let frame_len = setup_rpc(buf, &rpc, &data[..i]);
1048    write(&buf[..frame_len])?;
1049
1050    Ok(())
1051}