diff options
| author | Raul Alimbekov <[email protected]> | 2025-12-16 09:05:22 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-16 09:05:22 +0300 |
| commit | c9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch) | |
| tree | 6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /embassy-net-esp-hosted/src/lib.rs | |
| parent | cde24a3ef1117653ba5ed4184102b33f745782fb (diff) | |
| parent | 5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff) | |
Merge branch 'main' into main
Diffstat (limited to 'embassy-net-esp-hosted/src/lib.rs')
| -rw-r--r-- | embassy-net-esp-hosted/src/lib.rs | 78 |
1 files changed, 41 insertions, 37 deletions
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index f05e2a70a..2c7377281 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs | |||
| @@ -1,27 +1,34 @@ | |||
| 1 | #![no_std] | 1 | #![no_std] |
| 2 | #![doc = include_str!("../README.md")] | 2 | #![doc = include_str!("../README.md")] |
| 3 | #![warn(missing_docs)] | 3 | #![warn(missing_docs)] |
| 4 | #![allow(async_fn_in_trait)] | ||
| 4 | 5 | ||
| 5 | use embassy_futures::select::{select4, Either4}; | 6 | use embassy_futures::select::{Either4, select4}; |
| 6 | use embassy_net_driver_channel as ch; | 7 | use embassy_net_driver_channel as ch; |
| 7 | use embassy_net_driver_channel::driver::LinkState; | 8 | use embassy_net_driver_channel::driver::LinkState; |
| 8 | use embassy_time::{Duration, Instant, Timer}; | 9 | use embassy_time::{Duration, Instant, Timer}; |
| 9 | use embedded_hal::digital::{InputPin, OutputPin}; | 10 | use embedded_hal::digital::OutputPin; |
| 10 | use embedded_hal_async::digital::Wait; | ||
| 11 | use embedded_hal_async::spi::SpiDevice; | ||
| 12 | 11 | ||
| 13 | use crate::ioctl::{PendingIoctl, Shared}; | 12 | use crate::ioctl::{PendingIoctl, Shared}; |
| 14 | use crate::proto::{CtrlMsg, CtrlMsgPayload}; | 13 | use crate::proto::{CtrlMsg, CtrlMsg_}; |
| 15 | 14 | ||
| 15 | #[allow(unused)] | ||
| 16 | #[allow(non_snake_case)] | ||
| 17 | #[allow(non_camel_case_types)] | ||
| 18 | #[allow(non_upper_case_globals)] | ||
| 19 | #[allow(missing_docs)] | ||
| 20 | #[allow(clippy::all)] | ||
| 16 | mod proto; | 21 | mod proto; |
| 17 | 22 | ||
| 18 | // must be first | 23 | // must be first |
| 19 | mod fmt; | 24 | mod fmt; |
| 20 | 25 | ||
| 21 | mod control; | 26 | mod control; |
| 27 | mod iface; | ||
| 22 | mod ioctl; | 28 | mod ioctl; |
| 23 | 29 | ||
| 24 | pub use control::*; | 30 | pub use control::*; |
| 31 | pub use iface::*; | ||
| 25 | 32 | ||
| 26 | const MTU: usize = 1514; | 33 | const MTU: usize = 1514; |
| 27 | 34 | ||
| @@ -118,20 +125,17 @@ impl State { | |||
| 118 | /// Type alias for network driver. | 125 | /// Type alias for network driver. |
| 119 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | 126 | pub type NetDriver<'a> = ch::Device<'a, MTU>; |
| 120 | 127 | ||
| 121 | /// Create a new esp-hosted driver using the provided state, SPI peripheral and pins. | 128 | /// Create a new esp-hosted driver using the provided state, interface, and reset pin. |
| 122 | /// | 129 | /// |
| 123 | /// Returns a device handle for interfacing with embassy-net, a control handle for | 130 | /// Returns a device handle for interfacing with embassy-net, a control handle for |
| 124 | /// interacting with the driver, and a runner for communicating with the WiFi device. | 131 | /// interacting with the driver, and a runner for communicating with the WiFi device. |
| 125 | pub async fn new<'a, SPI, IN, OUT>( | 132 | pub async fn new<'a, I, OUT>( |
| 126 | state: &'a mut State, | 133 | state: &'a mut State, |
| 127 | spi: SPI, | 134 | iface: I, |
| 128 | handshake: IN, | ||
| 129 | ready: IN, | ||
| 130 | reset: OUT, | 135 | reset: OUT, |
| 131 | ) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>) | 136 | ) -> (NetDriver<'a>, Control<'a>, Runner<'a, I, OUT>) |
| 132 | where | 137 | where |
| 133 | SPI: SpiDevice, | 138 | I: Interface, |
| 134 | IN: InputPin + Wait, | ||
| 135 | OUT: OutputPin, | 139 | OUT: OutputPin, |
| 136 | { | 140 | { |
| 137 | let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); | 141 | let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); |
| @@ -142,10 +146,8 @@ where | |||
| 142 | state_ch, | 146 | state_ch, |
| 143 | shared: &state.shared, | 147 | shared: &state.shared, |
| 144 | next_seq: 1, | 148 | next_seq: 1, |
| 145 | handshake, | ||
| 146 | ready, | ||
| 147 | reset, | 149 | reset, |
| 148 | spi, | 150 | iface, |
| 149 | heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, | 151 | heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, |
| 150 | }; | 152 | }; |
| 151 | 153 | ||
| @@ -153,7 +155,7 @@ where | |||
| 153 | } | 155 | } |
| 154 | 156 | ||
| 155 | /// Runner for communicating with the WiFi device. | 157 | /// Runner for communicating with the WiFi device. |
| 156 | pub struct Runner<'a, SPI, IN, OUT> { | 158 | pub struct Runner<'a, I, OUT> { |
| 157 | ch: ch::Runner<'a, MTU>, | 159 | ch: ch::Runner<'a, MTU>, |
| 158 | state_ch: ch::StateRunner<'a>, | 160 | state_ch: ch::StateRunner<'a>, |
| 159 | shared: &'a Shared, | 161 | shared: &'a Shared, |
| @@ -161,16 +163,13 @@ pub struct Runner<'a, SPI, IN, OUT> { | |||
| 161 | next_seq: u16, | 163 | next_seq: u16, |
| 162 | heartbeat_deadline: Instant, | 164 | heartbeat_deadline: Instant, |
| 163 | 165 | ||
| 164 | spi: SPI, | 166 | iface: I, |
| 165 | handshake: IN, | ||
| 166 | ready: IN, | ||
| 167 | reset: OUT, | 167 | reset: OUT, |
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT> | 170 | impl<'a, I, OUT> Runner<'a, I, OUT> |
| 171 | where | 171 | where |
| 172 | SPI: SpiDevice, | 172 | I: Interface, |
| 173 | IN: InputPin + Wait, | ||
| 174 | OUT: OutputPin, | 173 | OUT: OutputPin, |
| 175 | { | 174 | { |
| 176 | /// Run the packet processing. | 175 | /// Run the packet processing. |
| @@ -185,11 +184,11 @@ where | |||
| 185 | let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; | 184 | let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; |
| 186 | 185 | ||
| 187 | loop { | 186 | loop { |
| 188 | self.handshake.wait_for_high().await.unwrap(); | 187 | self.iface.wait_for_handshake().await; |
| 189 | 188 | ||
| 190 | let ioctl = self.shared.ioctl_wait_pending(); | 189 | let ioctl = self.shared.ioctl_wait_pending(); |
| 191 | let tx = self.ch.tx_buf(); | 190 | let tx = self.ch.tx_buf(); |
| 192 | let ev = async { self.ready.wait_for_high().await.unwrap() }; | 191 | let ev = async { self.iface.wait_for_ready().await }; |
| 193 | let hb = Timer::at(self.heartbeat_deadline); | 192 | let hb = Timer::at(self.heartbeat_deadline); |
| 194 | 193 | ||
| 195 | match select4(ioctl, tx, ev, hb).await { | 194 | match select4(ioctl, tx, ev, hb).await { |
| @@ -235,6 +234,11 @@ where | |||
| 235 | tx_buf[..PayloadHeader::SIZE].fill(0); | 234 | tx_buf[..PayloadHeader::SIZE].fill(0); |
| 236 | } | 235 | } |
| 237 | Either4::Fourth(()) => { | 236 | Either4::Fourth(()) => { |
| 237 | // Extend the deadline if initializing | ||
| 238 | if let ioctl::ControlState::Reboot = self.shared.state() { | ||
| 239 | self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP; | ||
| 240 | continue; | ||
| 241 | } | ||
| 238 | panic!("heartbeat from esp32 stopped") | 242 | panic!("heartbeat from esp32 stopped") |
| 239 | } | 243 | } |
| 240 | } | 244 | } |
| @@ -243,15 +247,9 @@ where | |||
| 243 | trace!("tx: {:02x}", &tx_buf[..40]); | 247 | trace!("tx: {:02x}", &tx_buf[..40]); |
| 244 | } | 248 | } |
| 245 | 249 | ||
| 246 | self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | 250 | self.iface.transfer(&mut rx_buf, &tx_buf).await; |
| 247 | 251 | ||
| 248 | // The esp-hosted firmware deasserts the HANSHAKE pin a few us AFTER ending the SPI transfer | ||
| 249 | // If we check it again too fast, we'll see it's high from the previous transfer, and if we send it | ||
| 250 | // data it will get lost. | ||
| 251 | // Make sure we check it after 100us at minimum. | ||
| 252 | let delay_until = Instant::now() + Duration::from_micros(100); | ||
| 253 | self.handle_rx(&mut rx_buf); | 252 | self.handle_rx(&mut rx_buf); |
| 254 | Timer::at(delay_until).await; | ||
| 255 | } | 253 | } |
| 256 | } | 254 | } |
| 257 | 255 | ||
| @@ -326,10 +324,12 @@ where | |||
| 326 | } | 324 | } |
| 327 | 325 | ||
| 328 | fn handle_event(&mut self, data: &[u8]) { | 326 | fn handle_event(&mut self, data: &[u8]) { |
| 329 | let Ok(event) = noproto::read::<CtrlMsg>(data) else { | 327 | use micropb::MessageDecode; |
| 328 | let mut event = CtrlMsg::default(); | ||
| 329 | if event.decode_from_bytes(data).is_err() { | ||
| 330 | warn!("failed to parse event"); | 330 | warn!("failed to parse event"); |
| 331 | return; | 331 | return; |
| 332 | }; | 332 | } |
| 333 | 333 | ||
| 334 | debug!("event: {:?}", &event); | 334 | debug!("event: {:?}", &event); |
| 335 | 335 | ||
| @@ -339,9 +339,13 @@ where | |||
| 339 | }; | 339 | }; |
| 340 | 340 | ||
| 341 | match payload { | 341 | match payload { |
| 342 | CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), | 342 | CtrlMsg_::Payload::EventEspInit(_) => self.shared.init_done(), |
| 343 | CtrlMsgPayload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP, | 343 | CtrlMsg_::Payload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP, |
| 344 | CtrlMsgPayload::EventStationDisconnectFromAp(e) => { | 344 | CtrlMsg_::Payload::EventStationConnectedToAp(e) => { |
| 345 | info!("connected, code {}", e.resp); | ||
| 346 | self.state_ch.set_link_state(LinkState::Up); | ||
| 347 | } | ||
| 348 | CtrlMsg_::Payload::EventStationDisconnectFromAp(e) => { | ||
| 345 | info!("disconnected, code {}", e.resp); | 349 | info!("disconnected, code {}", e.resp); |
| 346 | self.state_ch.set_link_state(LinkState::Down); | 350 | self.state_ch.set_link_state(LinkState::Down); |
| 347 | } | 351 | } |
