aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-06-09 03:36:48 +0200
committerDario Nieuwenhuis <[email protected]>2023-06-22 21:12:10 +0200
commit6c123596b7d48ee66ea93e8b1515e91231e9bced (patch)
tree7f657d78d2fac5a24982d74b909789d60a2dfee7
parent1f2be2dac5eeed739d2866b9b63ca06fdd84c276 (diff)
wip: esp-hosted net driver.
-rw-r--r--embassy-net-esp-hosted/Cargo.toml20
-rw-r--r--embassy-net-esp-hosted/src/control.rs141
-rw-r--r--embassy-net-esp-hosted/src/esp_hosted_config.proto432
-rw-r--r--embassy-net-esp-hosted/src/fmt.rs257
-rw-r--r--embassy-net-esp-hosted/src/ioctl.rs119
-rw-r--r--embassy-net-esp-hosted/src/lib.rs300
-rw-r--r--embassy-net-esp-hosted/src/proto.rs598
-rw-r--r--examples/nrf52840/Cargo.toml2
-rw-r--r--examples/nrf52840/src/bin/wifi_esp_hosted.rs143
9 files changed, 2012 insertions, 0 deletions
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 @@
1[package]
2name = "embassy-net-esp-hosted"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7defmt = { version = "0.3", optional = true }
8log = { version = "0.4.14", optional = true }
9
10embassy-time = { version = "0.1.0", path = "../embassy-time" }
11embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
12embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
13embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
14
15embedded-hal = { version = "1.0.0-alpha.10" }
16embedded-hal-async = { version = "=0.2.0-alpha.1" }
17
18noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
19#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
20heapless = "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 @@
1use ch::driver::LinkState;
2use defmt::Debug2Format;
3use embassy_net_driver_channel as ch;
4use heapless::String;
5
6use crate::ioctl::IoctlState;
7use crate::proto::{self, CtrlMsg};
8
9#[derive(Debug)]
10pub struct Error {
11 pub status: u32,
12}
13
14pub struct Control<'a> {
15 state_ch: ch::StateRunner<'a>,
16 ioctl_state: &'a IoctlState,
17}
18
19enum WifiMode {
20 None = 0,
21 Sta = 1,
22 Ap = 2,
23 ApSta = 3,
24}
25
26impl<'a> Control<'a> {
27 pub(crate) fn new(state_ch: ch::StateRunner<'a>, ioctl_state: &'a IoctlState) -> Self {
28 Self { state_ch, ioctl_state }
29 }
30
31 pub async fn init(&mut self) {
32 debug!("set wifi mode");
33 self.set_wifi_mode(WifiMode::Sta as _).await;
34 let mac_addr = self.get_mac_addr().await;
35 debug!("mac addr: {:02x}", mac_addr);
36 self.state_ch.set_ethernet_address(mac_addr);
37 }
38
39 pub async fn join(&mut self, ssid: &str, password: &str) {
40 let req = proto::CtrlMsg {
41 msg_id: proto::CtrlMsgId::ReqConnectAp as _,
42 msg_type: proto::CtrlMsgType::Req as _,
43 payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp {
44 ssid: String::from(ssid),
45 pwd: String::from(password),
46 bssid: String::new(),
47 listen_interval: 3,
48 is_wpa3_supported: false,
49 })),
50 };
51 let resp = self.ioctl(req).await;
52 let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
53 debug!("======= {:?}", Debug2Format(&resp));
54 assert_eq!(resp.resp, 0);
55 self.state_ch.set_link_state(LinkState::Up);
56 }
57
58 async fn get_mac_addr(&mut self) -> [u8; 6] {
59 let req = proto::CtrlMsg {
60 msg_id: proto::CtrlMsgId::ReqGetMacAddress as _,
61 msg_type: proto::CtrlMsgType::Req as _,
62 payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress(
63 proto::CtrlMsgReqGetMacAddress {
64 mode: WifiMode::Sta as _,
65 },
66 )),
67 };
68 let resp = self.ioctl(req).await;
69 let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
70 assert_eq!(resp.resp, 0);
71
72 // WHY IS THIS A STRING? WHYYYY
73 fn nibble_from_hex(b: u8) -> u8 {
74 match b {
75 b'0'..=b'9' => b - b'0',
76 b'a'..=b'f' => b + 0xa - b'a',
77 b'A'..=b'F' => b + 0xa - b'A',
78 _ => panic!("invalid hex digit {}", b),
79 }
80 }
81
82 let mac = resp.mac.as_bytes();
83 let mut res = [0; 6];
84 assert_eq!(mac.len(), 17);
85 for (i, b) in res.iter_mut().enumerate() {
86 *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
87 }
88 res
89 }
90
91 async fn get_wifi_mode(&mut self) -> u32 {
92 let req = proto::CtrlMsg {
93 msg_id: proto::CtrlMsgId::ReqGetWifiMode as _,
94 msg_type: proto::CtrlMsgType::Req as _,
95 payload: Some(proto::CtrlMsgPayload::ReqGetWifiMode(proto::CtrlMsgReqGetMode {})),
96 };
97 let resp = self.ioctl(req).await;
98 let proto::CtrlMsgPayload::RespGetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
99 assert_eq!(resp.resp, 0);
100 resp.mode
101 }
102
103 async fn set_wifi_mode(&mut self, mode: u32) {
104 let req = proto::CtrlMsg {
105 msg_id: proto::CtrlMsgId::ReqSetWifiMode as _,
106 msg_type: proto::CtrlMsgType::Req as _,
107 payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
108 };
109 let resp = self.ioctl(req).await;
110 let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
111 assert_eq!(resp.resp, 0);
112 }
113
114 async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg {
115 let mut buf = [0u8; 128];
116
117 let req_len = noproto::write(&req, &mut buf).unwrap();
118
119 struct CancelOnDrop<'a>(&'a IoctlState);
120
121 impl CancelOnDrop<'_> {
122 fn defuse(self) {
123 core::mem::forget(self);
124 }
125 }
126
127 impl Drop for CancelOnDrop<'_> {
128 fn drop(&mut self) {
129 self.0.cancel_ioctl();
130 }
131 }
132
133 let ioctl = CancelOnDrop(self.ioctl_state);
134
135 let resp_len = ioctl.0.do_ioctl(&mut buf, req_len).await;
136
137 ioctl.defuse();
138
139 noproto::read(&buf[..resp_len]).unwrap()
140 }
141}
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 @@
1syntax = "proto3";
2
3/* Enums similar to ESP IDF */
4enum Ctrl_VendorIEType {
5 Beacon = 0;
6 Probe_req = 1;
7 Probe_resp = 2;
8 Assoc_req = 3;
9 Assoc_resp = 4;
10}
11
12enum Ctrl_VendorIEID {
13 ID_0 = 0;
14 ID_1 = 1;
15}
16
17enum Ctrl_WifiMode {
18 NONE = 0;
19 STA = 1;
20 AP = 2;
21 APSTA = 3;
22}
23
24enum Ctrl_WifiBw {
25 BW_Invalid = 0;
26 HT20 = 1;
27 HT40 = 2;
28}
29
30enum Ctrl_WifiPowerSave {
31 PS_Invalid = 0;
32 MIN_MODEM = 1;
33 MAX_MODEM = 2;
34}
35
36enum Ctrl_WifiSecProt {
37 Open = 0;
38 WEP = 1;
39 WPA_PSK = 2;
40 WPA2_PSK = 3;
41 WPA_WPA2_PSK = 4;
42 WPA2_ENTERPRISE = 5;
43 WPA3_PSK = 6;
44 WPA2_WPA3_PSK = 7;
45}
46
47/* enums for Control path */
48enum Ctrl_Status {
49 Connected = 0;
50 Not_Connected = 1;
51 No_AP_Found = 2;
52 Connection_Fail = 3;
53 Invalid_Argument = 4;
54 Out_Of_Range = 5;
55}
56
57
58enum CtrlMsgType {
59 MsgType_Invalid = 0;
60 Req = 1;
61 Resp = 2;
62 Event = 3;
63 MsgType_Max = 4;
64}
65
66enum CtrlMsgId {
67 MsgId_Invalid = 0;
68
69 /** Request Msgs **/
70 Req_Base = 100;
71
72 Req_GetMACAddress = 101;
73 Req_SetMacAddress = 102;
74 Req_GetWifiMode = 103;
75 Req_SetWifiMode = 104;
76
77 Req_GetAPScanList = 105;
78 Req_GetAPConfig = 106;
79 Req_ConnectAP = 107;
80 Req_DisconnectAP = 108;
81
82 Req_GetSoftAPConfig = 109;
83 Req_SetSoftAPVendorSpecificIE = 110;
84 Req_StartSoftAP = 111;
85 Req_GetSoftAPConnectedSTAList = 112;
86 Req_StopSoftAP = 113;
87
88 Req_SetPowerSaveMode = 114;
89 Req_GetPowerSaveMode = 115;
90
91 Req_OTABegin = 116;
92 Req_OTAWrite = 117;
93 Req_OTAEnd = 118;
94
95 Req_SetWifiMaxTxPower = 119;
96 Req_GetWifiCurrTxPower = 120;
97
98 Req_ConfigHeartbeat = 121;
99 /* Add new control path command response before Req_Max
100 * and update Req_Max */
101 Req_Max = 122;
102
103 /** Response Msgs **/
104 Resp_Base = 200;
105
106 Resp_GetMACAddress = 201;
107 Resp_SetMacAddress = 202;
108 Resp_GetWifiMode = 203;
109 Resp_SetWifiMode = 204;
110
111 Resp_GetAPScanList = 205;
112 Resp_GetAPConfig = 206;
113 Resp_ConnectAP = 207;
114 Resp_DisconnectAP = 208;
115
116 Resp_GetSoftAPConfig = 209;
117 Resp_SetSoftAPVendorSpecificIE = 210;
118 Resp_StartSoftAP = 211;
119 Resp_GetSoftAPConnectedSTAList = 212;
120 Resp_StopSoftAP = 213;
121
122 Resp_SetPowerSaveMode = 214;
123 Resp_GetPowerSaveMode = 215;
124
125 Resp_OTABegin = 216;
126 Resp_OTAWrite = 217;
127 Resp_OTAEnd = 218;
128
129 Resp_SetWifiMaxTxPower = 219;
130 Resp_GetWifiCurrTxPower = 220;
131
132 Resp_ConfigHeartbeat = 221;
133 /* Add new control path command response before Resp_Max
134 * and update Resp_Max */
135 Resp_Max = 222;
136
137 /** Event Msgs **/
138 Event_Base = 300;
139 Event_ESPInit = 301;
140 Event_Heartbeat = 302;
141 Event_StationDisconnectFromAP = 303;
142 Event_StationDisconnectFromESPSoftAP = 304;
143 /* Add new control path command notification before Event_Max
144 * and update Event_Max */
145 Event_Max = 305;
146}
147
148/* internal supporting structures for CtrlMsg */
149message ScanResult {
150 bytes ssid = 1;
151 uint32 chnl = 2;
152 int32 rssi = 3;
153 bytes bssid = 4;
154 Ctrl_WifiSecProt sec_prot = 5;
155}
156
157message ConnectedSTAList {
158 bytes mac = 1;
159 int32 rssi = 2;
160}
161
162
163/* Control path structures */
164/** Req/Resp structure **/
165message CtrlMsg_Req_GetMacAddress {
166 int32 mode = 1;
167}
168
169message CtrlMsg_Resp_GetMacAddress {
170 bytes mac = 1;
171 int32 resp = 2;
172}
173
174message CtrlMsg_Req_GetMode {
175}
176
177message CtrlMsg_Resp_GetMode {
178 int32 mode = 1;
179 int32 resp = 2;
180}
181
182message CtrlMsg_Req_SetMode {
183 int32 mode = 1;
184}
185
186message CtrlMsg_Resp_SetMode {
187 int32 resp = 1;
188}
189
190message CtrlMsg_Req_GetStatus {
191}
192
193message CtrlMsg_Resp_GetStatus {
194 int32 resp = 1;
195}
196
197message CtrlMsg_Req_SetMacAddress {
198 bytes mac = 1;
199 int32 mode = 2;
200}
201
202message CtrlMsg_Resp_SetMacAddress {
203 int32 resp = 1;
204}
205
206message CtrlMsg_Req_GetAPConfig {
207}
208
209message CtrlMsg_Resp_GetAPConfig {
210 bytes ssid = 1;
211 bytes bssid = 2;
212 int32 rssi = 3;
213 int32 chnl = 4;
214 Ctrl_WifiSecProt sec_prot = 5;
215 int32 resp = 6;
216}
217
218message CtrlMsg_Req_ConnectAP {
219 string ssid = 1;
220 string pwd = 2;
221 string bssid = 3;
222 bool is_wpa3_supported = 4;
223 int32 listen_interval = 5;
224}
225
226message CtrlMsg_Resp_ConnectAP {
227 int32 resp = 1;
228 bytes mac = 2;
229}
230
231message CtrlMsg_Req_GetSoftAPConfig {
232}
233
234message CtrlMsg_Resp_GetSoftAPConfig {
235 bytes ssid = 1;
236 bytes pwd = 2;
237 int32 chnl = 3;
238 Ctrl_WifiSecProt sec_prot = 4;
239 int32 max_conn = 5;
240 bool ssid_hidden = 6;
241 int32 bw = 7;
242 int32 resp = 8;
243}
244
245message CtrlMsg_Req_StartSoftAP {
246 string ssid = 1;
247 string pwd = 2;
248 int32 chnl = 3;
249 Ctrl_WifiSecProt sec_prot = 4;
250 int32 max_conn = 5;
251 bool ssid_hidden = 6;
252 int32 bw = 7;
253}
254
255message CtrlMsg_Resp_StartSoftAP {
256 int32 resp = 1;
257 bytes mac = 2;
258}
259
260message CtrlMsg_Req_ScanResult {
261}
262
263message CtrlMsg_Resp_ScanResult {
264 uint32 count = 1;
265 repeated ScanResult entries = 2;
266 int32 resp = 3;
267}
268
269message CtrlMsg_Req_SoftAPConnectedSTA {
270}
271
272message CtrlMsg_Resp_SoftAPConnectedSTA {
273 uint32 num = 1;
274 repeated ConnectedSTAList stations = 2;
275 int32 resp = 3;
276}
277
278message CtrlMsg_Req_OTABegin {
279}
280
281message CtrlMsg_Resp_OTABegin {
282 int32 resp = 1;
283}
284
285message CtrlMsg_Req_OTAWrite {
286 bytes ota_data = 1;
287}
288
289message CtrlMsg_Resp_OTAWrite {
290 int32 resp = 1;
291}
292
293message CtrlMsg_Req_OTAEnd {
294}
295
296message CtrlMsg_Resp_OTAEnd {
297 int32 resp = 1;
298}
299
300message CtrlMsg_Req_VendorIEData {
301 int32 element_id = 1;
302 int32 length = 2;
303 bytes vendor_oui = 3;
304 int32 vendor_oui_type = 4;
305 bytes payload = 5;
306}
307
308message CtrlMsg_Req_SetSoftAPVendorSpecificIE {
309 bool enable = 1;
310 Ctrl_VendorIEType type = 2;
311 Ctrl_VendorIEID idx = 3;
312 CtrlMsg_Req_VendorIEData vendor_ie_data = 4;
313}
314
315message CtrlMsg_Resp_SetSoftAPVendorSpecificIE {
316 int32 resp = 1;
317}
318
319message CtrlMsg_Req_SetWifiMaxTxPower {
320 int32 wifi_max_tx_power = 1;
321}
322
323message CtrlMsg_Resp_SetWifiMaxTxPower {
324 int32 resp = 1;
325}
326
327message CtrlMsg_Req_GetWifiCurrTxPower {
328}
329
330message CtrlMsg_Resp_GetWifiCurrTxPower {
331 int32 wifi_curr_tx_power = 1;
332 int32 resp = 2;
333}
334
335message CtrlMsg_Req_ConfigHeartbeat {
336 bool enable = 1;
337 int32 duration = 2;
338}
339
340message CtrlMsg_Resp_ConfigHeartbeat {
341 int32 resp = 1;
342}
343
344/** Event structure **/
345message CtrlMsg_Event_ESPInit {
346 bytes init_data = 1;
347}
348
349message CtrlMsg_Event_Heartbeat {
350 int32 hb_num = 1;
351}
352
353message CtrlMsg_Event_StationDisconnectFromAP {
354 int32 resp = 1;
355}
356
357message CtrlMsg_Event_StationDisconnectFromESPSoftAP {
358 int32 resp = 1;
359 bytes mac = 2;
360}
361
362message CtrlMsg {
363 /* msg_type could be req, resp or Event */
364 CtrlMsgType msg_type = 1;
365
366 /* msg id */
367 CtrlMsgId msg_id = 2;
368
369 /* union of all msg ids */
370 oneof payload {
371 /** Requests **/
372 CtrlMsg_Req_GetMacAddress req_get_mac_address = 101;
373 CtrlMsg_Req_SetMacAddress req_set_mac_address = 102;
374 CtrlMsg_Req_GetMode req_get_wifi_mode = 103;
375 CtrlMsg_Req_SetMode req_set_wifi_mode = 104;
376
377 CtrlMsg_Req_ScanResult req_scan_ap_list = 105;
378 CtrlMsg_Req_GetAPConfig req_get_ap_config = 106;
379 CtrlMsg_Req_ConnectAP req_connect_ap = 107;
380 CtrlMsg_Req_GetStatus req_disconnect_ap = 108;
381
382 CtrlMsg_Req_GetSoftAPConfig req_get_softap_config = 109;
383 CtrlMsg_Req_SetSoftAPVendorSpecificIE req_set_softap_vendor_specific_ie = 110;
384 CtrlMsg_Req_StartSoftAP req_start_softap = 111;
385 CtrlMsg_Req_SoftAPConnectedSTA req_softap_connected_stas_list = 112;
386 CtrlMsg_Req_GetStatus req_stop_softap = 113;
387
388 CtrlMsg_Req_SetMode req_set_power_save_mode = 114;
389 CtrlMsg_Req_GetMode req_get_power_save_mode = 115;
390
391 CtrlMsg_Req_OTABegin req_ota_begin = 116;
392 CtrlMsg_Req_OTAWrite req_ota_write = 117;
393 CtrlMsg_Req_OTAEnd req_ota_end = 118;
394
395 CtrlMsg_Req_SetWifiMaxTxPower req_set_wifi_max_tx_power = 119;
396 CtrlMsg_Req_GetWifiCurrTxPower req_get_wifi_curr_tx_power = 120;
397 CtrlMsg_Req_ConfigHeartbeat req_config_heartbeat = 121;
398
399 /** Responses **/
400 CtrlMsg_Resp_GetMacAddress resp_get_mac_address = 201;
401 CtrlMsg_Resp_SetMacAddress resp_set_mac_address = 202;
402 CtrlMsg_Resp_GetMode resp_get_wifi_mode = 203;
403 CtrlMsg_Resp_SetMode resp_set_wifi_mode = 204;
404
405 CtrlMsg_Resp_ScanResult resp_scan_ap_list = 205;
406 CtrlMsg_Resp_GetAPConfig resp_get_ap_config = 206;
407 CtrlMsg_Resp_ConnectAP resp_connect_ap = 207;
408 CtrlMsg_Resp_GetStatus resp_disconnect_ap = 208;
409
410 CtrlMsg_Resp_GetSoftAPConfig resp_get_softap_config = 209;
411 CtrlMsg_Resp_SetSoftAPVendorSpecificIE resp_set_softap_vendor_specific_ie = 210;
412 CtrlMsg_Resp_StartSoftAP resp_start_softap = 211;
413 CtrlMsg_Resp_SoftAPConnectedSTA resp_softap_connected_stas_list = 212;
414 CtrlMsg_Resp_GetStatus resp_stop_softap = 213;
415
416 CtrlMsg_Resp_SetMode resp_set_power_save_mode = 214;
417 CtrlMsg_Resp_GetMode resp_get_power_save_mode = 215;
418
419 CtrlMsg_Resp_OTABegin resp_ota_begin = 216;
420 CtrlMsg_Resp_OTAWrite resp_ota_write = 217;
421 CtrlMsg_Resp_OTAEnd resp_ota_end = 218;
422 CtrlMsg_Resp_SetWifiMaxTxPower resp_set_wifi_max_tx_power = 219;
423 CtrlMsg_Resp_GetWifiCurrTxPower resp_get_wifi_curr_tx_power = 220;
424 CtrlMsg_Resp_ConfigHeartbeat resp_config_heartbeat = 221;
425
426 /** Notifications **/
427 CtrlMsg_Event_ESPInit event_esp_init = 301;
428 CtrlMsg_Event_Heartbeat event_heartbeat = 302;
429 CtrlMsg_Event_StationDisconnectFromAP event_station_disconnect_from_AP = 303;
430 CtrlMsg_Event_StationDisconnectFromESPSoftAP event_station_disconnect_from_ESP_SoftAP = 304;
431 }
432}
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 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4use core::fmt::{Debug, Display, LowerHex};
5
6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features.");
8
9macro_rules! assert {
10 ($($x:tt)*) => {
11 {
12 #[cfg(not(feature = "defmt"))]
13 ::core::assert!($($x)*);
14 #[cfg(feature = "defmt")]
15 ::defmt::assert!($($x)*);
16 }
17 };
18}
19
20macro_rules! assert_eq {
21 ($($x:tt)*) => {
22 {
23 #[cfg(not(feature = "defmt"))]
24 ::core::assert_eq!($($x)*);
25 #[cfg(feature = "defmt")]
26 ::defmt::assert_eq!($($x)*);
27 }
28 };
29}
30
31macro_rules! assert_ne {
32 ($($x:tt)*) => {
33 {
34 #[cfg(not(feature = "defmt"))]
35 ::core::assert_ne!($($x)*);
36 #[cfg(feature = "defmt")]
37 ::defmt::assert_ne!($($x)*);
38 }
39 };
40}
41
42macro_rules! debug_assert {
43 ($($x:tt)*) => {
44 {
45 #[cfg(not(feature = "defmt"))]
46 ::core::debug_assert!($($x)*);
47 #[cfg(feature = "defmt")]
48 ::defmt::debug_assert!($($x)*);
49 }
50 };
51}
52
53macro_rules! debug_assert_eq {
54 ($($x:tt)*) => {
55 {
56 #[cfg(not(feature = "defmt"))]
57 ::core::debug_assert_eq!($($x)*);
58 #[cfg(feature = "defmt")]
59 ::defmt::debug_assert_eq!($($x)*);
60 }
61 };
62}
63
64macro_rules! debug_assert_ne {
65 ($($x:tt)*) => {
66 {
67 #[cfg(not(feature = "defmt"))]
68 ::core::debug_assert_ne!($($x)*);
69 #[cfg(feature = "defmt")]
70 ::defmt::debug_assert_ne!($($x)*);
71 }
72 };
73}
74
75macro_rules! todo {
76 ($($x:tt)*) => {
77 {
78 #[cfg(not(feature = "defmt"))]
79 ::core::todo!($($x)*);
80 #[cfg(feature = "defmt")]
81 ::defmt::todo!($($x)*);
82 }
83 };
84}
85
86#[cfg(not(feature = "defmt"))]
87macro_rules! unreachable {
88 ($($x:tt)*) => {
89 ::core::unreachable!($($x)*)
90 };
91}
92
93#[cfg(feature = "defmt")]
94macro_rules! unreachable {
95 ($($x:tt)*) => {
96 ::defmt::unreachable!($($x)*);
97 };
98}
99
100macro_rules! panic {
101 ($($x:tt)*) => {
102 {
103 #[cfg(not(feature = "defmt"))]
104 ::core::panic!($($x)*);
105 #[cfg(feature = "defmt")]
106 ::defmt::panic!($($x)*);
107 }
108 };
109}
110
111macro_rules! trace {
112 ($s:literal $(, $x:expr)* $(,)?) => {
113 {
114 #[cfg(feature = "log")]
115 ::log::trace!($s $(, $x)*);
116 #[cfg(feature = "defmt")]
117 ::defmt::trace!($s $(, $x)*);
118 #[cfg(not(any(feature = "log", feature="defmt")))]
119 let _ = ($( & $x ),*);
120 }
121 };
122}
123
124macro_rules! debug {
125 ($s:literal $(, $x:expr)* $(,)?) => {
126 {
127 #[cfg(feature = "log")]
128 ::log::debug!($s $(, $x)*);
129 #[cfg(feature = "defmt")]
130 ::defmt::debug!($s $(, $x)*);
131 #[cfg(not(any(feature = "log", feature="defmt")))]
132 let _ = ($( & $x ),*);
133 }
134 };
135}
136
137macro_rules! info {
138 ($s:literal $(, $x:expr)* $(,)?) => {
139 {
140 #[cfg(feature = "log")]
141 ::log::info!($s $(, $x)*);
142 #[cfg(feature = "defmt")]
143 ::defmt::info!($s $(, $x)*);
144 #[cfg(not(any(feature = "log", feature="defmt")))]
145 let _ = ($( & $x ),*);
146 }
147 };
148}
149
150macro_rules! warn {
151 ($s:literal $(, $x:expr)* $(,)?) => {
152 {
153 #[cfg(feature = "log")]
154 ::log::warn!($s $(, $x)*);
155 #[cfg(feature = "defmt")]
156 ::defmt::warn!($s $(, $x)*);
157 #[cfg(not(any(feature = "log", feature="defmt")))]
158 let _ = ($( & $x ),*);
159 }
160 };
161}
162
163macro_rules! error {
164 ($s:literal $(, $x:expr)* $(,)?) => {
165 {
166 #[cfg(feature = "log")]
167 ::log::error!($s $(, $x)*);
168 #[cfg(feature = "defmt")]
169 ::defmt::error!($s $(, $x)*);
170 #[cfg(not(any(feature = "log", feature="defmt")))]
171 let _ = ($( & $x ),*);
172 }
173 };
174}
175
176#[cfg(feature = "defmt")]
177macro_rules! unwrap {
178 ($($x:tt)*) => {
179 ::defmt::unwrap!($($x)*)
180 };
181}
182
183#[cfg(not(feature = "defmt"))]
184macro_rules! unwrap {
185 ($arg:expr) => {
186 match $crate::fmt::Try::into_result($arg) {
187 ::core::result::Result::Ok(t) => t,
188 ::core::result::Result::Err(e) => {
189 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
190 }
191 }
192 };
193 ($arg:expr, $($msg:expr),+ $(,)? ) => {
194 match $crate::fmt::Try::into_result($arg) {
195 ::core::result::Result::Ok(t) => t,
196 ::core::result::Result::Err(e) => {
197 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
198 }
199 }
200 }
201}
202
203#[derive(Debug, Copy, Clone, Eq, PartialEq)]
204pub struct NoneError;
205
206pub trait Try {
207 type Ok;
208 type Error;
209 fn into_result(self) -> Result<Self::Ok, Self::Error>;
210}
211
212impl<T> Try for Option<T> {
213 type Ok = T;
214 type Error = NoneError;
215
216 #[inline]
217 fn into_result(self) -> Result<T, NoneError> {
218 self.ok_or(NoneError)
219 }
220}
221
222impl<T, E> Try for Result<T, E> {
223 type Ok = T;
224 type Error = E;
225
226 #[inline]
227 fn into_result(self) -> Self {
228 self
229 }
230}
231
232pub struct Bytes<'a>(pub &'a [u8]);
233
234impl<'a> Debug for Bytes<'a> {
235 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
236 write!(f, "{:#02x?}", self.0)
237 }
238}
239
240impl<'a> Display for Bytes<'a> {
241 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
242 write!(f, "{:#02x?}", self.0)
243 }
244}
245
246impl<'a> LowerHex for Bytes<'a> {
247 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
248 write!(f, "{:#02x?}", self.0)
249 }
250}
251
252#[cfg(feature = "defmt")]
253impl<'a> defmt::Format for Bytes<'a> {
254 fn format(&self, fmt: defmt::Formatter) {
255 defmt::write!(fmt, "{:02x}", self.0)
256 }
257}
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 @@
1use core::cell::{Cell, RefCell};
2use core::future::poll_fn;
3use core::task::{Poll, Waker};
4
5use embassy_sync::waitqueue::WakerRegistration;
6
7use crate::fmt::Bytes;
8
9#[derive(Clone, Copy)]
10pub struct PendingIoctl {
11 pub buf: *mut [u8],
12 pub req_len: usize,
13}
14
15#[derive(Clone, Copy)]
16enum IoctlStateInner {
17 Pending(PendingIoctl),
18 Sent { buf: *mut [u8] },
19 Done { resp_len: usize },
20}
21
22struct Wakers {
23 control: WakerRegistration,
24 runner: WakerRegistration,
25}
26
27impl Default for Wakers {
28 fn default() -> Self {
29 Self {
30 control: WakerRegistration::new(),
31 runner: WakerRegistration::new(),
32 }
33 }
34}
35
36pub struct IoctlState {
37 state: Cell<IoctlStateInner>,
38 wakers: RefCell<Wakers>,
39}
40
41impl IoctlState {
42 pub fn new() -> Self {
43 Self {
44 state: Cell::new(IoctlStateInner::Done { resp_len: 0 }),
45 wakers: Default::default(),
46 }
47 }
48
49 fn wake_control(&self) {
50 self.wakers.borrow_mut().control.wake();
51 }
52
53 fn register_control(&self, waker: &Waker) {
54 self.wakers.borrow_mut().control.register(waker);
55 }
56
57 fn wake_runner(&self) {
58 self.wakers.borrow_mut().runner.wake();
59 }
60
61 fn register_runner(&self, waker: &Waker) {
62 self.wakers.borrow_mut().runner.register(waker);
63 }
64
65 pub async fn wait_complete(&self) -> usize {
66 poll_fn(|cx| {
67 if let IoctlStateInner::Done { resp_len } = self.state.get() {
68 Poll::Ready(resp_len)
69 } else {
70 self.register_control(cx.waker());
71 Poll::Pending
72 }
73 })
74 .await
75 }
76
77 pub async fn wait_pending(&self) -> PendingIoctl {
78 let pending = poll_fn(|cx| {
79 if let IoctlStateInner::Pending(pending) = self.state.get() {
80 Poll::Ready(pending)
81 } else {
82 self.register_runner(cx.waker());
83 Poll::Pending
84 }
85 })
86 .await;
87
88 self.state.set(IoctlStateInner::Sent { buf: pending.buf });
89 pending
90 }
91
92 pub fn cancel_ioctl(&self) {
93 self.state.set(IoctlStateInner::Done { resp_len: 0 });
94 }
95
96 pub async fn do_ioctl(&self, buf: &mut [u8], req_len: usize) -> usize {
97 debug!("IOCTL Request: {:02x}", Bytes(&buf[..req_len]));
98
99 self.state.set(IoctlStateInner::Pending(PendingIoctl { buf, req_len }));
100 self.wake_runner();
101 self.wait_complete().await
102 }
103
104 pub fn ioctl_done(&self, response: &[u8]) {
105 if let IoctlStateInner::Sent { buf } = self.state.get() {
106 debug!("IOCTL Response: {:02x}", Bytes(response));
107
108 // TODO fix this
109 (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response);
110
111 self.state.set(IoctlStateInner::Done {
112 resp_len: response.len(),
113 });
114 self.wake_control();
115 } else {
116 warn!("IOCTL Response but no pending Ioctl");
117 }
118 }
119}
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 @@
1#![no_std]
2
3use control::Control;
4use embassy_futures::select::{select3, Either3};
5use embassy_net_driver_channel as ch;
6use embassy_time::{Duration, Instant, Timer};
7use embedded_hal::digital::{InputPin, OutputPin};
8use embedded_hal_async::digital::Wait;
9use embedded_hal_async::spi::SpiDevice;
10use ioctl::IoctlState;
11
12use crate::ioctl::PendingIoctl;
13
14mod proto;
15
16// must be first
17mod fmt;
18
19mod control;
20mod ioctl;
21
22const MTU: usize = 1514;
23
24macro_rules! impl_bytes {
25 ($t:ident) => {
26 impl $t {
27 pub const SIZE: usize = core::mem::size_of::<Self>();
28
29 #[allow(unused)]
30 pub fn to_bytes(&self) -> [u8; Self::SIZE] {
31 unsafe { core::mem::transmute(*self) }
32 }
33
34 #[allow(unused)]
35 pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {
36 let alignment = core::mem::align_of::<Self>();
37 assert_eq!(
38 bytes.as_ptr().align_offset(alignment),
39 0,
40 "{} is not aligned",
41 core::any::type_name::<Self>()
42 );
43 unsafe { core::mem::transmute(bytes) }
44 }
45
46 #[allow(unused)]
47 pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {
48 let alignment = core::mem::align_of::<Self>();
49 assert_eq!(
50 bytes.as_ptr().align_offset(alignment),
51 0,
52 "{} is not aligned",
53 core::any::type_name::<Self>()
54 );
55
56 unsafe { core::mem::transmute(bytes) }
57 }
58 }
59 };
60}
61
62#[repr(C, packed)]
63#[derive(Clone, Copy, Debug, Default)]
64struct PayloadHeader {
65 /// InterfaceType on lower 4 bits, number on higher 4 bits.
66 if_type_and_num: u8,
67
68 /// Flags.
69 ///
70 /// bit 0: more fragments.
71 flags: u8,
72
73 len: u16,
74 offset: u16,
75 checksum: u16,
76 seq_num: u16,
77 reserved2: u8,
78
79 /// Packet type for HCI or PRIV interface, reserved otherwise
80 hci_priv_packet_type: u8,
81}
82impl_bytes!(PayloadHeader);
83
84#[repr(u8)]
85enum InterfaceType {
86 Sta = 0,
87 Ap = 1,
88 Serial = 2,
89 Hci = 3,
90 Priv = 4,
91 Test = 5,
92}
93
94const MAX_SPI_BUFFER_SIZE: usize = 1600;
95
96pub struct State {
97 ioctl_state: IoctlState,
98 ch: ch::State<MTU, 4, 4>,
99}
100
101impl State {
102 pub fn new() -> Self {
103 Self {
104 ioctl_state: IoctlState::new(),
105 ch: ch::State::new(),
106 }
107 }
108}
109
110pub type NetDriver<'a> = ch::Device<'a, MTU>;
111
112pub async fn new<'a, SPI, IN, OUT>(
113 state: &'a mut State,
114 spi: SPI,
115 handshake: IN,
116 ready: IN,
117 reset: OUT,
118) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>)
119where
120 SPI: SpiDevice,
121 IN: InputPin + Wait,
122 OUT: OutputPin,
123{
124 let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
125 let state_ch = ch_runner.state_runner();
126
127 let mut runner = Runner {
128 ch: ch_runner,
129 ioctl_state: &state.ioctl_state,
130 next_seq: 1,
131 handshake,
132 ready,
133 reset,
134 spi,
135 };
136 runner.init().await;
137
138 (device, Control::new(state_ch, &state.ioctl_state), runner)
139}
140
141pub struct Runner<'a, SPI, IN, OUT> {
142 ch: ch::Runner<'a, MTU>,
143 ioctl_state: &'a IoctlState,
144
145 next_seq: u16,
146
147 spi: SPI,
148 handshake: IN,
149 ready: IN,
150 reset: OUT,
151}
152
153impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT>
154where
155 SPI: SpiDevice,
156 IN: InputPin + Wait,
157 OUT: OutputPin,
158{
159 async fn init(&mut self) {}
160
161 pub async fn run(mut self) -> ! {
162 debug!("resetting...");
163 self.reset.set_low().unwrap();
164 Timer::after(Duration::from_millis(100)).await;
165 self.reset.set_high().unwrap();
166 Timer::after(Duration::from_millis(1000)).await;
167
168 let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
169 let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
170
171 loop {
172 self.handshake.wait_for_high().await.unwrap();
173
174 let ioctl = self.ioctl_state.wait_pending();
175 let tx = self.ch.tx_buf();
176 let ev = async { self.ready.wait_for_high().await.unwrap() };
177
178 match select3(ioctl, tx, ev).await {
179 Either3::First(PendingIoctl { buf, req_len }) => {
180 tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02");
181 tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes());
182 tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]);
183
184 let mut header = PayloadHeader {
185 if_type_and_num: InterfaceType::Serial as _,
186 len: (req_len + 14) as _,
187 offset: PayloadHeader::SIZE as _,
188 seq_num: self.next_seq,
189 ..Default::default()
190 };
191 self.next_seq = self.next_seq.wrapping_add(1);
192
193 // Calculate checksum
194 tx_buf[0..12].copy_from_slice(&header.to_bytes());
195 header.checksum = checksum(&tx_buf[..26 + req_len]);
196 tx_buf[0..12].copy_from_slice(&header.to_bytes());
197
198 debug!("====== SENDING IOCTL");
199 }
200 Either3::Second(packet) => {
201 tx_buf[12..][..packet.len()].copy_from_slice(packet);
202
203 let mut header = PayloadHeader {
204 if_type_and_num: InterfaceType::Sta as _,
205 len: packet.len() as _,
206 offset: PayloadHeader::SIZE as _,
207 seq_num: self.next_seq,
208 ..Default::default()
209 };
210 self.next_seq = self.next_seq.wrapping_add(1);
211
212 // Calculate checksum
213 tx_buf[0..12].copy_from_slice(&header.to_bytes());
214 header.checksum = checksum(&tx_buf[..12 + packet.len()]);
215 tx_buf[0..12].copy_from_slice(&header.to_bytes());
216
217 self.ch.tx_done();
218 }
219 Either3::Third(()) => {
220 tx_buf[..PayloadHeader::SIZE].fill(0);
221 }
222 }
223
224 if tx_buf[0] != 0 {
225 trace!("tx: {:02x}", &tx_buf[..40]);
226 }
227
228 self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
229 let delay_until = Instant::now() + Duration::from_millis(1);
230 self.handle_rx(&mut rx_buf);
231 Timer::at(delay_until).await;
232 }
233 }
234
235 fn handle_rx(&mut self, buf: &mut [u8]) {
236 trace!("rx: {:02x}", &buf[..40]);
237
238 let buf_len = buf.len();
239 let h = PayloadHeader::from_bytes_mut((&mut buf[..PayloadHeader::SIZE]).try_into().unwrap());
240
241 if h.len == 0 || h.offset as usize != PayloadHeader::SIZE {
242 return;
243 }
244
245 let payload_len = h.len as usize;
246 if buf_len < PayloadHeader::SIZE + payload_len {
247 warn!("rx: len too big");
248 return;
249 }
250
251 let if_type_and_num = h.if_type_and_num;
252 let want_checksum = h.checksum;
253 h.checksum = 0;
254 let got_checksum = checksum(&buf[..PayloadHeader::SIZE + payload_len]);
255 if want_checksum != got_checksum {
256 warn!("rx: bad checksum. Got {:04x}, want {:04x}", got_checksum, want_checksum);
257 return;
258 }
259
260 let payload = &mut buf[PayloadHeader::SIZE..][..payload_len];
261
262 match if_type_and_num & 0x0f {
263 // STA
264 0 => match self.ch.try_rx_buf() {
265 Some(buf) => {
266 buf[..payload.len()].copy_from_slice(payload);
267 self.ch.rx_done(payload.len())
268 }
269 None => warn!("failed to push rxd packet to the channel."),
270 },
271 // serial
272 2 => {
273 debug!("serial rx: {:02x}", payload);
274 if payload.len() < 14 {
275 warn!("serial rx: too short");
276 return;
277 }
278 if &payload[..12] != b"\x01\x08\x00ctrlResp\x02" {
279 warn!("serial rx: bad tlv");
280 return;
281 }
282 let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize;
283 if payload.len() < 14 + len {
284 warn!("serial rx: too short 2");
285 return;
286 }
287 self.ioctl_state.ioctl_done(&payload[14..][..len]);
288 }
289 _ => warn!("unknown iftype {}", if_type_and_num),
290 }
291 }
292}
293
294fn checksum(buf: &[u8]) -> u16 {
295 let mut res = 0u16;
296 for &b in buf {
297 res = res.wrapping_add(b as _);
298 }
299 res
300}
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 @@
1use heapless::{String, Vec};
2
3/// internal supporting structures for CtrlMsg
4
5#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
6pub struct ScanResult {
7 #[noproto(tag = "1")]
8 pub ssid: String<32>,
9 #[noproto(tag = "2")]
10 pub chnl: u32,
11 #[noproto(tag = "3")]
12 pub rssi: u32,
13 #[noproto(tag = "4")]
14 pub bssid: String<32>,
15 #[noproto(tag = "5")]
16 pub sec_prot: CtrlWifiSecProt,
17}
18
19#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
20pub struct ConnectedStaList {
21 #[noproto(tag = "1")]
22 pub mac: String<32>,
23 #[noproto(tag = "2")]
24 pub rssi: u32,
25}
26/// * Req/Resp structure *
27
28#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
29pub struct CtrlMsgReqGetMacAddress {
30 #[noproto(tag = "1")]
31 pub mode: u32,
32}
33
34#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
35pub struct CtrlMsgRespGetMacAddress {
36 #[noproto(tag = "1")]
37 pub mac: String<32>,
38 #[noproto(tag = "2")]
39 pub resp: u32,
40}
41
42#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
43pub struct CtrlMsgReqGetMode {}
44
45#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
46pub struct CtrlMsgRespGetMode {
47 #[noproto(tag = "1")]
48 pub mode: u32,
49 #[noproto(tag = "2")]
50 pub resp: u32,
51}
52
53#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
54pub struct CtrlMsgReqSetMode {
55 #[noproto(tag = "1")]
56 pub mode: u32,
57}
58
59#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
60pub struct CtrlMsgRespSetMode {
61 #[noproto(tag = "1")]
62 pub resp: u32,
63}
64
65#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
66pub struct CtrlMsgReqGetStatus {}
67
68#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
69pub struct CtrlMsgRespGetStatus {
70 #[noproto(tag = "1")]
71 pub resp: u32,
72}
73
74#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
75pub struct CtrlMsgReqSetMacAddress {
76 #[noproto(tag = "1")]
77 pub mac: String<32>,
78 #[noproto(tag = "2")]
79 pub mode: u32,
80}
81
82#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
83pub struct CtrlMsgRespSetMacAddress {
84 #[noproto(tag = "1")]
85 pub resp: u32,
86}
87
88#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
89pub struct CtrlMsgReqGetApConfig {}
90
91#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
92pub struct CtrlMsgRespGetApConfig {
93 #[noproto(tag = "1")]
94 pub ssid: String<32>,
95 #[noproto(tag = "2")]
96 pub bssid: String<32>,
97 #[noproto(tag = "3")]
98 pub rssi: u32,
99 #[noproto(tag = "4")]
100 pub chnl: u32,
101 #[noproto(tag = "5")]
102 pub sec_prot: CtrlWifiSecProt,
103 #[noproto(tag = "6")]
104 pub resp: u32,
105}
106
107#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
108pub struct CtrlMsgReqConnectAp {
109 #[noproto(tag = "1")]
110 pub ssid: String<32>,
111 #[noproto(tag = "2")]
112 pub pwd: String<32>,
113 #[noproto(tag = "3")]
114 pub bssid: String<32>,
115 #[noproto(tag = "4")]
116 pub is_wpa3_supported: bool,
117 #[noproto(tag = "5")]
118 pub listen_interval: u32,
119}
120
121#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
122pub struct CtrlMsgRespConnectAp {
123 #[noproto(tag = "1")]
124 pub resp: u32,
125 #[noproto(tag = "2")]
126 pub mac: String<32>,
127}
128
129#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
130pub struct CtrlMsgReqGetSoftApConfig {}
131
132#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
133pub struct CtrlMsgRespGetSoftApConfig {
134 #[noproto(tag = "1")]
135 pub ssid: String<32>,
136 #[noproto(tag = "2")]
137 pub pwd: String<32>,
138 #[noproto(tag = "3")]
139 pub chnl: u32,
140 #[noproto(tag = "4")]
141 pub sec_prot: CtrlWifiSecProt,
142 #[noproto(tag = "5")]
143 pub max_conn: u32,
144 #[noproto(tag = "6")]
145 pub ssid_hidden: bool,
146 #[noproto(tag = "7")]
147 pub bw: u32,
148 #[noproto(tag = "8")]
149 pub resp: u32,
150}
151
152#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
153pub struct CtrlMsgReqStartSoftAp {
154 #[noproto(tag = "1")]
155 pub ssid: String<32>,
156 #[noproto(tag = "2")]
157 pub pwd: String<32>,
158 #[noproto(tag = "3")]
159 pub chnl: u32,
160 #[noproto(tag = "4")]
161 pub sec_prot: CtrlWifiSecProt,
162 #[noproto(tag = "5")]
163 pub max_conn: u32,
164 #[noproto(tag = "6")]
165 pub ssid_hidden: bool,
166 #[noproto(tag = "7")]
167 pub bw: u32,
168}
169
170#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
171pub struct CtrlMsgRespStartSoftAp {
172 #[noproto(tag = "1")]
173 pub resp: u32,
174 #[noproto(tag = "2")]
175 pub mac: String<32>,
176}
177
178#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
179pub struct CtrlMsgReqScanResult {}
180
181#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
182pub struct CtrlMsgRespScanResult {
183 #[noproto(tag = "1")]
184 pub count: u32,
185 #[noproto(repeated, tag = "2")]
186 pub entries: Vec<ScanResult, 16>,
187 #[noproto(tag = "3")]
188 pub resp: u32,
189}
190
191#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
192pub struct CtrlMsgReqSoftApConnectedSta {}
193
194#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
195pub struct CtrlMsgRespSoftApConnectedSta {
196 #[noproto(tag = "1")]
197 pub num: u32,
198 #[noproto(repeated, tag = "2")]
199 pub stations: Vec<ConnectedStaList, 16>,
200 #[noproto(tag = "3")]
201 pub resp: u32,
202}
203
204#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
205pub struct CtrlMsgReqOtaBegin {}
206
207#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
208pub struct CtrlMsgRespOtaBegin {
209 #[noproto(tag = "1")]
210 pub resp: u32,
211}
212
213#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
214pub struct CtrlMsgReqOtaWrite {
215 #[noproto(tag = "1")]
216 pub ota_data: Vec<u8, 1024>,
217}
218
219#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
220pub struct CtrlMsgRespOtaWrite {
221 #[noproto(tag = "1")]
222 pub resp: u32,
223}
224
225#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
226pub struct CtrlMsgReqOtaEnd {}
227
228#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
229pub struct CtrlMsgRespOtaEnd {
230 #[noproto(tag = "1")]
231 pub resp: u32,
232}
233
234#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
235pub struct CtrlMsgReqVendorIeData {
236 #[noproto(tag = "1")]
237 pub element_id: u32,
238 #[noproto(tag = "2")]
239 pub length: u32,
240 #[noproto(tag = "3")]
241 pub vendor_oui: Vec<u8, 8>,
242 #[noproto(tag = "4")]
243 pub vendor_oui_type: u32,
244 #[noproto(tag = "5")]
245 pub payload: Vec<u8, 64>,
246}
247
248#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
249pub struct CtrlMsgReqSetSoftApVendorSpecificIe {
250 #[noproto(tag = "1")]
251 pub enable: bool,
252 #[noproto(tag = "2")]
253 pub r#type: CtrlVendorIeType,
254 #[noproto(tag = "3")]
255 pub idx: CtrlVendorIeid,
256 #[noproto(optional, tag = "4")]
257 pub vendor_ie_data: Option<CtrlMsgReqVendorIeData>,
258}
259
260#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
261pub struct CtrlMsgRespSetSoftApVendorSpecificIe {
262 #[noproto(tag = "1")]
263 pub resp: u32,
264}
265
266#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
267pub struct CtrlMsgReqSetWifiMaxTxPower {
268 #[noproto(tag = "1")]
269 pub wifi_max_tx_power: u32,
270}
271
272#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
273pub struct CtrlMsgRespSetWifiMaxTxPower {
274 #[noproto(tag = "1")]
275 pub resp: u32,
276}
277
278#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
279pub struct CtrlMsgReqGetWifiCurrTxPower {}
280
281#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
282pub struct CtrlMsgRespGetWifiCurrTxPower {
283 #[noproto(tag = "1")]
284 pub wifi_curr_tx_power: u32,
285 #[noproto(tag = "2")]
286 pub resp: u32,
287}
288
289#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
290pub struct CtrlMsgReqConfigHeartbeat {
291 #[noproto(tag = "1")]
292 pub enable: bool,
293 #[noproto(tag = "2")]
294 pub duration: u32,
295}
296
297#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
298pub struct CtrlMsgRespConfigHeartbeat {
299 #[noproto(tag = "1")]
300 pub resp: u32,
301}
302/// * Event structure *
303
304#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
305pub struct CtrlMsgEventEspInit {
306 #[noproto(tag = "1")]
307 pub init_data: Vec<u8, 64>,
308}
309
310#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
311pub struct CtrlMsgEventHeartbeat {
312 #[noproto(tag = "1")]
313 pub hb_num: u32,
314}
315
316#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
317pub struct CtrlMsgEventStationDisconnectFromAp {
318 #[noproto(tag = "1")]
319 pub resp: u32,
320}
321
322#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
323pub struct CtrlMsgEventStationDisconnectFromEspSoftAp {
324 #[noproto(tag = "1")]
325 pub resp: u32,
326 #[noproto(tag = "2")]
327 pub mac: String<32>,
328}
329
330#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
331pub struct CtrlMsg {
332 /// msg_type could be req, resp or Event
333 #[noproto(tag = "1")]
334 pub msg_type: CtrlMsgType,
335 /// msg id
336 #[noproto(tag = "2")]
337 pub msg_id: CtrlMsgId,
338 /// union of all msg ids
339 #[noproto(
340 oneof,
341 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"
342 )]
343 pub payload: Option<CtrlMsgPayload>,
344}
345
346/// union of all msg ids
347#[derive(Debug, Clone, Eq, PartialEq, noproto::Oneof)]
348pub enum CtrlMsgPayload {
349 /// * Requests *
350 #[noproto(tag = "101")]
351 ReqGetMacAddress(CtrlMsgReqGetMacAddress),
352 #[noproto(tag = "102")]
353 ReqSetMacAddress(CtrlMsgReqSetMacAddress),
354 #[noproto(tag = "103")]
355 ReqGetWifiMode(CtrlMsgReqGetMode),
356 #[noproto(tag = "104")]
357 ReqSetWifiMode(CtrlMsgReqSetMode),
358 #[noproto(tag = "105")]
359 ReqScanApList(CtrlMsgReqScanResult),
360 #[noproto(tag = "106")]
361 ReqGetApConfig(CtrlMsgReqGetApConfig),
362 #[noproto(tag = "107")]
363 ReqConnectAp(CtrlMsgReqConnectAp),
364 #[noproto(tag = "108")]
365 ReqDisconnectAp(CtrlMsgReqGetStatus),
366 #[noproto(tag = "109")]
367 ReqGetSoftapConfig(CtrlMsgReqGetSoftApConfig),
368 #[noproto(tag = "110")]
369 ReqSetSoftapVendorSpecificIe(CtrlMsgReqSetSoftApVendorSpecificIe),
370 #[noproto(tag = "111")]
371 ReqStartSoftap(CtrlMsgReqStartSoftAp),
372 #[noproto(tag = "112")]
373 ReqSoftapConnectedStasList(CtrlMsgReqSoftApConnectedSta),
374 #[noproto(tag = "113")]
375 ReqStopSoftap(CtrlMsgReqGetStatus),
376 #[noproto(tag = "114")]
377 ReqSetPowerSaveMode(CtrlMsgReqSetMode),
378 #[noproto(tag = "115")]
379 ReqGetPowerSaveMode(CtrlMsgReqGetMode),
380 #[noproto(tag = "116")]
381 ReqOtaBegin(CtrlMsgReqOtaBegin),
382 #[noproto(tag = "117")]
383 ReqOtaWrite(CtrlMsgReqOtaWrite),
384 #[noproto(tag = "118")]
385 ReqOtaEnd(CtrlMsgReqOtaEnd),
386 #[noproto(tag = "119")]
387 ReqSetWifiMaxTxPower(CtrlMsgReqSetWifiMaxTxPower),
388 #[noproto(tag = "120")]
389 ReqGetWifiCurrTxPower(CtrlMsgReqGetWifiCurrTxPower),
390 #[noproto(tag = "121")]
391 ReqConfigHeartbeat(CtrlMsgReqConfigHeartbeat),
392 /// * Responses *
393 #[noproto(tag = "201")]
394 RespGetMacAddress(CtrlMsgRespGetMacAddress),
395 #[noproto(tag = "202")]
396 RespSetMacAddress(CtrlMsgRespSetMacAddress),
397 #[noproto(tag = "203")]
398 RespGetWifiMode(CtrlMsgRespGetMode),
399 #[noproto(tag = "204")]
400 RespSetWifiMode(CtrlMsgRespSetMode),
401 #[noproto(tag = "205")]
402 RespScanApList(CtrlMsgRespScanResult),
403 #[noproto(tag = "206")]
404 RespGetApConfig(CtrlMsgRespGetApConfig),
405 #[noproto(tag = "207")]
406 RespConnectAp(CtrlMsgRespConnectAp),
407 #[noproto(tag = "208")]
408 RespDisconnectAp(CtrlMsgRespGetStatus),
409 #[noproto(tag = "209")]
410 RespGetSoftapConfig(CtrlMsgRespGetSoftApConfig),
411 #[noproto(tag = "210")]
412 RespSetSoftapVendorSpecificIe(CtrlMsgRespSetSoftApVendorSpecificIe),
413 #[noproto(tag = "211")]
414 RespStartSoftap(CtrlMsgRespStartSoftAp),
415 #[noproto(tag = "212")]
416 RespSoftapConnectedStasList(CtrlMsgRespSoftApConnectedSta),
417 #[noproto(tag = "213")]
418 RespStopSoftap(CtrlMsgRespGetStatus),
419 #[noproto(tag = "214")]
420 RespSetPowerSaveMode(CtrlMsgRespSetMode),
421 #[noproto(tag = "215")]
422 RespGetPowerSaveMode(CtrlMsgRespGetMode),
423 #[noproto(tag = "216")]
424 RespOtaBegin(CtrlMsgRespOtaBegin),
425 #[noproto(tag = "217")]
426 RespOtaWrite(CtrlMsgRespOtaWrite),
427 #[noproto(tag = "218")]
428 RespOtaEnd(CtrlMsgRespOtaEnd),
429 #[noproto(tag = "219")]
430 RespSetWifiMaxTxPower(CtrlMsgRespSetWifiMaxTxPower),
431 #[noproto(tag = "220")]
432 RespGetWifiCurrTxPower(CtrlMsgRespGetWifiCurrTxPower),
433 #[noproto(tag = "221")]
434 RespConfigHeartbeat(CtrlMsgRespConfigHeartbeat),
435 /// * Notifications *
436 #[noproto(tag = "301")]
437 EventEspInit(CtrlMsgEventEspInit),
438 #[noproto(tag = "302")]
439 EventHeartbeat(CtrlMsgEventHeartbeat),
440 #[noproto(tag = "303")]
441 EventStationDisconnectFromAp(CtrlMsgEventStationDisconnectFromAp),
442 #[noproto(tag = "304")]
443 EventStationDisconnectFromEspSoftAp(CtrlMsgEventStationDisconnectFromEspSoftAp),
444}
445
446/// Enums similar to ESP IDF
447#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
448#[repr(u32)]
449pub enum CtrlVendorIeType {
450 #[default]
451 Beacon = 0,
452 ProbeReq = 1,
453 ProbeResp = 2,
454 AssocReq = 3,
455 AssocResp = 4,
456}
457
458#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
459#[repr(u32)]
460pub enum CtrlVendorIeid {
461 #[default]
462 Id0 = 0,
463 Id1 = 1,
464}
465
466#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
467#[repr(u32)]
468pub enum CtrlWifiMode {
469 #[default]
470 None = 0,
471 Sta = 1,
472 Ap = 2,
473 Apsta = 3,
474}
475
476#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
477#[repr(u32)]
478pub enum CtrlWifiBw {
479 #[default]
480 BwInvalid = 0,
481 Ht20 = 1,
482 Ht40 = 2,
483}
484
485#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
486#[repr(u32)]
487pub enum CtrlWifiPowerSave {
488 #[default]
489 PsInvalid = 0,
490 MinModem = 1,
491 MaxModem = 2,
492}
493
494#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
495#[repr(u32)]
496pub enum CtrlWifiSecProt {
497 #[default]
498 Open = 0,
499 Wep = 1,
500 WpaPsk = 2,
501 Wpa2Psk = 3,
502 WpaWpa2Psk = 4,
503 Wpa2Enterprise = 5,
504 Wpa3Psk = 6,
505 Wpa2Wpa3Psk = 7,
506}
507
508/// enums for Control path
509#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
510#[repr(u32)]
511pub enum CtrlStatus {
512 #[default]
513 Connected = 0,
514 NotConnected = 1,
515 NoApFound = 2,
516 ConnectionFail = 3,
517 InvalidArgument = 4,
518 OutOfRange = 5,
519}
520
521#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
522#[repr(u32)]
523pub enum CtrlMsgType {
524 #[default]
525 MsgTypeInvalid = 0,
526 Req = 1,
527 Resp = 2,
528 Event = 3,
529 MsgTypeMax = 4,
530}
531
532#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
533#[repr(u32)]
534pub enum CtrlMsgId {
535 #[default]
536 MsgIdInvalid = 0,
537 /// * Request Msgs *
538 ReqBase = 100,
539 ReqGetMacAddress = 101,
540 ReqSetMacAddress = 102,
541 ReqGetWifiMode = 103,
542 ReqSetWifiMode = 104,
543 ReqGetApScanList = 105,
544 ReqGetApConfig = 106,
545 ReqConnectAp = 107,
546 ReqDisconnectAp = 108,
547 ReqGetSoftApConfig = 109,
548 ReqSetSoftApVendorSpecificIe = 110,
549 ReqStartSoftAp = 111,
550 ReqGetSoftApConnectedStaList = 112,
551 ReqStopSoftAp = 113,
552 ReqSetPowerSaveMode = 114,
553 ReqGetPowerSaveMode = 115,
554 ReqOtaBegin = 116,
555 ReqOtaWrite = 117,
556 ReqOtaEnd = 118,
557 ReqSetWifiMaxTxPower = 119,
558 ReqGetWifiCurrTxPower = 120,
559 ReqConfigHeartbeat = 121,
560 /// Add new control path command response before Req_Max
561 /// and update Req_Max
562 ReqMax = 122,
563 /// * Response Msgs *
564 RespBase = 200,
565 RespGetMacAddress = 201,
566 RespSetMacAddress = 202,
567 RespGetWifiMode = 203,
568 RespSetWifiMode = 204,
569 RespGetApScanList = 205,
570 RespGetApConfig = 206,
571 RespConnectAp = 207,
572 RespDisconnectAp = 208,
573 RespGetSoftApConfig = 209,
574 RespSetSoftApVendorSpecificIe = 210,
575 RespStartSoftAp = 211,
576 RespGetSoftApConnectedStaList = 212,
577 RespStopSoftAp = 213,
578 RespSetPowerSaveMode = 214,
579 RespGetPowerSaveMode = 215,
580 RespOtaBegin = 216,
581 RespOtaWrite = 217,
582 RespOtaEnd = 218,
583 RespSetWifiMaxTxPower = 219,
584 RespGetWifiCurrTxPower = 220,
585 RespConfigHeartbeat = 221,
586 /// Add new control path command response before Resp_Max
587 /// and update Resp_Max
588 RespMax = 222,
589 /// * Event Msgs *
590 EventBase = 300,
591 EventEspInit = 301,
592 EventHeartbeat = 302,
593 EventStationDisconnectFromAp = 303,
594 EventStationDisconnectFromEspSoftAp = 304,
595 /// Add new control path command notification before Event_Max
596 /// and update Event_Max
597 EventMax = 305,
598}
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
22lora-phy = { version = "1", optional = true } 22lora-phy = { version = "1", optional = true }
23lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } 23lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
24lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } 24lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
25embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
25 26
26defmt = "0.3" 27defmt = "0.3"
27defmt-rtt = "0.4" 28defmt-rtt = "0.4"
@@ -35,3 +36,4 @@ rand = { version = "0.8.4", default-features = false }
35embedded-storage = "0.3.0" 36embedded-storage = "0.3.0"
36usbd-hid = "0.6.0" 37usbd-hid = "0.6.0"
37serde = { version = "1.0.136", default-features = false } 38serde = { version = "1.0.136", default-features = false }
39embedded-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 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap, warn};
6use embassy_executor::Spawner;
7use embassy_net::tcp::TcpSocket;
8use embassy_net::{Stack, StackResources};
9use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull};
10use embassy_nrf::rng::Rng;
11use embassy_nrf::spim::{self, Spim};
12use embassy_nrf::{bind_interrupts, peripherals};
13use embassy_time::{Duration, Timer};
14use embedded_hal_async::spi::ExclusiveDevice;
15use embedded_io::asynch::Write;
16use static_cell::make_static;
17use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _};
18
19bind_interrupts!(struct Irqs {
20 SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
21 RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
22});
23
24#[embassy_executor::task]
25async fn wifi_task(
26 runner: hosted::Runner<
27 'static,
28 ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_31>>,
29 Input<'static, AnyPin>,
30 Output<'static, peripherals::P1_03>,
31 >,
32) -> ! {
33 runner.run().await
34}
35
36#[embassy_executor::task]
37async fn net_task(stack: &'static Stack<hosted::NetDriver<'static>>) -> ! {
38 stack.run().await
39}
40
41#[embassy_executor::main]
42async fn main(spawner: Spawner) {
43 info!("Hello World!");
44
45 let p = embassy_nrf::init(Default::default());
46
47 let miso = p.P0_28;
48 let sck = p.P0_29;
49 let mosi = p.P0_30;
50 let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive);
51 let handshake = Input::new(p.P1_01.degrade(), Pull::Up);
52 let ready = Input::new(p.P1_02.degrade(), Pull::None);
53 let reset = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
54
55 let mut config = spim::Config::default();
56 config.frequency = spim::Frequency::M1;
57 config.mode = spim::MODE_2; // !!!
58 let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);
59 let spi = ExclusiveDevice::new(spi, cs);
60
61 let (device, mut control, runner) = embassy_net_esp_hosted::new(
62 make_static!(embassy_net_esp_hosted::State::new()),
63 spi,
64 handshake,
65 ready,
66 reset,
67 )
68 .await;
69
70 unwrap!(spawner.spawn(wifi_task(runner)));
71
72 // TODO: wait for ESP_INIT event instead of hardcoding delay.
73 Timer::after(Duration::from_secs(3)).await;
74
75 control.init().await;
76 control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await;
77
78 let config = embassy_net::Config::dhcpv4(Default::default());
79 // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
80 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
81 // dns_servers: Vec::new(),
82 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
83 // });
84
85 // Generate random seed
86 let mut rng = Rng::new(p.RNG, Irqs);
87 let mut seed = [0; 8];
88 rng.blocking_fill_bytes(&mut seed);
89 let seed = u64::from_le_bytes(seed);
90
91 // Init network stack
92 let stack = &*make_static!(Stack::new(
93 device,
94 config,
95 make_static!(StackResources::<2>::new()),
96 seed
97 ));
98
99 unwrap!(spawner.spawn(net_task(stack)));
100
101 // And now we can use it!
102
103 let mut rx_buffer = [0; 4096];
104 let mut tx_buffer = [0; 4096];
105 let mut buf = [0; 4096];
106
107 loop {
108 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
109 socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));
110
111 info!("Listening on TCP:1234...");
112 if let Err(e) = socket.accept(1234).await {
113 warn!("accept error: {:?}", e);
114 continue;
115 }
116
117 info!("Received connection from {:?}", socket.remote_endpoint());
118
119 loop {
120 let n = match socket.read(&mut buf).await {
121 Ok(0) => {
122 warn!("read EOF");
123 break;
124 }
125 Ok(n) => n,
126 Err(e) => {
127 warn!("read error: {:?}", e);
128 break;
129 }
130 };
131
132 info!("rxd {:02x}", &buf[..n]);
133
134 match socket.write_all(&buf[..n]).await {
135 Ok(()) => {}
136 Err(e) => {
137 warn!("write error: {:?}", e);
138 break;
139 }
140 };
141 }
142 }
143}