aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-net-esp-hosted/src/iface.rs62
-rw-r--r--embassy-net-esp-hosted/src/lib.rs47
-rw-r--r--examples/nrf52840/src/bin/wifi_esp_hosted.rs16
-rw-r--r--tests/nrf/src/bin/wifi_esp_hosted_perf.rs15
4 files changed, 89 insertions, 51 deletions
diff --git a/embassy-net-esp-hosted/src/iface.rs b/embassy-net-esp-hosted/src/iface.rs
new file mode 100644
index 000000000..1f57851e0
--- /dev/null
+++ b/embassy-net-esp-hosted/src/iface.rs
@@ -0,0 +1,62 @@
1use embassy_time::Timer;
2use embedded_hal::digital::InputPin;
3use embedded_hal_async::digital::Wait;
4use embedded_hal_async::spi::SpiDevice;
5
6/// Physical interface trait for communicating with the ESP chip.
7pub trait Interface {
8 /// Wait for the HANDSHAKE signal indicating the ESP is ready for a new transaction.
9 async fn wait_for_handshake(&mut self);
10
11 /// Wait for the READY signal indicating the ESP has data to send.
12 async fn wait_for_ready(&mut self);
13
14 /// Perform a SPI transfer, exchanging data with the ESP chip.
15 async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]);
16}
17
18/// Standard SPI interface.
19///
20/// This interface is what's implemented in the upstream `esp-hosted-fg` firmware. It uses:
21/// - An `SpiDevice` for SPI communication (CS is handled by the device)
22/// - A handshake pin that signals when the ESP is ready for a new transaction
23/// - A ready pin that indicates when the ESP has data to send
24pub struct SpiInterface<SPI, IN> {
25 spi: SPI,
26 handshake: IN,
27 ready: IN,
28}
29
30impl<SPI, IN> SpiInterface<SPI, IN>
31where
32 SPI: SpiDevice,
33 IN: InputPin + Wait,
34{
35 /// Create a new SpiInterface.
36 pub fn new(spi: SPI, handshake: IN, ready: IN) -> Self {
37 Self { spi, handshake, ready }
38 }
39}
40
41impl<SPI, IN> Interface for SpiInterface<SPI, IN>
42where
43 SPI: SpiDevice,
44 IN: InputPin + Wait,
45{
46 async fn wait_for_handshake(&mut self) {
47 self.handshake.wait_for_high().await.unwrap();
48 }
49
50 async fn wait_for_ready(&mut self) {
51 self.ready.wait_for_high().await.unwrap();
52 }
53
54 async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) {
55 self.spi.transfer(rx, tx).await.unwrap();
56
57 // The esp-hosted firmware deasserts the HANDSHAKE pin a few us AFTER ending the SPI transfer
58 // If we check it again too fast, we'll see it's high from the previous transfer, and if we send it
59 // data it will get lost.
60 Timer::after_micros(100).await;
61 }
62}
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs
index 4405d9f77..8da0cd4dc 100644
--- a/embassy-net-esp-hosted/src/lib.rs
+++ b/embassy-net-esp-hosted/src/lib.rs
@@ -1,14 +1,13 @@
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
5use embassy_futures::select::{Either4, select4}; 6use embassy_futures::select::{Either4, select4};
6use embassy_net_driver_channel as ch; 7use embassy_net_driver_channel as ch;
7use embassy_net_driver_channel::driver::LinkState; 8use embassy_net_driver_channel::driver::LinkState;
8use embassy_time::{Duration, Instant, Timer}; 9use embassy_time::{Duration, Instant, Timer};
9use embedded_hal::digital::{InputPin, OutputPin}; 10use embedded_hal::digital::OutputPin;
10use embedded_hal_async::digital::Wait;
11use embedded_hal_async::spi::SpiDevice;
12 11
13use crate::ioctl::{PendingIoctl, Shared}; 12use crate::ioctl::{PendingIoctl, Shared};
14use crate::proto::{CtrlMsg, CtrlMsgPayload}; 13use crate::proto::{CtrlMsg, CtrlMsgPayload};
@@ -19,9 +18,11 @@ mod proto;
19mod fmt; 18mod fmt;
20 19
21mod control; 20mod control;
21mod iface;
22mod ioctl; 22mod ioctl;
23 23
24pub use control::*; 24pub use control::*;
25pub use iface::*;
25 26
26const MTU: usize = 1514; 27const MTU: usize = 1514;
27 28
@@ -118,20 +119,17 @@ impl State {
118/// Type alias for network driver. 119/// Type alias for network driver.
119pub type NetDriver<'a> = ch::Device<'a, MTU>; 120pub type NetDriver<'a> = ch::Device<'a, MTU>;
120 121
121/// Create a new esp-hosted driver using the provided state, SPI peripheral and pins. 122/// Create a new esp-hosted driver using the provided state, interface, and reset pin.
122/// 123///
123/// Returns a device handle for interfacing with embassy-net, a control handle for 124/// 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. 125/// interacting with the driver, and a runner for communicating with the WiFi device.
125pub async fn new<'a, SPI, IN, OUT>( 126pub async fn new<'a, I, OUT>(
126 state: &'a mut State, 127 state: &'a mut State,
127 spi: SPI, 128 iface: I,
128 handshake: IN,
129 ready: IN,
130 reset: OUT, 129 reset: OUT,
131) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>) 130) -> (NetDriver<'a>, Control<'a>, Runner<'a, I, OUT>)
132where 131where
133 SPI: SpiDevice, 132 I: Interface,
134 IN: InputPin + Wait,
135 OUT: OutputPin, 133 OUT: OutputPin,
136{ 134{
137 let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); 135 let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
@@ -142,10 +140,8 @@ where
142 state_ch, 140 state_ch,
143 shared: &state.shared, 141 shared: &state.shared,
144 next_seq: 1, 142 next_seq: 1,
145 handshake,
146 ready,
147 reset, 143 reset,
148 spi, 144 iface,
149 heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, 145 heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,
150 }; 146 };
151 147
@@ -153,7 +149,7 @@ where
153} 149}
154 150
155/// Runner for communicating with the WiFi device. 151/// Runner for communicating with the WiFi device.
156pub struct Runner<'a, SPI, IN, OUT> { 152pub struct Runner<'a, I, OUT> {
157 ch: ch::Runner<'a, MTU>, 153 ch: ch::Runner<'a, MTU>,
158 state_ch: ch::StateRunner<'a>, 154 state_ch: ch::StateRunner<'a>,
159 shared: &'a Shared, 155 shared: &'a Shared,
@@ -161,16 +157,13 @@ pub struct Runner<'a, SPI, IN, OUT> {
161 next_seq: u16, 157 next_seq: u16,
162 heartbeat_deadline: Instant, 158 heartbeat_deadline: Instant,
163 159
164 spi: SPI, 160 iface: I,
165 handshake: IN,
166 ready: IN,
167 reset: OUT, 161 reset: OUT,
168} 162}
169 163
170impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT> 164impl<'a, I, OUT> Runner<'a, I, OUT>
171where 165where
172 SPI: SpiDevice, 166 I: Interface,
173 IN: InputPin + Wait,
174 OUT: OutputPin, 167 OUT: OutputPin,
175{ 168{
176 /// Run the packet processing. 169 /// Run the packet processing.
@@ -185,11 +178,11 @@ where
185 let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; 178 let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
186 179
187 loop { 180 loop {
188 self.handshake.wait_for_high().await.unwrap(); 181 self.iface.wait_for_handshake().await;
189 182
190 let ioctl = self.shared.ioctl_wait_pending(); 183 let ioctl = self.shared.ioctl_wait_pending();
191 let tx = self.ch.tx_buf(); 184 let tx = self.ch.tx_buf();
192 let ev = async { self.ready.wait_for_high().await.unwrap() }; 185 let ev = async { self.iface.wait_for_ready().await };
193 let hb = Timer::at(self.heartbeat_deadline); 186 let hb = Timer::at(self.heartbeat_deadline);
194 187
195 match select4(ioctl, tx, ev, hb).await { 188 match select4(ioctl, tx, ev, hb).await {
@@ -243,15 +236,9 @@ where
243 trace!("tx: {:02x}", &tx_buf[..40]); 236 trace!("tx: {:02x}", &tx_buf[..40]);
244 } 237 }
245 238
246 self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); 239 self.iface.transfer(&mut rx_buf, &tx_buf).await;
247 240
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); 241 self.handle_rx(&mut rx_buf);
254 Timer::at(delay_until).await;
255 } 242 }
256 } 243 }
257 244
diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs
index 07752ffc4..2f9c06b56 100644
--- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs
+++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs
@@ -27,14 +27,12 @@ bind_interrupts!(struct Irqs {
27async fn wifi_task( 27async fn wifi_task(
28 runner: hosted::Runner< 28 runner: hosted::Runner<
29 'static, 29 'static,
30 ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, 30 hosted::SpiInterface<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Input<'static>>,
31 Input<'static>,
32 Output<'static>, 31 Output<'static>,
33 >, 32 >,
34) -> ! { 33) -> ! {
35 runner.run().await 34 runner.run().await
36} 35}
37
38#[embassy_executor::task] 36#[embassy_executor::task]
39async fn net_task(mut runner: embassy_net::Runner<'static, hosted::NetDriver<'static>>) -> ! { 37async fn net_task(mut runner: embassy_net::Runner<'static, hosted::NetDriver<'static>>) -> ! {
40 runner.run().await 38 runner.run().await
@@ -60,15 +58,11 @@ async fn main(spawner: Spawner) {
60 let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); 58 let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);
61 let spi = ExclusiveDevice::new(spi, cs, Delay); 59 let spi = ExclusiveDevice::new(spi, cs, Delay);
62 60
61 let iface = hosted::SpiInterface::new(spi, handshake, ready);
62
63 static ESP_STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new(); 63 static ESP_STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new();
64 let (device, mut control, runner) = embassy_net_esp_hosted::new( 64 let (device, mut control, runner) =
65 ESP_STATE.init(embassy_net_esp_hosted::State::new()), 65 embassy_net_esp_hosted::new(ESP_STATE.init(embassy_net_esp_hosted::State::new()), iface, reset).await;
66 spi,
67 handshake,
68 ready,
69 reset,
70 )
71 .await;
72 66
73 spawner.spawn(unwrap!(wifi_task(runner))); 67 spawner.spawn(unwrap!(wifi_task(runner)));
74 68
diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs
index 091a70ce9..ac082dbb8 100644
--- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs
+++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs
@@ -29,8 +29,7 @@ const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud";
29async fn wifi_task( 29async fn wifi_task(
30 runner: hosted::Runner< 30 runner: hosted::Runner<
31 'static, 31 'static,
32 ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, 32 hosted::SpiInterface<ExclusiveDevice<Spim<'static>, Output<'static>, Delay>, Input<'static>>,
33 Input<'static>,
34 Output<'static>, 33 Output<'static>,
35 >, 34 >,
36) -> ! { 35) -> ! {
@@ -64,15 +63,11 @@ async fn main(spawner: Spawner) {
64 let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); 63 let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);
65 let spi = ExclusiveDevice::new(spi, cs, Delay); 64 let spi = ExclusiveDevice::new(spi, cs, Delay);
66 65
66 let iface = hosted::SpiInterface::new(spi, handshake, ready);
67
67 static STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new(); 68 static STATE: StaticCell<embassy_net_esp_hosted::State> = StaticCell::new();
68 let (device, mut control, runner) = embassy_net_esp_hosted::new( 69 let (device, mut control, runner) =
69 STATE.init(embassy_net_esp_hosted::State::new()), 70 embassy_net_esp_hosted::new(STATE.init(embassy_net_esp_hosted::State::new()), iface, reset).await;
70 spi,
71 handshake,
72 ready,
73 reset,
74 )
75 .await;
76 71
77 spawner.spawn(unwrap!(wifi_task(runner))); 72 spawner.spawn(unwrap!(wifi_task(runner)));
78 73