diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-08-18 14:21:10 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-08-18 14:21:10 +0000 |
| commit | d327b626e30b5073cf3534c44b608bdb6598222c (patch) | |
| tree | b0f8d7e9cd28ee891e7573ae433f1c60606c4aab | |
| parent | f48d13a16a5bc9b1027ea39bfd86d548b460edd5 (diff) | |
| parent | 1cb76e0d99f8da12891491b31176c8247a7a81a4 (diff) | |
Merge pull request #1788 from embassy-rs/esp-hosted-cleanup
net-esp-hosted: misc improvements.
| -rw-r--r-- | embassy-net-esp-hosted/src/control.rs | 136 | ||||
| -rw-r--r-- | embassy-net-esp-hosted/src/lib.rs | 36 | ||||
| -rw-r--r-- | examples/nrf52840/src/bin/wifi_esp_hosted.rs | 4 | ||||
| -rw-r--r-- | tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 4 |
4 files changed, 110 insertions, 70 deletions
diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 37f220da0..6cd57f68a 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs | |||
| @@ -5,9 +5,12 @@ use heapless::String; | |||
| 5 | use crate::ioctl::Shared; | 5 | use crate::ioctl::Shared; |
| 6 | use crate::proto::{self, CtrlMsg}; | 6 | use crate::proto::{self, CtrlMsg}; |
| 7 | 7 | ||
| 8 | #[derive(Debug)] | 8 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] |
| 9 | pub struct Error { | 9 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 10 | pub status: u32, | 10 | pub enum Error { |
| 11 | Failed(u32), | ||
| 12 | Timeout, | ||
| 13 | Internal, | ||
| 11 | } | 14 | } |
| 12 | 15 | ||
| 13 | pub struct Control<'a> { | 16 | pub struct Control<'a> { |
| @@ -23,58 +26,78 @@ enum WifiMode { | |||
| 23 | ApSta = 3, | 26 | ApSta = 3, |
| 24 | } | 27 | } |
| 25 | 28 | ||
| 29 | macro_rules! ioctl { | ||
| 30 | ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { | ||
| 31 | let mut msg = proto::CtrlMsg { | ||
| 32 | msg_id: proto::CtrlMsgId::$req_variant as _, | ||
| 33 | msg_type: proto::CtrlMsgType::Req as _, | ||
| 34 | payload: Some(proto::CtrlMsgPayload::$req_variant($req)), | ||
| 35 | }; | ||
| 36 | $self.ioctl(&mut msg).await?; | ||
| 37 | let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { | ||
| 38 | warn!("unexpected response variant"); | ||
| 39 | return Err(Error::Internal); | ||
| 40 | }; | ||
| 41 | if $resp.resp != 0 { | ||
| 42 | return Err(Error::Failed($resp.resp)); | ||
| 43 | } | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 26 | impl<'a> Control<'a> { | 47 | impl<'a> Control<'a> { |
| 27 | pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { | 48 | pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { |
| 28 | Self { state_ch, shared } | 49 | Self { state_ch, shared } |
| 29 | } | 50 | } |
| 30 | 51 | ||
| 31 | pub async fn init(&mut self) { | 52 | pub async fn init(&mut self) -> Result<(), Error> { |
| 32 | debug!("wait for init event..."); | 53 | debug!("wait for init event..."); |
| 33 | self.shared.init_wait().await; | 54 | self.shared.init_wait().await; |
| 34 | 55 | ||
| 56 | debug!("set heartbeat"); | ||
| 57 | self.set_heartbeat(10).await?; | ||
| 58 | |||
| 35 | debug!("set wifi mode"); | 59 | debug!("set wifi mode"); |
| 36 | self.set_wifi_mode(WifiMode::Sta as _).await; | 60 | self.set_wifi_mode(WifiMode::Sta as _).await?; |
| 37 | 61 | ||
| 38 | let mac_addr = self.get_mac_addr().await; | 62 | let mac_addr = self.get_mac_addr().await?; |
| 39 | debug!("mac addr: {:02x}", mac_addr); | 63 | debug!("mac addr: {:02x}", mac_addr); |
| 40 | self.state_ch.set_ethernet_address(mac_addr); | 64 | self.state_ch.set_ethernet_address(mac_addr); |
| 65 | |||
| 66 | Ok(()) | ||
| 41 | } | 67 | } |
| 42 | 68 | ||
| 43 | pub async fn join(&mut self, ssid: &str, password: &str) { | 69 | pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> { |
| 44 | let req = proto::CtrlMsg { | 70 | let req = proto::CtrlMsgReqConnectAp { |
| 45 | msg_id: proto::CtrlMsgId::ReqConnectAp as _, | 71 | ssid: String::from(ssid), |
| 46 | msg_type: proto::CtrlMsgType::Req as _, | 72 | pwd: String::from(password), |
| 47 | payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { | 73 | bssid: String::new(), |
| 48 | ssid: String::from(ssid), | 74 | listen_interval: 3, |
| 49 | pwd: String::from(password), | 75 | is_wpa3_supported: false, |
| 50 | bssid: String::new(), | ||
| 51 | listen_interval: 3, | ||
| 52 | is_wpa3_supported: false, | ||
| 53 | })), | ||
| 54 | }; | ||
| 55 | let resp = self.ioctl(req).await; | ||
| 56 | let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { | ||
| 57 | panic!("unexpected resp") | ||
| 58 | }; | 76 | }; |
| 59 | assert_eq!(resp.resp, 0); | 77 | ioctl!(self, ReqConnectAp, RespConnectAp, req, resp); |
| 60 | self.state_ch.set_link_state(LinkState::Up); | 78 | self.state_ch.set_link_state(LinkState::Up); |
| 79 | Ok(()) | ||
| 61 | } | 80 | } |
| 62 | 81 | ||
| 63 | async fn get_mac_addr(&mut self) -> [u8; 6] { | 82 | pub async fn disconnect(&mut self) -> Result<(), Error> { |
| 64 | let req = proto::CtrlMsg { | 83 | let req = proto::CtrlMsgReqGetStatus {}; |
| 65 | msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, | 84 | ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp); |
| 66 | msg_type: proto::CtrlMsgType::Req as _, | 85 | self.state_ch.set_link_state(LinkState::Up); |
| 67 | payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( | 86 | Ok(()) |
| 68 | proto::CtrlMsgReqGetMacAddress { | 87 | } |
| 69 | mode: WifiMode::Sta as _, | 88 | |
| 70 | }, | 89 | /// duration in seconds, clamped to [10, 3600] |
| 71 | )), | 90 | async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { |
| 72 | }; | 91 | let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration }; |
| 73 | let resp = self.ioctl(req).await; | 92 | ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp); |
| 74 | let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { | 93 | Ok(()) |
| 75 | panic!("unexpected resp") | 94 | } |
| 95 | |||
| 96 | async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> { | ||
| 97 | let req = proto::CtrlMsgReqGetMacAddress { | ||
| 98 | mode: WifiMode::Sta as _, | ||
| 76 | }; | 99 | }; |
| 77 | assert_eq!(resp.resp, 0); | 100 | ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); |
| 78 | 101 | ||
| 79 | // WHY IS THIS A STRING? WHYYYY | 102 | // WHY IS THIS A STRING? WHYYYY |
| 80 | fn nibble_from_hex(b: u8) -> u8 { | 103 | fn nibble_from_hex(b: u8) -> u8 { |
| @@ -88,32 +111,32 @@ impl<'a> Control<'a> { | |||
| 88 | 111 | ||
| 89 | let mac = resp.mac.as_bytes(); | 112 | let mac = resp.mac.as_bytes(); |
| 90 | let mut res = [0; 6]; | 113 | let mut res = [0; 6]; |
| 91 | assert_eq!(mac.len(), 17); | 114 | if mac.len() != 17 { |
| 115 | warn!("unexpected MAC respnse length"); | ||
| 116 | return Err(Error::Internal); | ||
| 117 | } | ||
| 92 | for (i, b) in res.iter_mut().enumerate() { | 118 | for (i, b) in res.iter_mut().enumerate() { |
| 93 | *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) | 119 | *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) |
| 94 | } | 120 | } |
| 95 | res | 121 | Ok(res) |
| 96 | } | 122 | } |
| 97 | 123 | ||
| 98 | async fn set_wifi_mode(&mut self, mode: u32) { | 124 | async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { |
| 99 | let req = proto::CtrlMsg { | 125 | let req = proto::CtrlMsgReqSetMode { mode }; |
| 100 | msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, | 126 | ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp); |
| 101 | msg_type: proto::CtrlMsgType::Req as _, | 127 | |
| 102 | payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), | 128 | Ok(()) |
| 103 | }; | ||
| 104 | let resp = self.ioctl(req).await; | ||
| 105 | let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { | ||
| 106 | panic!("unexpected resp") | ||
| 107 | }; | ||
| 108 | assert_eq!(resp.resp, 0); | ||
| 109 | } | 129 | } |
| 110 | 130 | ||
| 111 | async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { | 131 | async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { |
| 112 | debug!("ioctl req: {:?}", &req); | 132 | debug!("ioctl req: {:?}", &msg); |
| 113 | 133 | ||
| 114 | let mut buf = [0u8; 128]; | 134 | let mut buf = [0u8; 128]; |
| 115 | 135 | ||
| 116 | let req_len = noproto::write(&req, &mut buf).unwrap(); | 136 | let req_len = noproto::write(msg, &mut buf).map_err(|_| { |
| 137 | warn!("failed to serialize control request"); | ||
| 138 | Error::Internal | ||
| 139 | })?; | ||
| 117 | 140 | ||
| 118 | struct CancelOnDrop<'a>(&'a Shared); | 141 | struct CancelOnDrop<'a>(&'a Shared); |
| 119 | 142 | ||
| @@ -135,9 +158,12 @@ impl<'a> Control<'a> { | |||
| 135 | 158 | ||
| 136 | ioctl.defuse(); | 159 | ioctl.defuse(); |
| 137 | 160 | ||
| 138 | let res = noproto::read(&buf[..resp_len]).unwrap(); | 161 | *msg = noproto::read(&buf[..resp_len]).map_err(|_| { |
| 139 | debug!("ioctl resp: {:?}", &res); | 162 | warn!("failed to serialize control request"); |
| 163 | Error::Internal | ||
| 164 | })?; | ||
| 165 | debug!("ioctl resp: {:?}", msg); | ||
| 140 | 166 | ||
| 141 | res | 167 | Ok(()) |
| 142 | } | 168 | } |
| 143 | } | 169 | } |
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 96fddce58..4a318b20d 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs | |||
| @@ -1,17 +1,15 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | 2 | ||
| 3 | use control::Control; | 3 | use embassy_futures::select::{select4, Either4}; |
| 4 | use embassy_futures::select::{select3, Either3}; | ||
| 5 | use embassy_net_driver_channel as ch; | 4 | use embassy_net_driver_channel as ch; |
| 5 | use embassy_net_driver_channel::driver::LinkState; | ||
| 6 | use embassy_time::{Duration, Instant, Timer}; | 6 | use embassy_time::{Duration, Instant, Timer}; |
| 7 | use embedded_hal::digital::{InputPin, OutputPin}; | 7 | use embedded_hal::digital::{InputPin, OutputPin}; |
| 8 | use embedded_hal_async::digital::Wait; | 8 | use embedded_hal_async::digital::Wait; |
| 9 | use embedded_hal_async::spi::SpiDevice; | 9 | use embedded_hal_async::spi::SpiDevice; |
| 10 | use ioctl::Shared; | ||
| 11 | use proto::CtrlMsg; | ||
| 12 | 10 | ||
| 13 | use crate::ioctl::PendingIoctl; | 11 | use crate::ioctl::{PendingIoctl, Shared}; |
| 14 | use crate::proto::CtrlMsgPayload; | 12 | use crate::proto::{CtrlMsg, CtrlMsgPayload}; |
| 15 | 13 | ||
| 16 | mod proto; | 14 | mod proto; |
| 17 | 15 | ||
| @@ -21,6 +19,8 @@ mod fmt; | |||
| 21 | mod control; | 19 | mod control; |
| 22 | mod ioctl; | 20 | mod ioctl; |
| 23 | 21 | ||
| 22 | pub use control::*; | ||
| 23 | |||
| 24 | const MTU: usize = 1514; | 24 | const MTU: usize = 1514; |
| 25 | 25 | ||
| 26 | macro_rules! impl_bytes { | 26 | macro_rules! impl_bytes { |
| @@ -95,6 +95,7 @@ enum InterfaceType { | |||
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | const MAX_SPI_BUFFER_SIZE: usize = 1600; | 97 | const MAX_SPI_BUFFER_SIZE: usize = 1600; |
| 98 | const HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20); | ||
| 98 | 99 | ||
| 99 | pub struct State { | 100 | pub struct State { |
| 100 | shared: Shared, | 101 | shared: Shared, |
| @@ -129,12 +130,14 @@ where | |||
| 129 | 130 | ||
| 130 | let mut runner = Runner { | 131 | let mut runner = Runner { |
| 131 | ch: ch_runner, | 132 | ch: ch_runner, |
| 133 | state_ch, | ||
| 132 | shared: &state.shared, | 134 | shared: &state.shared, |
| 133 | next_seq: 1, | 135 | next_seq: 1, |
| 134 | handshake, | 136 | handshake, |
| 135 | ready, | 137 | ready, |
| 136 | reset, | 138 | reset, |
| 137 | spi, | 139 | spi, |
| 140 | heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, | ||
| 138 | }; | 141 | }; |
| 139 | runner.init().await; | 142 | runner.init().await; |
| 140 | 143 | ||
| @@ -143,9 +146,11 @@ where | |||
| 143 | 146 | ||
| 144 | pub struct Runner<'a, SPI, IN, OUT> { | 147 | pub struct Runner<'a, SPI, IN, OUT> { |
| 145 | ch: ch::Runner<'a, MTU>, | 148 | ch: ch::Runner<'a, MTU>, |
| 149 | state_ch: ch::StateRunner<'a>, | ||
| 146 | shared: &'a Shared, | 150 | shared: &'a Shared, |
| 147 | 151 | ||
| 148 | next_seq: u16, | 152 | next_seq: u16, |
| 153 | heartbeat_deadline: Instant, | ||
| 149 | 154 | ||
| 150 | spi: SPI, | 155 | spi: SPI, |
| 151 | handshake: IN, | 156 | handshake: IN, |
| @@ -177,9 +182,10 @@ where | |||
| 177 | let ioctl = self.shared.ioctl_wait_pending(); | 182 | let ioctl = self.shared.ioctl_wait_pending(); |
| 178 | let tx = self.ch.tx_buf(); | 183 | let tx = self.ch.tx_buf(); |
| 179 | let ev = async { self.ready.wait_for_high().await.unwrap() }; | 184 | let ev = async { self.ready.wait_for_high().await.unwrap() }; |
| 185 | let hb = Timer::at(self.heartbeat_deadline); | ||
| 180 | 186 | ||
| 181 | match select3(ioctl, tx, ev).await { | 187 | match select4(ioctl, tx, ev, hb).await { |
| 182 | Either3::First(PendingIoctl { buf, req_len }) => { | 188 | Either4::First(PendingIoctl { buf, req_len }) => { |
| 183 | tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); | 189 | tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); |
| 184 | tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); | 190 | tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); |
| 185 | tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); | 191 | tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); |
| @@ -198,7 +204,7 @@ where | |||
| 198 | header.checksum = checksum(&tx_buf[..26 + req_len]); | 204 | header.checksum = checksum(&tx_buf[..26 + req_len]); |
| 199 | tx_buf[0..12].copy_from_slice(&header.to_bytes()); | 205 | tx_buf[0..12].copy_from_slice(&header.to_bytes()); |
| 200 | } | 206 | } |
| 201 | Either3::Second(packet) => { | 207 | Either4::Second(packet) => { |
| 202 | tx_buf[12..][..packet.len()].copy_from_slice(packet); | 208 | tx_buf[12..][..packet.len()].copy_from_slice(packet); |
| 203 | 209 | ||
| 204 | let mut header = PayloadHeader { | 210 | let mut header = PayloadHeader { |
| @@ -217,9 +223,12 @@ where | |||
| 217 | 223 | ||
| 218 | self.ch.tx_done(); | 224 | self.ch.tx_done(); |
| 219 | } | 225 | } |
| 220 | Either3::Third(()) => { | 226 | Either4::Third(()) => { |
| 221 | tx_buf[..PayloadHeader::SIZE].fill(0); | 227 | tx_buf[..PayloadHeader::SIZE].fill(0); |
| 222 | } | 228 | } |
| 229 | Either4::Fourth(()) => { | ||
| 230 | panic!("heartbeat from esp32 stopped") | ||
| 231 | } | ||
| 223 | } | 232 | } |
| 224 | 233 | ||
| 225 | if tx_buf[0] != 0 { | 234 | if tx_buf[0] != 0 { |
| @@ -308,7 +317,7 @@ where | |||
| 308 | } | 317 | } |
| 309 | } | 318 | } |
| 310 | 319 | ||
| 311 | fn handle_event(&self, data: &[u8]) { | 320 | fn handle_event(&mut self, data: &[u8]) { |
| 312 | let Ok(event) = noproto::read::<CtrlMsg>(data) else { | 321 | let Ok(event) = noproto::read::<CtrlMsg>(data) else { |
| 313 | warn!("failed to parse event"); | 322 | warn!("failed to parse event"); |
| 314 | return; | 323 | return; |
| @@ -323,6 +332,11 @@ where | |||
| 323 | 332 | ||
| 324 | match payload { | 333 | match payload { |
| 325 | CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), | 334 | CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), |
| 335 | CtrlMsgPayload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP, | ||
| 336 | CtrlMsgPayload::EventStationDisconnectFromAp(e) => { | ||
| 337 | info!("disconnected, code {}", e.resp); | ||
| 338 | self.state_ch.set_link_state(LinkState::Down); | ||
| 339 | } | ||
| 326 | _ => {} | 340 | _ => {} |
| 327 | } | 341 | } |
| 328 | } | 342 | } |
diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index e114e50b9..a60822fd9 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs | |||
| @@ -72,8 +72,8 @@ async fn main(spawner: Spawner) { | |||
| 72 | 72 | ||
| 73 | unwrap!(spawner.spawn(wifi_task(runner))); | 73 | unwrap!(spawner.spawn(wifi_task(runner))); |
| 74 | 74 | ||
| 75 | control.init().await; | 75 | unwrap!(control.init().await); |
| 76 | control.join(WIFI_NETWORK, WIFI_PASSWORD).await; | 76 | unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); |
| 77 | 77 | ||
| 78 | let config = embassy_net::Config::dhcpv4(Default::default()); | 78 | let config = embassy_net::Config::dhcpv4(Default::default()); |
| 79 | // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { | 79 | // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { |
diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index ee46af2a3..97ebafec8 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs | |||
| @@ -73,8 +73,8 @@ async fn main(spawner: Spawner) { | |||
| 73 | 73 | ||
| 74 | unwrap!(spawner.spawn(wifi_task(runner))); | 74 | unwrap!(spawner.spawn(wifi_task(runner))); |
| 75 | 75 | ||
| 76 | control.init().await; | 76 | unwrap!(control.init().await); |
| 77 | control.join(WIFI_NETWORK, WIFI_PASSWORD).await; | 77 | unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); |
| 78 | 78 | ||
| 79 | // Generate random seed | 79 | // Generate random seed |
| 80 | let mut rng = Rng::new(p.RNG, Irqs); | 80 | let mut rng = Rng::new(p.RNG, Irqs); |
