From 6c123596b7d48ee66ea93e8b1515e91231e9bced Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 9 Jun 2023 03:36:48 +0200 Subject: [PATCH 1/8] wip: esp-hosted net driver. --- embassy-net-esp-hosted/Cargo.toml | 20 + embassy-net-esp-hosted/src/control.rs | 141 +++++ .../src/esp_hosted_config.proto | 432 +++++++++++++ embassy-net-esp-hosted/src/fmt.rs | 257 ++++++++ embassy-net-esp-hosted/src/ioctl.rs | 119 ++++ embassy-net-esp-hosted/src/lib.rs | 300 +++++++++ embassy-net-esp-hosted/src/proto.rs | 598 ++++++++++++++++++ examples/nrf52840/Cargo.toml | 2 + examples/nrf52840/src/bin/wifi_esp_hosted.rs | 143 +++++ 9 files changed, 2012 insertions(+) create mode 100644 embassy-net-esp-hosted/Cargo.toml create mode 100644 embassy-net-esp-hosted/src/control.rs create mode 100644 embassy-net-esp-hosted/src/esp_hosted_config.proto create mode 100644 embassy-net-esp-hosted/src/fmt.rs create mode 100644 embassy-net-esp-hosted/src/ioctl.rs create mode 100644 embassy-net-esp-hosted/src/lib.rs create mode 100644 embassy-net-esp-hosted/src/proto.rs create mode 100644 examples/nrf52840/src/bin/wifi_esp_hosted.rs diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml new file mode 100644 index 000000000..a7e18ee09 --- /dev/null +++ b/embassy-net-esp-hosted/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "embassy-net-esp-hosted" +version = "0.1.0" +edition = "2021" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-sync = { version = "0.2.0", path = "../embassy-sync"} +embassy-futures = { version = "0.1.0", path = "../embassy-futures"} +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} + +embedded-hal = { version = "1.0.0-alpha.10" } +embedded-hal-async = { version = "=0.2.0-alpha.1" } + +noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } +#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } +heapless = "0.7.16" diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs new file mode 100644 index 000000000..2381c6b84 --- /dev/null +++ b/embassy-net-esp-hosted/src/control.rs @@ -0,0 +1,141 @@ +use ch::driver::LinkState; +use defmt::Debug2Format; +use embassy_net_driver_channel as ch; +use heapless::String; + +use crate::ioctl::IoctlState; +use crate::proto::{self, CtrlMsg}; + +#[derive(Debug)] +pub struct Error { + pub status: u32, +} + +pub struct Control<'a> { + state_ch: ch::StateRunner<'a>, + ioctl_state: &'a IoctlState, +} + +enum WifiMode { + None = 0, + Sta = 1, + Ap = 2, + ApSta = 3, +} + +impl<'a> Control<'a> { + pub(crate) fn new(state_ch: ch::StateRunner<'a>, ioctl_state: &'a IoctlState) -> Self { + Self { state_ch, ioctl_state } + } + + pub async fn init(&mut self) { + debug!("set wifi mode"); + self.set_wifi_mode(WifiMode::Sta as _).await; + let mac_addr = self.get_mac_addr().await; + debug!("mac addr: {:02x}", mac_addr); + self.state_ch.set_ethernet_address(mac_addr); + } + + pub async fn join(&mut self, ssid: &str, password: &str) { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqConnectAp as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { + ssid: String::from(ssid), + pwd: String::from(password), + bssid: String::new(), + listen_interval: 3, + is_wpa3_supported: false, + })), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + debug!("======= {:?}", Debug2Format(&resp)); + assert_eq!(resp.resp, 0); + self.state_ch.set_link_state(LinkState::Up); + } + + async fn get_mac_addr(&mut self) -> [u8; 6] { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( + proto::CtrlMsgReqGetMacAddress { + mode: WifiMode::Sta as _, + }, + )), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + assert_eq!(resp.resp, 0); + + // WHY IS THIS A STRING? WHYYYY + fn nibble_from_hex(b: u8) -> u8 { + match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => b + 0xa - b'a', + b'A'..=b'F' => b + 0xa - b'A', + _ => panic!("invalid hex digit {}", b), + } + } + + let mac = resp.mac.as_bytes(); + let mut res = [0; 6]; + assert_eq!(mac.len(), 17); + for (i, b) in res.iter_mut().enumerate() { + *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) + } + res + } + + async fn get_wifi_mode(&mut self) -> u32 { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqGetWifiMode as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqGetWifiMode(proto::CtrlMsgReqGetMode {})), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespGetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + assert_eq!(resp.resp, 0); + resp.mode + } + + async fn set_wifi_mode(&mut self, mode: u32) { + let req = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), + }; + let resp = self.ioctl(req).await; + let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + assert_eq!(resp.resp, 0); + } + + async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { + let mut buf = [0u8; 128]; + + let req_len = noproto::write(&req, &mut buf).unwrap(); + + 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(&mut buf, req_len).await; + + ioctl.defuse(); + + noproto::read(&buf[..resp_len]).unwrap() + } +} diff --git a/embassy-net-esp-hosted/src/esp_hosted_config.proto b/embassy-net-esp-hosted/src/esp_hosted_config.proto new file mode 100644 index 000000000..aa1bfde64 --- /dev/null +++ b/embassy-net-esp-hosted/src/esp_hosted_config.proto @@ -0,0 +1,432 @@ +syntax = "proto3"; + +/* Enums similar to ESP IDF */ +enum Ctrl_VendorIEType { + Beacon = 0; + Probe_req = 1; + Probe_resp = 2; + Assoc_req = 3; + Assoc_resp = 4; +} + +enum Ctrl_VendorIEID { + ID_0 = 0; + ID_1 = 1; +} + +enum Ctrl_WifiMode { + NONE = 0; + STA = 1; + AP = 2; + APSTA = 3; +} + +enum Ctrl_WifiBw { + BW_Invalid = 0; + HT20 = 1; + HT40 = 2; +} + +enum Ctrl_WifiPowerSave { + PS_Invalid = 0; + MIN_MODEM = 1; + MAX_MODEM = 2; +} + +enum Ctrl_WifiSecProt { + Open = 0; + WEP = 1; + WPA_PSK = 2; + WPA2_PSK = 3; + WPA_WPA2_PSK = 4; + WPA2_ENTERPRISE = 5; + WPA3_PSK = 6; + WPA2_WPA3_PSK = 7; +} + +/* enums for Control path */ +enum Ctrl_Status { + Connected = 0; + Not_Connected = 1; + No_AP_Found = 2; + Connection_Fail = 3; + Invalid_Argument = 4; + Out_Of_Range = 5; +} + + +enum CtrlMsgType { + MsgType_Invalid = 0; + Req = 1; + Resp = 2; + Event = 3; + MsgType_Max = 4; +} + +enum CtrlMsgId { + MsgId_Invalid = 0; + + /** Request Msgs **/ + Req_Base = 100; + + Req_GetMACAddress = 101; + Req_SetMacAddress = 102; + Req_GetWifiMode = 103; + Req_SetWifiMode = 104; + + Req_GetAPScanList = 105; + Req_GetAPConfig = 106; + Req_ConnectAP = 107; + Req_DisconnectAP = 108; + + Req_GetSoftAPConfig = 109; + Req_SetSoftAPVendorSpecificIE = 110; + Req_StartSoftAP = 111; + Req_GetSoftAPConnectedSTAList = 112; + Req_StopSoftAP = 113; + + Req_SetPowerSaveMode = 114; + Req_GetPowerSaveMode = 115; + + Req_OTABegin = 116; + Req_OTAWrite = 117; + Req_OTAEnd = 118; + + Req_SetWifiMaxTxPower = 119; + Req_GetWifiCurrTxPower = 120; + + Req_ConfigHeartbeat = 121; + /* Add new control path command response before Req_Max + * and update Req_Max */ + Req_Max = 122; + + /** Response Msgs **/ + Resp_Base = 200; + + Resp_GetMACAddress = 201; + Resp_SetMacAddress = 202; + Resp_GetWifiMode = 203; + Resp_SetWifiMode = 204; + + Resp_GetAPScanList = 205; + Resp_GetAPConfig = 206; + Resp_ConnectAP = 207; + Resp_DisconnectAP = 208; + + Resp_GetSoftAPConfig = 209; + Resp_SetSoftAPVendorSpecificIE = 210; + Resp_StartSoftAP = 211; + Resp_GetSoftAPConnectedSTAList = 212; + Resp_StopSoftAP = 213; + + Resp_SetPowerSaveMode = 214; + Resp_GetPowerSaveMode = 215; + + Resp_OTABegin = 216; + Resp_OTAWrite = 217; + Resp_OTAEnd = 218; + + Resp_SetWifiMaxTxPower = 219; + Resp_GetWifiCurrTxPower = 220; + + Resp_ConfigHeartbeat = 221; + /* Add new control path command response before Resp_Max + * and update Resp_Max */ + Resp_Max = 222; + + /** Event Msgs **/ + Event_Base = 300; + Event_ESPInit = 301; + Event_Heartbeat = 302; + Event_StationDisconnectFromAP = 303; + Event_StationDisconnectFromESPSoftAP = 304; + /* Add new control path command notification before Event_Max + * and update Event_Max */ + Event_Max = 305; +} + +/* internal supporting structures for CtrlMsg */ +message ScanResult { + bytes ssid = 1; + uint32 chnl = 2; + int32 rssi = 3; + bytes bssid = 4; + Ctrl_WifiSecProt sec_prot = 5; +} + +message ConnectedSTAList { + bytes mac = 1; + int32 rssi = 2; +} + + +/* Control path structures */ +/** Req/Resp structure **/ +message CtrlMsg_Req_GetMacAddress { + int32 mode = 1; +} + +message CtrlMsg_Resp_GetMacAddress { + bytes mac = 1; + int32 resp = 2; +} + +message CtrlMsg_Req_GetMode { +} + +message CtrlMsg_Resp_GetMode { + int32 mode = 1; + int32 resp = 2; +} + +message CtrlMsg_Req_SetMode { + int32 mode = 1; +} + +message CtrlMsg_Resp_SetMode { + int32 resp = 1; +} + +message CtrlMsg_Req_GetStatus { +} + +message CtrlMsg_Resp_GetStatus { + int32 resp = 1; +} + +message CtrlMsg_Req_SetMacAddress { + bytes mac = 1; + int32 mode = 2; +} + +message CtrlMsg_Resp_SetMacAddress { + int32 resp = 1; +} + +message CtrlMsg_Req_GetAPConfig { +} + +message CtrlMsg_Resp_GetAPConfig { + bytes ssid = 1; + bytes bssid = 2; + int32 rssi = 3; + int32 chnl = 4; + Ctrl_WifiSecProt sec_prot = 5; + int32 resp = 6; +} + +message CtrlMsg_Req_ConnectAP { + string ssid = 1; + string pwd = 2; + string bssid = 3; + bool is_wpa3_supported = 4; + int32 listen_interval = 5; +} + +message CtrlMsg_Resp_ConnectAP { + int32 resp = 1; + bytes mac = 2; +} + +message CtrlMsg_Req_GetSoftAPConfig { +} + +message CtrlMsg_Resp_GetSoftAPConfig { + bytes ssid = 1; + bytes pwd = 2; + int32 chnl = 3; + Ctrl_WifiSecProt sec_prot = 4; + int32 max_conn = 5; + bool ssid_hidden = 6; + int32 bw = 7; + int32 resp = 8; +} + +message CtrlMsg_Req_StartSoftAP { + string ssid = 1; + string pwd = 2; + int32 chnl = 3; + Ctrl_WifiSecProt sec_prot = 4; + int32 max_conn = 5; + bool ssid_hidden = 6; + int32 bw = 7; +} + +message CtrlMsg_Resp_StartSoftAP { + int32 resp = 1; + bytes mac = 2; +} + +message CtrlMsg_Req_ScanResult { +} + +message CtrlMsg_Resp_ScanResult { + uint32 count = 1; + repeated ScanResult entries = 2; + int32 resp = 3; +} + +message CtrlMsg_Req_SoftAPConnectedSTA { +} + +message CtrlMsg_Resp_SoftAPConnectedSTA { + uint32 num = 1; + repeated ConnectedSTAList stations = 2; + int32 resp = 3; +} + +message CtrlMsg_Req_OTABegin { +} + +message CtrlMsg_Resp_OTABegin { + int32 resp = 1; +} + +message CtrlMsg_Req_OTAWrite { + bytes ota_data = 1; +} + +message CtrlMsg_Resp_OTAWrite { + int32 resp = 1; +} + +message CtrlMsg_Req_OTAEnd { +} + +message CtrlMsg_Resp_OTAEnd { + int32 resp = 1; +} + +message CtrlMsg_Req_VendorIEData { + int32 element_id = 1; + int32 length = 2; + bytes vendor_oui = 3; + int32 vendor_oui_type = 4; + bytes payload = 5; +} + +message CtrlMsg_Req_SetSoftAPVendorSpecificIE { + bool enable = 1; + Ctrl_VendorIEType type = 2; + Ctrl_VendorIEID idx = 3; + CtrlMsg_Req_VendorIEData vendor_ie_data = 4; +} + +message CtrlMsg_Resp_SetSoftAPVendorSpecificIE { + int32 resp = 1; +} + +message CtrlMsg_Req_SetWifiMaxTxPower { + int32 wifi_max_tx_power = 1; +} + +message CtrlMsg_Resp_SetWifiMaxTxPower { + int32 resp = 1; +} + +message CtrlMsg_Req_GetWifiCurrTxPower { +} + +message CtrlMsg_Resp_GetWifiCurrTxPower { + int32 wifi_curr_tx_power = 1; + int32 resp = 2; +} + +message CtrlMsg_Req_ConfigHeartbeat { + bool enable = 1; + int32 duration = 2; +} + +message CtrlMsg_Resp_ConfigHeartbeat { + int32 resp = 1; +} + +/** Event structure **/ +message CtrlMsg_Event_ESPInit { + bytes init_data = 1; +} + +message CtrlMsg_Event_Heartbeat { + int32 hb_num = 1; +} + +message CtrlMsg_Event_StationDisconnectFromAP { + int32 resp = 1; +} + +message CtrlMsg_Event_StationDisconnectFromESPSoftAP { + int32 resp = 1; + bytes mac = 2; +} + +message CtrlMsg { + /* msg_type could be req, resp or Event */ + CtrlMsgType msg_type = 1; + + /* msg id */ + CtrlMsgId msg_id = 2; + + /* union of all msg ids */ + oneof payload { + /** Requests **/ + CtrlMsg_Req_GetMacAddress req_get_mac_address = 101; + CtrlMsg_Req_SetMacAddress req_set_mac_address = 102; + CtrlMsg_Req_GetMode req_get_wifi_mode = 103; + CtrlMsg_Req_SetMode req_set_wifi_mode = 104; + + CtrlMsg_Req_ScanResult req_scan_ap_list = 105; + CtrlMsg_Req_GetAPConfig req_get_ap_config = 106; + CtrlMsg_Req_ConnectAP req_connect_ap = 107; + CtrlMsg_Req_GetStatus req_disconnect_ap = 108; + + CtrlMsg_Req_GetSoftAPConfig req_get_softap_config = 109; + CtrlMsg_Req_SetSoftAPVendorSpecificIE req_set_softap_vendor_specific_ie = 110; + CtrlMsg_Req_StartSoftAP req_start_softap = 111; + CtrlMsg_Req_SoftAPConnectedSTA req_softap_connected_stas_list = 112; + CtrlMsg_Req_GetStatus req_stop_softap = 113; + + CtrlMsg_Req_SetMode req_set_power_save_mode = 114; + CtrlMsg_Req_GetMode req_get_power_save_mode = 115; + + CtrlMsg_Req_OTABegin req_ota_begin = 116; + CtrlMsg_Req_OTAWrite req_ota_write = 117; + CtrlMsg_Req_OTAEnd req_ota_end = 118; + + CtrlMsg_Req_SetWifiMaxTxPower req_set_wifi_max_tx_power = 119; + CtrlMsg_Req_GetWifiCurrTxPower req_get_wifi_curr_tx_power = 120; + CtrlMsg_Req_ConfigHeartbeat req_config_heartbeat = 121; + + /** Responses **/ + CtrlMsg_Resp_GetMacAddress resp_get_mac_address = 201; + CtrlMsg_Resp_SetMacAddress resp_set_mac_address = 202; + CtrlMsg_Resp_GetMode resp_get_wifi_mode = 203; + CtrlMsg_Resp_SetMode resp_set_wifi_mode = 204; + + CtrlMsg_Resp_ScanResult resp_scan_ap_list = 205; + CtrlMsg_Resp_GetAPConfig resp_get_ap_config = 206; + CtrlMsg_Resp_ConnectAP resp_connect_ap = 207; + CtrlMsg_Resp_GetStatus resp_disconnect_ap = 208; + + CtrlMsg_Resp_GetSoftAPConfig resp_get_softap_config = 209; + CtrlMsg_Resp_SetSoftAPVendorSpecificIE resp_set_softap_vendor_specific_ie = 210; + CtrlMsg_Resp_StartSoftAP resp_start_softap = 211; + CtrlMsg_Resp_SoftAPConnectedSTA resp_softap_connected_stas_list = 212; + CtrlMsg_Resp_GetStatus resp_stop_softap = 213; + + CtrlMsg_Resp_SetMode resp_set_power_save_mode = 214; + CtrlMsg_Resp_GetMode resp_get_power_save_mode = 215; + + CtrlMsg_Resp_OTABegin resp_ota_begin = 216; + CtrlMsg_Resp_OTAWrite resp_ota_write = 217; + CtrlMsg_Resp_OTAEnd resp_ota_end = 218; + CtrlMsg_Resp_SetWifiMaxTxPower resp_set_wifi_max_tx_power = 219; + CtrlMsg_Resp_GetWifiCurrTxPower resp_get_wifi_curr_tx_power = 220; + CtrlMsg_Resp_ConfigHeartbeat resp_config_heartbeat = 221; + + /** Notifications **/ + CtrlMsg_Event_ESPInit event_esp_init = 301; + CtrlMsg_Event_Heartbeat event_heartbeat = 302; + CtrlMsg_Event_StationDisconnectFromAP event_station_disconnect_from_AP = 303; + CtrlMsg_Event_StationDisconnectFromESPSoftAP event_station_disconnect_from_ESP_SoftAP = 304; + } +} diff --git a/embassy-net-esp-hosted/src/fmt.rs b/embassy-net-esp-hosted/src/fmt.rs new file mode 100644 index 000000000..91984bde1 --- /dev/null +++ b/embassy-net-esp-hosted/src/fmt.rs @@ -0,0 +1,257 @@ +#![macro_use] +#![allow(unused_macros)] + +use core::fmt::{Debug, Display, LowerHex}; + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unreachable { + ($($x:tt)*) => { + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*); + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} + +pub struct Bytes<'a>(pub &'a [u8]); + +impl<'a> Debug for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> Display for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +impl<'a> LowerHex for Bytes<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#02x?}", self.0) + } +} + +#[cfg(feature = "defmt")] +impl<'a> defmt::Format for Bytes<'a> { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "{:02x}", self.0) + } +} diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs new file mode 100644 index 000000000..59bdabf37 --- /dev/null +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -0,0 +1,119 @@ +use core::cell::{Cell, RefCell}; +use core::future::poll_fn; +use core::task::{Poll, Waker}; + +use embassy_sync::waitqueue::WakerRegistration; + +use crate::fmt::Bytes; + +#[derive(Clone, Copy)] +pub struct PendingIoctl { + pub buf: *mut [u8], + pub req_len: usize, +} + +#[derive(Clone, Copy)] +enum IoctlStateInner { + Pending(PendingIoctl), + Sent { buf: *mut [u8] }, + Done { resp_len: usize }, +} + +struct Wakers { + control: WakerRegistration, + runner: WakerRegistration, +} + +impl Default for Wakers { + fn default() -> Self { + Self { + control: WakerRegistration::new(), + runner: WakerRegistration::new(), + } + } +} + +pub struct IoctlState { + state: Cell, + wakers: RefCell, +} + +impl IoctlState { + pub fn new() -> Self { + Self { + state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), + wakers: Default::default(), + } + } + + fn wake_control(&self) { + self.wakers.borrow_mut().control.wake(); + } + + fn register_control(&self, waker: &Waker) { + self.wakers.borrow_mut().control.register(waker); + } + + fn wake_runner(&self) { + self.wakers.borrow_mut().runner.wake(); + } + + fn register_runner(&self, waker: &Waker) { + self.wakers.borrow_mut().runner.register(waker); + } + + pub async fn wait_complete(&self) -> usize { + poll_fn(|cx| { + if let IoctlStateInner::Done { resp_len } = self.state.get() { + Poll::Ready(resp_len) + } else { + self.register_control(cx.waker()); + Poll::Pending + } + }) + .await + } + + pub async fn wait_pending(&self) -> PendingIoctl { + let pending = poll_fn(|cx| { + if let IoctlStateInner::Pending(pending) = self.state.get() { + Poll::Ready(pending) + } else { + self.register_runner(cx.waker()); + Poll::Pending + } + }) + .await; + + self.state.set(IoctlStateInner::Sent { buf: pending.buf }); + pending + } + + pub fn cancel_ioctl(&self) { + self.state.set(IoctlStateInner::Done { resp_len: 0 }); + } + + pub async fn do_ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { + debug!("IOCTL Request: {:02x}", Bytes(&buf[..req_len])); + + self.state.set(IoctlStateInner::Pending(PendingIoctl { buf, req_len })); + self.wake_runner(); + self.wait_complete().await + } + + pub fn ioctl_done(&self, response: &[u8]) { + if let IoctlStateInner::Sent { buf } = self.state.get() { + debug!("IOCTL Response: {:02x}", Bytes(response)); + + // TODO fix this + (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); + + self.state.set(IoctlStateInner::Done { + resp_len: response.len(), + }); + self.wake_control(); + } else { + warn!("IOCTL Response but no pending Ioctl"); + } + } +} diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs new file mode 100644 index 000000000..2cf05a7df --- /dev/null +++ b/embassy-net-esp-hosted/src/lib.rs @@ -0,0 +1,300 @@ +#![no_std] + +use control::Control; +use embassy_futures::select::{select3, Either3}; +use embassy_net_driver_channel as ch; +use embassy_time::{Duration, Instant, Timer}; +use embedded_hal::digital::{InputPin, OutputPin}; +use embedded_hal_async::digital::Wait; +use embedded_hal_async::spi::SpiDevice; +use ioctl::IoctlState; + +use crate::ioctl::PendingIoctl; + +mod proto; + +// must be first +mod fmt; + +mod control; +mod ioctl; + +const MTU: usize = 1514; + +macro_rules! impl_bytes { + ($t:ident) => { + impl $t { + pub const SIZE: usize = core::mem::size_of::(); + + #[allow(unused)] + pub fn to_bytes(&self) -> [u8; Self::SIZE] { + unsafe { core::mem::transmute(*self) } + } + + #[allow(unused)] + pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self { + let alignment = core::mem::align_of::(); + assert_eq!( + bytes.as_ptr().align_offset(alignment), + 0, + "{} is not aligned", + core::any::type_name::() + ); + unsafe { core::mem::transmute(bytes) } + } + + #[allow(unused)] + pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self { + let alignment = core::mem::align_of::(); + assert_eq!( + bytes.as_ptr().align_offset(alignment), + 0, + "{} is not aligned", + core::any::type_name::() + ); + + unsafe { core::mem::transmute(bytes) } + } + } + }; +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug, Default)] +struct PayloadHeader { + /// InterfaceType on lower 4 bits, number on higher 4 bits. + if_type_and_num: u8, + + /// Flags. + /// + /// bit 0: more fragments. + flags: u8, + + len: u16, + offset: u16, + checksum: u16, + seq_num: u16, + reserved2: u8, + + /// Packet type for HCI or PRIV interface, reserved otherwise + hci_priv_packet_type: u8, +} +impl_bytes!(PayloadHeader); + +#[repr(u8)] +enum InterfaceType { + Sta = 0, + Ap = 1, + Serial = 2, + Hci = 3, + Priv = 4, + Test = 5, +} + +const MAX_SPI_BUFFER_SIZE: usize = 1600; + +pub struct State { + ioctl_state: IoctlState, + ch: ch::State, +} + +impl State { + pub fn new() -> Self { + Self { + ioctl_state: IoctlState::new(), + ch: ch::State::new(), + } + } +} + +pub type NetDriver<'a> = ch::Device<'a, MTU>; + +pub async fn new<'a, SPI, IN, OUT>( + state: &'a mut State, + spi: SPI, + handshake: IN, + ready: IN, + reset: OUT, +) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>) +where + SPI: SpiDevice, + IN: InputPin + Wait, + OUT: OutputPin, +{ + let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let state_ch = ch_runner.state_runner(); + + let mut runner = Runner { + ch: ch_runner, + ioctl_state: &state.ioctl_state, + next_seq: 1, + handshake, + ready, + reset, + spi, + }; + runner.init().await; + + (device, Control::new(state_ch, &state.ioctl_state), runner) +} + +pub struct Runner<'a, SPI, IN, OUT> { + ch: ch::Runner<'a, MTU>, + ioctl_state: &'a IoctlState, + + next_seq: u16, + + spi: SPI, + handshake: IN, + ready: IN, + reset: OUT, +} + +impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT> +where + SPI: SpiDevice, + IN: InputPin + Wait, + OUT: OutputPin, +{ + async fn init(&mut self) {} + + pub async fn run(mut self) -> ! { + debug!("resetting..."); + self.reset.set_low().unwrap(); + Timer::after(Duration::from_millis(100)).await; + self.reset.set_high().unwrap(); + Timer::after(Duration::from_millis(1000)).await; + + let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; + let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; + + loop { + self.handshake.wait_for_high().await.unwrap(); + + let ioctl = self.ioctl_state.wait_pending(); + let tx = self.ch.tx_buf(); + let ev = async { self.ready.wait_for_high().await.unwrap() }; + + match select3(ioctl, tx, ev).await { + Either3::First(PendingIoctl { buf, req_len }) => { + tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); + tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); + tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); + + let mut header = PayloadHeader { + if_type_and_num: InterfaceType::Serial as _, + len: (req_len + 14) as _, + offset: PayloadHeader::SIZE as _, + seq_num: self.next_seq, + ..Default::default() + }; + self.next_seq = self.next_seq.wrapping_add(1); + + // Calculate checksum + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + header.checksum = checksum(&tx_buf[..26 + req_len]); + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + + debug!("====== SENDING IOCTL"); + } + Either3::Second(packet) => { + tx_buf[12..][..packet.len()].copy_from_slice(packet); + + let mut header = PayloadHeader { + if_type_and_num: InterfaceType::Sta as _, + len: packet.len() as _, + offset: PayloadHeader::SIZE as _, + seq_num: self.next_seq, + ..Default::default() + }; + self.next_seq = self.next_seq.wrapping_add(1); + + // Calculate checksum + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + header.checksum = checksum(&tx_buf[..12 + packet.len()]); + tx_buf[0..12].copy_from_slice(&header.to_bytes()); + + self.ch.tx_done(); + } + Either3::Third(()) => { + tx_buf[..PayloadHeader::SIZE].fill(0); + } + } + + if tx_buf[0] != 0 { + trace!("tx: {:02x}", &tx_buf[..40]); + } + + self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + let delay_until = Instant::now() + Duration::from_millis(1); + self.handle_rx(&mut rx_buf); + Timer::at(delay_until).await; + } + } + + fn handle_rx(&mut self, buf: &mut [u8]) { + trace!("rx: {:02x}", &buf[..40]); + + let buf_len = buf.len(); + let h = PayloadHeader::from_bytes_mut((&mut buf[..PayloadHeader::SIZE]).try_into().unwrap()); + + if h.len == 0 || h.offset as usize != PayloadHeader::SIZE { + return; + } + + let payload_len = h.len as usize; + if buf_len < PayloadHeader::SIZE + payload_len { + warn!("rx: len too big"); + return; + } + + let if_type_and_num = h.if_type_and_num; + let want_checksum = h.checksum; + h.checksum = 0; + let got_checksum = checksum(&buf[..PayloadHeader::SIZE + payload_len]); + if want_checksum != got_checksum { + warn!("rx: bad checksum. Got {:04x}, want {:04x}", got_checksum, want_checksum); + return; + } + + let payload = &mut buf[PayloadHeader::SIZE..][..payload_len]; + + match if_type_and_num & 0x0f { + // STA + 0 => match self.ch.try_rx_buf() { + Some(buf) => { + buf[..payload.len()].copy_from_slice(payload); + self.ch.rx_done(payload.len()) + } + None => warn!("failed to push rxd packet to the channel."), + }, + // serial + 2 => { + debug!("serial rx: {:02x}", payload); + if payload.len() < 14 { + warn!("serial rx: too short"); + return; + } + if &payload[..12] != b"\x01\x08\x00ctrlResp\x02" { + warn!("serial rx: bad tlv"); + return; + } + let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize; + if payload.len() < 14 + len { + warn!("serial rx: too short 2"); + return; + } + self.ioctl_state.ioctl_done(&payload[14..][..len]); + } + _ => warn!("unknown iftype {}", if_type_and_num), + } + } +} + +fn checksum(buf: &[u8]) -> u16 { + let mut res = 0u16; + for &b in buf { + res = res.wrapping_add(b as _); + } + res +} diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs new file mode 100644 index 000000000..e105e393c --- /dev/null +++ b/embassy-net-esp-hosted/src/proto.rs @@ -0,0 +1,598 @@ +use heapless::{String, Vec}; + +/// internal supporting structures for CtrlMsg + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct ScanResult { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub chnl: u32, + #[noproto(tag = "3")] + pub rssi: u32, + #[noproto(tag = "4")] + pub bssid: String<32>, + #[noproto(tag = "5")] + pub sec_prot: CtrlWifiSecProt, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct ConnectedStaList { + #[noproto(tag = "1")] + pub mac: String<32>, + #[noproto(tag = "2")] + pub rssi: u32, +} +/// * Req/Resp structure * + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetMacAddress { + #[noproto(tag = "1")] + pub mode: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetMacAddress { + #[noproto(tag = "1")] + pub mac: String<32>, + #[noproto(tag = "2")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetMode {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetMode { + #[noproto(tag = "1")] + pub mode: u32, + #[noproto(tag = "2")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetMode { + #[noproto(tag = "1")] + pub mode: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetMode { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetStatus {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetStatus { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetMacAddress { + #[noproto(tag = "1")] + pub mac: String<32>, + #[noproto(tag = "2")] + pub mode: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetMacAddress { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetApConfig {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetApConfig { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub bssid: String<32>, + #[noproto(tag = "3")] + pub rssi: u32, + #[noproto(tag = "4")] + pub chnl: u32, + #[noproto(tag = "5")] + pub sec_prot: CtrlWifiSecProt, + #[noproto(tag = "6")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqConnectAp { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub pwd: String<32>, + #[noproto(tag = "3")] + pub bssid: String<32>, + #[noproto(tag = "4")] + pub is_wpa3_supported: bool, + #[noproto(tag = "5")] + pub listen_interval: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespConnectAp { + #[noproto(tag = "1")] + pub resp: u32, + #[noproto(tag = "2")] + pub mac: String<32>, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetSoftApConfig {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetSoftApConfig { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub pwd: String<32>, + #[noproto(tag = "3")] + pub chnl: u32, + #[noproto(tag = "4")] + pub sec_prot: CtrlWifiSecProt, + #[noproto(tag = "5")] + pub max_conn: u32, + #[noproto(tag = "6")] + pub ssid_hidden: bool, + #[noproto(tag = "7")] + pub bw: u32, + #[noproto(tag = "8")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqStartSoftAp { + #[noproto(tag = "1")] + pub ssid: String<32>, + #[noproto(tag = "2")] + pub pwd: String<32>, + #[noproto(tag = "3")] + pub chnl: u32, + #[noproto(tag = "4")] + pub sec_prot: CtrlWifiSecProt, + #[noproto(tag = "5")] + pub max_conn: u32, + #[noproto(tag = "6")] + pub ssid_hidden: bool, + #[noproto(tag = "7")] + pub bw: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespStartSoftAp { + #[noproto(tag = "1")] + pub resp: u32, + #[noproto(tag = "2")] + pub mac: String<32>, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqScanResult {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespScanResult { + #[noproto(tag = "1")] + pub count: u32, + #[noproto(repeated, tag = "2")] + pub entries: Vec, + #[noproto(tag = "3")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSoftApConnectedSta {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSoftApConnectedSta { + #[noproto(tag = "1")] + pub num: u32, + #[noproto(repeated, tag = "2")] + pub stations: Vec, + #[noproto(tag = "3")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqOtaBegin {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespOtaBegin { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqOtaWrite { + #[noproto(tag = "1")] + pub ota_data: Vec, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespOtaWrite { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqOtaEnd {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespOtaEnd { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqVendorIeData { + #[noproto(tag = "1")] + pub element_id: u32, + #[noproto(tag = "2")] + pub length: u32, + #[noproto(tag = "3")] + pub vendor_oui: Vec, + #[noproto(tag = "4")] + pub vendor_oui_type: u32, + #[noproto(tag = "5")] + pub payload: Vec, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetSoftApVendorSpecificIe { + #[noproto(tag = "1")] + pub enable: bool, + #[noproto(tag = "2")] + pub r#type: CtrlVendorIeType, + #[noproto(tag = "3")] + pub idx: CtrlVendorIeid, + #[noproto(optional, tag = "4")] + pub vendor_ie_data: Option, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetSoftApVendorSpecificIe { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqSetWifiMaxTxPower { + #[noproto(tag = "1")] + pub wifi_max_tx_power: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespSetWifiMaxTxPower { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqGetWifiCurrTxPower {} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespGetWifiCurrTxPower { + #[noproto(tag = "1")] + pub wifi_curr_tx_power: u32, + #[noproto(tag = "2")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgReqConfigHeartbeat { + #[noproto(tag = "1")] + pub enable: bool, + #[noproto(tag = "2")] + pub duration: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgRespConfigHeartbeat { + #[noproto(tag = "1")] + pub resp: u32, +} +/// * Event structure * + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventEspInit { + #[noproto(tag = "1")] + pub init_data: Vec, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventHeartbeat { + #[noproto(tag = "1")] + pub hb_num: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventStationDisconnectFromAp { + #[noproto(tag = "1")] + pub resp: u32, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsgEventStationDisconnectFromEspSoftAp { + #[noproto(tag = "1")] + pub resp: u32, + #[noproto(tag = "2")] + pub mac: String<32>, +} + +#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +pub struct CtrlMsg { + /// msg_type could be req, resp or Event + #[noproto(tag = "1")] + pub msg_type: CtrlMsgType, + /// msg id + #[noproto(tag = "2")] + pub msg_id: CtrlMsgId, + /// union of all msg ids + #[noproto( + oneof, + tags = "101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 301, 302, 303, 304" + )] + pub payload: Option, +} + +/// union of all msg ids +#[derive(Debug, Clone, Eq, PartialEq, noproto::Oneof)] +pub enum CtrlMsgPayload { + /// * Requests * + #[noproto(tag = "101")] + ReqGetMacAddress(CtrlMsgReqGetMacAddress), + #[noproto(tag = "102")] + ReqSetMacAddress(CtrlMsgReqSetMacAddress), + #[noproto(tag = "103")] + ReqGetWifiMode(CtrlMsgReqGetMode), + #[noproto(tag = "104")] + ReqSetWifiMode(CtrlMsgReqSetMode), + #[noproto(tag = "105")] + ReqScanApList(CtrlMsgReqScanResult), + #[noproto(tag = "106")] + ReqGetApConfig(CtrlMsgReqGetApConfig), + #[noproto(tag = "107")] + ReqConnectAp(CtrlMsgReqConnectAp), + #[noproto(tag = "108")] + ReqDisconnectAp(CtrlMsgReqGetStatus), + #[noproto(tag = "109")] + ReqGetSoftapConfig(CtrlMsgReqGetSoftApConfig), + #[noproto(tag = "110")] + ReqSetSoftapVendorSpecificIe(CtrlMsgReqSetSoftApVendorSpecificIe), + #[noproto(tag = "111")] + ReqStartSoftap(CtrlMsgReqStartSoftAp), + #[noproto(tag = "112")] + ReqSoftapConnectedStasList(CtrlMsgReqSoftApConnectedSta), + #[noproto(tag = "113")] + ReqStopSoftap(CtrlMsgReqGetStatus), + #[noproto(tag = "114")] + ReqSetPowerSaveMode(CtrlMsgReqSetMode), + #[noproto(tag = "115")] + ReqGetPowerSaveMode(CtrlMsgReqGetMode), + #[noproto(tag = "116")] + ReqOtaBegin(CtrlMsgReqOtaBegin), + #[noproto(tag = "117")] + ReqOtaWrite(CtrlMsgReqOtaWrite), + #[noproto(tag = "118")] + ReqOtaEnd(CtrlMsgReqOtaEnd), + #[noproto(tag = "119")] + ReqSetWifiMaxTxPower(CtrlMsgReqSetWifiMaxTxPower), + #[noproto(tag = "120")] + ReqGetWifiCurrTxPower(CtrlMsgReqGetWifiCurrTxPower), + #[noproto(tag = "121")] + ReqConfigHeartbeat(CtrlMsgReqConfigHeartbeat), + /// * Responses * + #[noproto(tag = "201")] + RespGetMacAddress(CtrlMsgRespGetMacAddress), + #[noproto(tag = "202")] + RespSetMacAddress(CtrlMsgRespSetMacAddress), + #[noproto(tag = "203")] + RespGetWifiMode(CtrlMsgRespGetMode), + #[noproto(tag = "204")] + RespSetWifiMode(CtrlMsgRespSetMode), + #[noproto(tag = "205")] + RespScanApList(CtrlMsgRespScanResult), + #[noproto(tag = "206")] + RespGetApConfig(CtrlMsgRespGetApConfig), + #[noproto(tag = "207")] + RespConnectAp(CtrlMsgRespConnectAp), + #[noproto(tag = "208")] + RespDisconnectAp(CtrlMsgRespGetStatus), + #[noproto(tag = "209")] + RespGetSoftapConfig(CtrlMsgRespGetSoftApConfig), + #[noproto(tag = "210")] + RespSetSoftapVendorSpecificIe(CtrlMsgRespSetSoftApVendorSpecificIe), + #[noproto(tag = "211")] + RespStartSoftap(CtrlMsgRespStartSoftAp), + #[noproto(tag = "212")] + RespSoftapConnectedStasList(CtrlMsgRespSoftApConnectedSta), + #[noproto(tag = "213")] + RespStopSoftap(CtrlMsgRespGetStatus), + #[noproto(tag = "214")] + RespSetPowerSaveMode(CtrlMsgRespSetMode), + #[noproto(tag = "215")] + RespGetPowerSaveMode(CtrlMsgRespGetMode), + #[noproto(tag = "216")] + RespOtaBegin(CtrlMsgRespOtaBegin), + #[noproto(tag = "217")] + RespOtaWrite(CtrlMsgRespOtaWrite), + #[noproto(tag = "218")] + RespOtaEnd(CtrlMsgRespOtaEnd), + #[noproto(tag = "219")] + RespSetWifiMaxTxPower(CtrlMsgRespSetWifiMaxTxPower), + #[noproto(tag = "220")] + RespGetWifiCurrTxPower(CtrlMsgRespGetWifiCurrTxPower), + #[noproto(tag = "221")] + RespConfigHeartbeat(CtrlMsgRespConfigHeartbeat), + /// * Notifications * + #[noproto(tag = "301")] + EventEspInit(CtrlMsgEventEspInit), + #[noproto(tag = "302")] + EventHeartbeat(CtrlMsgEventHeartbeat), + #[noproto(tag = "303")] + EventStationDisconnectFromAp(CtrlMsgEventStationDisconnectFromAp), + #[noproto(tag = "304")] + EventStationDisconnectFromEspSoftAp(CtrlMsgEventStationDisconnectFromEspSoftAp), +} + +/// Enums similar to ESP IDF +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlVendorIeType { + #[default] + Beacon = 0, + ProbeReq = 1, + ProbeResp = 2, + AssocReq = 3, + AssocResp = 4, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlVendorIeid { + #[default] + Id0 = 0, + Id1 = 1, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiMode { + #[default] + None = 0, + Sta = 1, + Ap = 2, + Apsta = 3, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiBw { + #[default] + BwInvalid = 0, + Ht20 = 1, + Ht40 = 2, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiPowerSave { + #[default] + PsInvalid = 0, + MinModem = 1, + MaxModem = 2, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlWifiSecProt { + #[default] + Open = 0, + Wep = 1, + WpaPsk = 2, + Wpa2Psk = 3, + WpaWpa2Psk = 4, + Wpa2Enterprise = 5, + Wpa3Psk = 6, + Wpa2Wpa3Psk = 7, +} + +/// enums for Control path +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlStatus { + #[default] + Connected = 0, + NotConnected = 1, + NoApFound = 2, + ConnectionFail = 3, + InvalidArgument = 4, + OutOfRange = 5, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlMsgType { + #[default] + MsgTypeInvalid = 0, + Req = 1, + Resp = 2, + Event = 3, + MsgTypeMax = 4, +} + +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] +#[repr(u32)] +pub enum CtrlMsgId { + #[default] + MsgIdInvalid = 0, + /// * Request Msgs * + ReqBase = 100, + ReqGetMacAddress = 101, + ReqSetMacAddress = 102, + ReqGetWifiMode = 103, + ReqSetWifiMode = 104, + ReqGetApScanList = 105, + ReqGetApConfig = 106, + ReqConnectAp = 107, + ReqDisconnectAp = 108, + ReqGetSoftApConfig = 109, + ReqSetSoftApVendorSpecificIe = 110, + ReqStartSoftAp = 111, + ReqGetSoftApConnectedStaList = 112, + ReqStopSoftAp = 113, + ReqSetPowerSaveMode = 114, + ReqGetPowerSaveMode = 115, + ReqOtaBegin = 116, + ReqOtaWrite = 117, + ReqOtaEnd = 118, + ReqSetWifiMaxTxPower = 119, + ReqGetWifiCurrTxPower = 120, + ReqConfigHeartbeat = 121, + /// Add new control path command response before Req_Max + /// and update Req_Max + ReqMax = 122, + /// * Response Msgs * + RespBase = 200, + RespGetMacAddress = 201, + RespSetMacAddress = 202, + RespGetWifiMode = 203, + RespSetWifiMode = 204, + RespGetApScanList = 205, + RespGetApConfig = 206, + RespConnectAp = 207, + RespDisconnectAp = 208, + RespGetSoftApConfig = 209, + RespSetSoftApVendorSpecificIe = 210, + RespStartSoftAp = 211, + RespGetSoftApConnectedStaList = 212, + RespStopSoftAp = 213, + RespSetPowerSaveMode = 214, + RespGetPowerSaveMode = 215, + RespOtaBegin = 216, + RespOtaWrite = 217, + RespOtaEnd = 218, + RespSetWifiMaxTxPower = 219, + RespGetWifiCurrTxPower = 220, + RespConfigHeartbeat = 221, + /// Add new control path command response before Resp_Max + /// and update Resp_Max + RespMax = 222, + /// * Event Msgs * + EventBase = 300, + EventEspInit = 301, + EventHeartbeat = 302, + EventStationDisconnectFromAp = 303, + EventStationDisconnectFromEspSoftAp = 304, + /// Add new control path command notification before Event_Max + /// and update Event_Max + EventMax = 305, +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 6627b7861..7c9d48bad 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -22,6 +22,7 @@ embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["ti lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } +embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -35,3 +36,4 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } +embedded-hal-async = "0.2.0-alpha.1" diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs new file mode 100644 index 000000000..401dbd33c --- /dev/null +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -0,0 +1,143 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap, warn}; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, StackResources}; +use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::{self, Spim}; +use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::{Duration, Timer}; +use embedded_hal_async::spi::ExclusiveDevice; +use embedded_io::asynch::Write; +use static_cell::make_static; +use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn wifi_task( + runner: hosted::Runner< + 'static, + ExclusiveDevice, Output<'static, peripherals::P0_31>>, + Input<'static, AnyPin>, + Output<'static, peripherals::P1_03>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_nrf::init(Default::default()); + + let miso = p.P0_28; + let sck = p.P0_29; + let mosi = p.P0_30; + let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive); + let handshake = Input::new(p.P1_01.degrade(), Pull::Up); + let ready = Input::new(p.P1_02.degrade(), Pull::None); + let reset = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M1; + config.mode = spim::MODE_2; // !!! + let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); + let spi = ExclusiveDevice::new(spi, cs); + + let (device, mut control, runner) = embassy_net_esp_hosted::new( + make_static!(embassy_net_esp_hosted::State::new()), + spi, + handshake, + ready, + reset, + ) + .await; + + unwrap!(spawner.spawn(wifi_task(runner))); + + // TODO: wait for ESP_INIT event instead of hardcoding delay. + Timer::after(Duration::from_secs(3)).await; + + control.init().await; + control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} From ec2c095a76a0f55a031093e98a5283cff0daa576 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 18:13:19 +0200 Subject: [PATCH 2/8] esp-hosted: print events. --- embassy-net-esp-hosted/src/control.rs | 7 +++- embassy-net-esp-hosted/src/ioctl.rs | 4 +- embassy-net-esp-hosted/src/lib.rs | 36 ++++++++++++++---- embassy-net-esp-hosted/src/proto.rs | 54 +++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 11 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 2381c6b84..ec51933b3 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -112,6 +112,8 @@ impl<'a> Control<'a> { } async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { + debug!("ioctl req: {:?}", &req); + let mut buf = [0u8; 128]; let req_len = noproto::write(&req, &mut buf).unwrap(); @@ -136,6 +138,9 @@ impl<'a> Control<'a> { ioctl.defuse(); - noproto::read(&buf[..resp_len]).unwrap() + let res = noproto::read(&buf[..resp_len]).unwrap(); + debug!("ioctl resp: {:?}", &res); + + res } } diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 59bdabf37..689dd2a88 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -94,7 +94,7 @@ impl IoctlState { } pub async fn do_ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { - debug!("IOCTL Request: {:02x}", Bytes(&buf[..req_len])); + trace!("ioctl req bytes: {:02x}", Bytes(&buf[..req_len])); self.state.set(IoctlStateInner::Pending(PendingIoctl { buf, req_len })); self.wake_runner(); @@ -103,7 +103,7 @@ impl IoctlState { pub fn ioctl_done(&self, response: &[u8]) { if let IoctlStateInner::Sent { buf } = self.state.get() { - debug!("IOCTL Response: {:02x}", Bytes(response)); + trace!("ioctl resp bytes: {:02x}", Bytes(response)); // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 2cf05a7df..084009966 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -8,6 +8,7 @@ use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; use ioctl::IoctlState; +use proto::CtrlMsg; use crate::ioctl::PendingIoctl; @@ -194,8 +195,6 @@ where tx_buf[0..12].copy_from_slice(&header.to_bytes()); header.checksum = checksum(&tx_buf[..26 + req_len]); tx_buf[0..12].copy_from_slice(&header.to_bytes()); - - debug!("====== SENDING IOCTL"); } Either3::Second(packet) => { tx_buf[12..][..packet.len()].copy_from_slice(packet); @@ -270,25 +269,46 @@ where }, // serial 2 => { - debug!("serial rx: {:02x}", payload); + trace!("serial rx: {:02x}", payload); if payload.len() < 14 { warn!("serial rx: too short"); return; } - if &payload[..12] != b"\x01\x08\x00ctrlResp\x02" { - warn!("serial rx: bad tlv"); - return; - } + + let isEvent = match &payload[..12] { + b"\x01\x08\x00ctrlResp\x02" => false, + b"\x01\x08\x00ctrlEvnt\x02" => true, + _ => { + warn!("serial rx: bad tlv"); + return; + } + }; + let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize; if payload.len() < 14 + len { warn!("serial rx: too short 2"); return; } - self.ioctl_state.ioctl_done(&payload[14..][..len]); + let data = &payload[14..][..len]; + + if isEvent { + self.handle_event(data); + } else { + self.ioctl_state.ioctl_done(data); + } } _ => warn!("unknown iftype {}", if_type_and_num), } } + + fn handle_event(&self, data: &[u8]) { + let Ok(event) = noproto::read::(data) else { + warn!("failed to parse event"); + return + }; + + debug!("event: {:?}", &event); + } } fn checksum(buf: &[u8]) -> u16 { diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs index e105e393c..8ceb1579d 100644 --- a/embassy-net-esp-hosted/src/proto.rs +++ b/embassy-net-esp-hosted/src/proto.rs @@ -3,6 +3,7 @@ use heapless::{String, Vec}; /// internal supporting structures for CtrlMsg #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanResult { #[noproto(tag = "1")] pub ssid: String<32>, @@ -17,6 +18,7 @@ pub struct ScanResult { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ConnectedStaList { #[noproto(tag = "1")] pub mac: String<32>, @@ -26,12 +28,14 @@ pub struct ConnectedStaList { /// * Req/Resp structure * #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetMacAddress { #[noproto(tag = "1")] pub mode: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetMacAddress { #[noproto(tag = "1")] pub mac: String<32>, @@ -40,9 +44,11 @@ pub struct CtrlMsgRespGetMacAddress { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetMode {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetMode { #[noproto(tag = "1")] pub mode: u32, @@ -51,27 +57,32 @@ pub struct CtrlMsgRespGetMode { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetMode { #[noproto(tag = "1")] pub mode: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetMode { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetStatus {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetStatus { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetMacAddress { #[noproto(tag = "1")] pub mac: String<32>, @@ -80,15 +91,18 @@ pub struct CtrlMsgReqSetMacAddress { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetMacAddress { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetApConfig {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetApConfig { #[noproto(tag = "1")] pub ssid: String<32>, @@ -105,6 +119,7 @@ pub struct CtrlMsgRespGetApConfig { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqConnectAp { #[noproto(tag = "1")] pub ssid: String<32>, @@ -119,6 +134,7 @@ pub struct CtrlMsgReqConnectAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespConnectAp { #[noproto(tag = "1")] pub resp: u32, @@ -127,9 +143,11 @@ pub struct CtrlMsgRespConnectAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetSoftApConfig {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetSoftApConfig { #[noproto(tag = "1")] pub ssid: String<32>, @@ -150,6 +168,7 @@ pub struct CtrlMsgRespGetSoftApConfig { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqStartSoftAp { #[noproto(tag = "1")] pub ssid: String<32>, @@ -168,6 +187,7 @@ pub struct CtrlMsgReqStartSoftAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespStartSoftAp { #[noproto(tag = "1")] pub resp: u32, @@ -176,9 +196,11 @@ pub struct CtrlMsgRespStartSoftAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqScanResult {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespScanResult { #[noproto(tag = "1")] pub count: u32, @@ -189,9 +211,11 @@ pub struct CtrlMsgRespScanResult { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSoftApConnectedSta {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSoftApConnectedSta { #[noproto(tag = "1")] pub num: u32, @@ -202,36 +226,43 @@ pub struct CtrlMsgRespSoftApConnectedSta { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqOtaBegin {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespOtaBegin { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqOtaWrite { #[noproto(tag = "1")] pub ota_data: Vec, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespOtaWrite { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqOtaEnd {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespOtaEnd { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqVendorIeData { #[noproto(tag = "1")] pub element_id: u32, @@ -246,6 +277,7 @@ pub struct CtrlMsgReqVendorIeData { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetSoftApVendorSpecificIe { #[noproto(tag = "1")] pub enable: bool, @@ -258,27 +290,32 @@ pub struct CtrlMsgReqSetSoftApVendorSpecificIe { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetSoftApVendorSpecificIe { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqSetWifiMaxTxPower { #[noproto(tag = "1")] pub wifi_max_tx_power: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespSetWifiMaxTxPower { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqGetWifiCurrTxPower {} #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespGetWifiCurrTxPower { #[noproto(tag = "1")] pub wifi_curr_tx_power: u32, @@ -287,6 +324,7 @@ pub struct CtrlMsgRespGetWifiCurrTxPower { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgReqConfigHeartbeat { #[noproto(tag = "1")] pub enable: bool, @@ -295,6 +333,7 @@ pub struct CtrlMsgReqConfigHeartbeat { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgRespConfigHeartbeat { #[noproto(tag = "1")] pub resp: u32, @@ -302,24 +341,28 @@ pub struct CtrlMsgRespConfigHeartbeat { /// * Event structure * #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventEspInit { #[noproto(tag = "1")] pub init_data: Vec, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventHeartbeat { #[noproto(tag = "1")] pub hb_num: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventStationDisconnectFromAp { #[noproto(tag = "1")] pub resp: u32, } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsgEventStationDisconnectFromEspSoftAp { #[noproto(tag = "1")] pub resp: u32, @@ -328,6 +371,7 @@ pub struct CtrlMsgEventStationDisconnectFromEspSoftAp { } #[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CtrlMsg { /// msg_type could be req, resp or Event #[noproto(tag = "1")] @@ -345,6 +389,7 @@ pub struct CtrlMsg { /// union of all msg ids #[derive(Debug, Clone, Eq, PartialEq, noproto::Oneof)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlMsgPayload { /// * Requests * #[noproto(tag = "101")] @@ -446,6 +491,7 @@ pub enum CtrlMsgPayload { /// Enums similar to ESP IDF #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlVendorIeType { #[default] Beacon = 0, @@ -457,6 +503,7 @@ pub enum CtrlVendorIeType { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlVendorIeid { #[default] Id0 = 0, @@ -465,6 +512,7 @@ pub enum CtrlVendorIeid { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiMode { #[default] None = 0, @@ -475,6 +523,7 @@ pub enum CtrlWifiMode { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiBw { #[default] BwInvalid = 0, @@ -484,6 +533,7 @@ pub enum CtrlWifiBw { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiPowerSave { #[default] PsInvalid = 0, @@ -493,6 +543,7 @@ pub enum CtrlWifiPowerSave { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlWifiSecProt { #[default] Open = 0, @@ -508,6 +559,7 @@ pub enum CtrlWifiSecProt { /// enums for Control path #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlStatus { #[default] Connected = 0, @@ -520,6 +572,7 @@ pub enum CtrlStatus { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlMsgType { #[default] MsgTypeInvalid = 0, @@ -531,6 +584,7 @@ pub enum CtrlMsgType { #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)] #[repr(u32)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CtrlMsgId { #[default] MsgIdInvalid = 0, From 082f1ab494587e02d405cad8f186d48943acf16d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 18:55:29 +0200 Subject: [PATCH 3/8] esp-hosted: nicer names for shared state struct. --- embassy-net-esp-hosted/src/control.rs | 16 ++--- embassy-net-esp-hosted/src/ioctl.rs | 93 +++++++++++---------------- embassy-net-esp-hosted/src/lib.rs | 16 ++--- 3 files changed, 53 insertions(+), 72 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index ec51933b3..c98d0ebf4 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -3,7 +3,7 @@ use defmt::Debug2Format; use embassy_net_driver_channel as ch; use heapless::String; -use crate::ioctl::IoctlState; +use crate::ioctl::Shared; use crate::proto::{self, CtrlMsg}; #[derive(Debug)] @@ -13,7 +13,7 @@ pub struct Error { pub struct Control<'a> { state_ch: ch::StateRunner<'a>, - ioctl_state: &'a IoctlState, + shared: &'a Shared, } enum WifiMode { @@ -24,8 +24,8 @@ enum WifiMode { } impl<'a> Control<'a> { - pub(crate) fn new(state_ch: ch::StateRunner<'a>, ioctl_state: &'a IoctlState) -> Self { - Self { state_ch, ioctl_state } + pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { + Self { state_ch, shared } } pub async fn init(&mut self) { @@ -118,7 +118,7 @@ impl<'a> Control<'a> { let req_len = noproto::write(&req, &mut buf).unwrap(); - struct CancelOnDrop<'a>(&'a IoctlState); + struct CancelOnDrop<'a>(&'a Shared); impl CancelOnDrop<'_> { fn defuse(self) { @@ -128,13 +128,13 @@ impl<'a> Control<'a> { impl Drop for CancelOnDrop<'_> { fn drop(&mut self) { - self.0.cancel_ioctl(); + self.0.ioctl_cancel(); } } - let ioctl = CancelOnDrop(self.ioctl_state); + let ioctl = CancelOnDrop(self.shared); - let resp_len = ioctl.0.do_ioctl(&mut buf, req_len).await; + let resp_len = ioctl.0.ioctl(&mut buf, req_len).await; ioctl.defuse(); diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 689dd2a88..7cbe80b2a 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -13,105 +13,86 @@ pub struct PendingIoctl { } #[derive(Clone, Copy)] -enum IoctlStateInner { +enum IoctlState { Pending(PendingIoctl), Sent { buf: *mut [u8] }, Done { resp_len: usize }, } -struct Wakers { - control: WakerRegistration, - runner: WakerRegistration, +pub struct Shared(RefCell); + +struct SharedInner { + ioctl: IoctlState, + control_waker: WakerRegistration, + runner_waker: WakerRegistration, } -impl Default for Wakers { - fn default() -> Self { - Self { - control: WakerRegistration::new(), - runner: WakerRegistration::new(), - } - } -} - -pub struct IoctlState { - state: Cell, - wakers: RefCell, -} - -impl IoctlState { +impl Shared { pub fn new() -> Self { - Self { - state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), - wakers: Default::default(), - } + Self(RefCell::new(SharedInner { + ioctl: IoctlState::Done { resp_len: 0 }, + control_waker: WakerRegistration::new(), + runner_waker: WakerRegistration::new(), + })) } - 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 { + pub async fn ioctl_wait_complete(&self) -> usize { poll_fn(|cx| { - if let IoctlStateInner::Done { resp_len } = self.state.get() { + let mut this = self.0.borrow_mut(); + if let IoctlState::Done { resp_len } = this.ioctl { Poll::Ready(resp_len) } else { - self.register_control(cx.waker()); + this.control_waker.register(cx.waker()); Poll::Pending } }) .await } - pub async fn wait_pending(&self) -> PendingIoctl { + pub async fn ioctl_wait_pending(&self) -> PendingIoctl { let pending = poll_fn(|cx| { - if let IoctlStateInner::Pending(pending) = self.state.get() { + let mut this = self.0.borrow_mut(); + if let IoctlState::Pending(pending) = this.ioctl { Poll::Ready(pending) } else { - self.register_runner(cx.waker()); + this.runner_waker.register(cx.waker()); Poll::Pending } }) .await; - self.state.set(IoctlStateInner::Sent { buf: pending.buf }); + self.0.borrow_mut().ioctl = IoctlState::Sent { buf: pending.buf }; pending } - pub fn cancel_ioctl(&self) { - self.state.set(IoctlStateInner::Done { resp_len: 0 }); + pub fn ioctl_cancel(&self) { + self.0.borrow_mut().ioctl = IoctlState::Done { resp_len: 0 }; } - pub async fn do_ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { + pub async fn ioctl(&self, buf: &mut [u8], req_len: usize) -> usize { trace!("ioctl req bytes: {:02x}", Bytes(&buf[..req_len])); - self.state.set(IoctlStateInner::Pending(PendingIoctl { buf, req_len })); - self.wake_runner(); - self.wait_complete().await + { + let mut this = self.0.borrow_mut(); + this.ioctl = IoctlState::Pending(PendingIoctl { buf, req_len }); + this.runner_waker.wake(); + } + + self.ioctl_wait_complete().await } pub fn ioctl_done(&self, response: &[u8]) { - if let IoctlStateInner::Sent { buf } = self.state.get() { + let mut this = self.0.borrow_mut(); + if let IoctlState::Sent { buf } = this.ioctl { trace!("ioctl resp bytes: {:02x}", Bytes(response)); // TODO fix this (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); - self.state.set(IoctlStateInner::Done { + this.ioctl = IoctlState::Done { resp_len: response.len(), - }); - self.wake_control(); + }; + this.control_waker.wake(); } else { warn!("IOCTL Response but no pending Ioctl"); } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 084009966..700a5221c 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -7,7 +7,7 @@ use embassy_time::{Duration, Instant, Timer}; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use ioctl::IoctlState; +use ioctl::Shared; use proto::CtrlMsg; use crate::ioctl::PendingIoctl; @@ -95,14 +95,14 @@ enum InterfaceType { const MAX_SPI_BUFFER_SIZE: usize = 1600; pub struct State { - ioctl_state: IoctlState, + shared: Shared, ch: ch::State, } impl State { pub fn new() -> Self { Self { - ioctl_state: IoctlState::new(), + shared: Shared::new(), ch: ch::State::new(), } } @@ -127,7 +127,7 @@ where let mut runner = Runner { ch: ch_runner, - ioctl_state: &state.ioctl_state, + shared: &state.shared, next_seq: 1, handshake, ready, @@ -136,12 +136,12 @@ where }; runner.init().await; - (device, Control::new(state_ch, &state.ioctl_state), runner) + (device, Control::new(state_ch, &state.shared), runner) } pub struct Runner<'a, SPI, IN, OUT> { ch: ch::Runner<'a, MTU>, - ioctl_state: &'a IoctlState, + shared: &'a Shared, next_seq: u16, @@ -172,7 +172,7 @@ where loop { self.handshake.wait_for_high().await.unwrap(); - let ioctl = self.ioctl_state.wait_pending(); + let ioctl = self.shared.ioctl_wait_pending(); let tx = self.ch.tx_buf(); let ev = async { self.ready.wait_for_high().await.unwrap() }; @@ -294,7 +294,7 @@ where if isEvent { self.handle_event(data); } else { - self.ioctl_state.ioctl_done(data); + self.shared.ioctl_done(data); } } _ => warn!("unknown iftype {}", if_type_and_num), From 764b43e82c7b61c21621c1fd9f5fd2f6a3dc419c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 19:05:20 +0200 Subject: [PATCH 4/8] esp-hosted: wait for esp firmware init. --- embassy-net-esp-hosted/src/control.rs | 3 +++ embassy-net-esp-hosted/src/ioctl.rs | 23 ++++++++++++++++++++ embassy-net-esp-hosted/src/lib.rs | 11 ++++++++++ examples/nrf52840/src/bin/wifi_esp_hosted.rs | 3 --- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index c98d0ebf4..a2a65bc31 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -29,6 +29,9 @@ impl<'a> Control<'a> { } pub async fn init(&mut self) { + debug!("wait for init event..."); + self.shared.init_wait().await; + debug!("set wifi mode"); self.set_wifi_mode(WifiMode::Sta as _).await; let mac_addr = self.get_mac_addr().await; diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 7cbe80b2a..607b7b627 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -23,6 +23,7 @@ pub struct Shared(RefCell); struct SharedInner { ioctl: IoctlState, + is_init: bool, control_waker: WakerRegistration, runner_waker: WakerRegistration, } @@ -31,6 +32,7 @@ impl Shared { pub fn new() -> Self { Self(RefCell::new(SharedInner { ioctl: IoctlState::Done { resp_len: 0 }, + is_init: false, control_waker: WakerRegistration::new(), runner_waker: WakerRegistration::new(), })) @@ -97,4 +99,25 @@ impl Shared { warn!("IOCTL Response but no pending Ioctl"); } } + + // // // // // // // // // // // // // // // // // // // // + + pub fn init_done(&self) { + let mut this = self.0.borrow_mut(); + this.is_init = true; + this.control_waker.wake(); + } + + pub async fn init_wait(&self) { + poll_fn(|cx| { + let mut this = self.0.borrow_mut(); + if this.is_init { + Poll::Ready(()) + } else { + this.control_waker.register(cx.waker()); + Poll::Pending + } + }) + .await + } } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 700a5221c..ff7bed2b7 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -11,6 +11,7 @@ use ioctl::Shared; use proto::CtrlMsg; use crate::ioctl::PendingIoctl; +use crate::proto::CtrlMsgPayload; mod proto; @@ -308,6 +309,16 @@ where }; debug!("event: {:?}", &event); + + let Some(payload) = &event.payload else { + warn!("event without payload?"); + return + }; + + match payload { + CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), + _ => {} + } } } diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 401dbd33c..b75756f76 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -69,9 +69,6 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - // TODO: wait for ESP_INIT event instead of hardcoding delay. - Timer::after(Duration::from_secs(3)).await; - control.init().await; control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; From 1ed909ea74bfff75bd91b35739a44c5128271571 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 21 Jun 2023 19:08:09 +0200 Subject: [PATCH 5/8] esp-hosted: fix warnings. --- embassy-net-esp-hosted/src/control.rs | 14 ++------------ embassy-net-esp-hosted/src/ioctl.rs | 4 ++-- embassy-net-esp-hosted/src/lib.rs | 5 +++-- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 1 - 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index a2a65bc31..fce82ade7 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -16,6 +16,7 @@ pub struct Control<'a> { shared: &'a Shared, } +#[allow(unused)] enum WifiMode { None = 0, Sta = 1, @@ -34,6 +35,7 @@ impl<'a> Control<'a> { debug!("set wifi mode"); self.set_wifi_mode(WifiMode::Sta as _).await; + let mac_addr = self.get_mac_addr().await; debug!("mac addr: {:02x}", mac_addr); self.state_ch.set_ethernet_address(mac_addr); @@ -91,18 +93,6 @@ impl<'a> Control<'a> { res } - async fn get_wifi_mode(&mut self) -> u32 { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqGetWifiMode as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqGetWifiMode(proto::CtrlMsgReqGetMode {})), - }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespGetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; - assert_eq!(resp.resp, 0); - resp.mode - } - async fn set_wifi_mode(&mut self, mode: u32) { let req = proto::CtrlMsg { msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 607b7b627..e2a6815aa 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -1,6 +1,6 @@ -use core::cell::{Cell, RefCell}; +use core::cell::RefCell; use core::future::poll_fn; -use core::task::{Poll, Waker}; +use core::task::Poll; use embassy_sync::waitqueue::WakerRegistration; diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index ff7bed2b7..2a4601ce4 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -83,6 +83,7 @@ struct PayloadHeader { } impl_bytes!(PayloadHeader); +#[allow(unused)] #[repr(u8)] enum InterfaceType { Sta = 0, @@ -276,7 +277,7 @@ where return; } - let isEvent = match &payload[..12] { + let is_event = match &payload[..12] { b"\x01\x08\x00ctrlResp\x02" => false, b"\x01\x08\x00ctrlEvnt\x02" => true, _ => { @@ -292,7 +293,7 @@ where } let data = &payload[14..][..len]; - if isEvent { + if is_event { self.handle_event(data); } else { self.shared.ioctl_done(data); diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index b75756f76..cea45c5c8 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -10,7 +10,6 @@ use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; -use embassy_time::{Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use static_cell::make_static; From 0d02298ea628b9d2154fd05db7975f62b8d12edb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Jun 2023 15:30:26 +0200 Subject: [PATCH 6/8] esp-hosted: fix build on stable. --- examples/nrf52840/Cargo.toml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 7c9d48bad..8c4175966 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,8 +6,24 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", "static_cell/nightly", - "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lora-phy", "lorawan-device", "lorawan"] +nightly = [ + "embedded-hal-async", + "embassy-executor/nightly", + "embassy-nrf/nightly", + "embassy-net/nightly", + "embassy-net-esp-hosted", + "embassy-nrf/unstable-traits", + "embassy-time/nightly", + "embassy-time/unstable-traits", + "static_cell/nightly", + "embassy-usb", + "embedded-io/async", + "embassy-net", + "embassy-lora", + "lora-phy", + "lorawan-device", + "lorawan", +] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -22,7 +38,7 @@ embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["ti lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } -embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } +embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -36,4 +52,4 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } -embedded-hal-async = "0.2.0-alpha.1" +embedded-hal-async = { version = "0.2.0-alpha.1", optional = true } From 6e65282f185d94a8c449374e6d4ed4eefa5793a4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Jun 2023 21:10:58 +0200 Subject: [PATCH 7/8] esp-hosted: smaller delay after transfer, makes slightly better perf. --- embassy-net-esp-hosted/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 2a4601ce4..44dfbe89c 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -227,7 +227,12 @@ where } self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); - let delay_until = Instant::now() + Duration::from_millis(1); + + // The esp-hosted firmware deasserts the HANSHAKE pin a few us AFTER ending the SPI transfer + // If we check it again too fast, we'll see it's high from the previous transfer, and if we send it + // data it will get lost. + // Make sure we check it after 100us at minimum. + let delay_until = Instant::now() + Duration::from_micros(100); self.handle_rx(&mut rx_buf); Timer::at(delay_until).await; } From 8bbfa6827cd68f67a50a89b13947542e632d5411 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Jun 2023 21:11:53 +0200 Subject: [PATCH 8/8] esp-hosted: add perf hil test. --- ci.sh | 2 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 8 +- tests/nrf/Cargo.toml | 4 + tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 270 +++++++++++++++++++ tests/rp/src/bin/cyw43-perf.rs | 9 +- 5 files changed, 280 insertions(+), 13 deletions(-) create mode 100644 tests/nrf/src/bin/wifi_esp_hosted_perf.rs diff --git a/ci.sh b/ci.sh index 3fe1b1ce8..a03efb856 100755 --- a/ci.sh +++ b/ci.sh @@ -3,7 +3,7 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings -export DEFMT_LOG=trace,cyw43=info,cyw43_pio=info,smoltcp=info +export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info # needed by wifi examples export WIFI_NETWORK=x diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index cea45c5c8..4eb31b105 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -26,7 +26,7 @@ async fn wifi_task( 'static, ExclusiveDevice, Output<'static, peripherals::P0_31>>, Input<'static, AnyPin>, - Output<'static, peripherals::P1_03>, + Output<'static, peripherals::P1_05>, >, ) -> ! { runner.run().await @@ -48,11 +48,11 @@ async fn main(spawner: Spawner) { let mosi = p.P0_30; let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive); let handshake = Input::new(p.P1_01.degrade(), Pull::Up); - let ready = Input::new(p.P1_02.degrade(), Pull::None); - let reset = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); + let ready = Input::new(p.P1_04.degrade(), Pull::None); + let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); let mut config = spim::Config::default(); - config.frequency = spim::Frequency::M1; + config.frequency = spim::Frequency::M32; config.mode = spim::MODE_2; // !!! let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); let spi = ExclusiveDevice::new(spi, cs); diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 9735c87d9..4f9ecc47a 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -13,6 +13,10 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } +embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } +embedded-hal-async = { version = "0.2.0-alpha.1" } +static_cell = { version = "1.1", features = [ "nightly" ] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs new file mode 100644 index 000000000..277b985c5 --- /dev/null +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -0,0 +1,270 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../common.rs"] +mod common; + +use defmt::{error, info, unwrap}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Config, Ipv4Address, Stack, StackResources}; +use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::{self, Spim}; +use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::{with_timeout, Duration, Timer}; +use embedded_hal_async::spi::ExclusiveDevice; +use static_cell::make_static; +use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; + +teleprobe_meta::timeout!(120); + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn wifi_task( + runner: hosted::Runner< + 'static, + ExclusiveDevice, Output<'static, peripherals::P0_31>>, + Input<'static, AnyPin>, + Output<'static, peripherals::P1_05>, + >, +) -> ! { + runner.run().await +} + +type MyDriver = hosted::NetDriver<'static>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_nrf::init(Default::default()); + + let miso = p.P0_28; + let sck = p.P0_29; + let mosi = p.P0_30; + let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive); + let handshake = Input::new(p.P1_01.degrade(), Pull::Up); + let ready = Input::new(p.P1_04.degrade(), Pull::None); + let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard); + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M32; + config.mode = spim::MODE_2; // !!! + let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); + let spi = ExclusiveDevice::new(spi, cs); + + let (device, mut control, runner) = embassy_net_esp_hosted::new( + make_static!(embassy_net_esp_hosted::State::new()), + spi, + handshake, + ready, + reset, + ) + .await; + + unwrap!(spawner.spawn(wifi_task(runner))); + + control.init().await; + control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + Config::dhcpv4(Default::default()), + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + info!("Waiting for DHCP up..."); + while stack.config_v4().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); + assert!(up > TEST_EXPECTED_UPLOAD_KBPS); + assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +// Test-only wifi network, no internet access! +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + +const TEST_DURATION: usize = 10; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 150; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 150; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &'static Stack) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &'static Stack) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &'static Stack) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 7a94ea191..9fc537a4b 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -63,20 +63,13 @@ async fn main(spawner: Spawner) { .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; - let config = Config::dhcpv4(Default::default()); - //let config = embassy_net::Config::Static(embassy_net::Config { - // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), - // dns_servers: Vec::new(), - // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), - //}); - // Generate random seed let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. // Init network stack let stack = &*make_static!(Stack::new( net_device, - config, + Config::dhcpv4(Default::default()), make_static!(StackResources::<2>::new()), seed ));