1use core::fmt::Debug;
2use core::marker::PhantomData;
3use core::time::Duration;
4use core::{ffi, ops, ptr};
5
6extern crate alloc;
7use alloc::boxed::Box;
8use alloc::sync::Arc;
9
10use embedded_svc::eth::*;
11
12#[cfg(any(
13 all(esp32, esp_idf_eth_use_esp32_emac),
14 any(
15 esp_idf_eth_spi_ethernet_dm9051,
16 esp_idf_eth_spi_ethernet_w5500,
17 esp_idf_eth_spi_ethernet_ksz8851snl,
18 esp_idf_comp_espressif__dm9051_enabled,
20 esp_idf_comp_espressif__w5500_enabled,
21 esp_idf_comp_espressif__ksz8851snl_enabled,
22 )
23))]
24use crate::hal::gpio;
25#[cfg(any(
26 esp_idf_eth_spi_ethernet_dm9051,
27 esp_idf_comp_espressif__dm9051_enabled,
28 esp_idf_eth_spi_ethernet_w5500,
29 esp_idf_comp_espressif__w5500_enabled,
30 esp_idf_eth_spi_ethernet_ksz8851snl,
31 esp_idf_comp_espressif__ksz8851snl_enabled
32))]
33use crate::hal::{spi, units::Hertz};
34
35use crate::sys::*;
36
37use crate::eventloop::{
38 EspEventDeserializer, EspEventLoop, EspEventSource, EspSubscription, EspSystemEventLoop, System,
39};
40use crate::handle::RawHandle;
41#[cfg(esp_idf_comp_esp_netif_enabled)]
42use crate::netif::*;
43use crate::private::*;
44
45#[cfg(all(esp32, esp_idf_eth_use_esp32_emac))]
46#[derive(Copy, Clone, Debug, Eq, PartialEq)]
47pub enum RmiiEthChipset {
48 #[cfg(esp_idf_version_at_least_5_4_0)]
52 Generic,
53 #[cfg(any(
54 not(esp_idf_version_at_least_6_0_0),
55 esp_idf_comp_espressif__ip101_enabled
56 ))]
57 IP101,
58 #[cfg(any(
59 not(esp_idf_version_at_least_6_0_0),
60 esp_idf_comp_espressif__rtl8201_enabled
61 ))]
62 RTL8201,
63 #[cfg(any(
64 not(esp_idf_version_at_least_6_0_0),
65 esp_idf_comp_espressif__lan87xx_enabled
66 ))]
67 LAN87XX,
68 #[cfg(any(
69 not(esp_idf_version_at_least_6_0_0),
70 esp_idf_comp_espressif__dp83848_enabled
71 ))]
72 DP83848,
73 #[cfg(esp_idf_version_major = "4")]
74 KSZ8041,
75 #[cfg(esp_idf_version = "4.4")]
76 KSZ8081,
77 #[cfg(all(
78 not(esp_idf_version_major = "4"),
79 any(
80 not(esp_idf_version_at_least_6_0_0),
81 esp_idf_comp_espressif__ksz80xx_enabled
82 )
83 ))]
84 KSZ80XX,
85}
86
87#[cfg(all(esp32, esp_idf_eth_use_esp32_emac))]
88pub enum RmiiClockConfig<'d> {
89 Input(gpio::Gpio0<'d>),
90 OutputGpio0(gpio::Gpio0<'d>),
91 OutputGpio16(gpio::Gpio16<'d>),
93 OutputInvertedGpio17(gpio::Gpio17<'d>),
94}
95
96#[cfg(all(esp32, esp_idf_eth_use_esp32_emac))]
97impl RmiiClockConfig<'_> {
98 fn eth_mac_clock_config(&self) -> eth_mac_clock_config_t {
99 #[cfg(not(esp_idf_version_at_least_6_0_0))]
100 let rmii = match self {
101 Self::Input(_) => eth_mac_clock_config_t__bindgen_ty_2 {
102 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_EXT_IN,
103 clock_gpio: emac_rmii_clock_gpio_t_EMAC_CLK_IN_GPIO,
104 },
105 Self::OutputGpio0(_) => eth_mac_clock_config_t__bindgen_ty_2 {
106 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_OUT,
107 clock_gpio: emac_rmii_clock_gpio_t_EMAC_APPL_CLK_OUT_GPIO,
108 },
109 Self::OutputGpio16(_) => eth_mac_clock_config_t__bindgen_ty_2 {
110 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_OUT,
111 clock_gpio: emac_rmii_clock_gpio_t_EMAC_CLK_OUT_GPIO,
112 },
113 Self::OutputInvertedGpio17(_) => eth_mac_clock_config_t__bindgen_ty_2 {
114 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_OUT,
115 clock_gpio: emac_rmii_clock_gpio_t_EMAC_CLK_OUT_180_GPIO,
116 },
117 };
118
119 #[cfg(esp_idf_version_at_least_6_0_0)]
121 let rmii = match self {
122 Self::Input(_) => eth_mac_clock_config_t__bindgen_ty_2 {
123 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_EXT_IN,
124 clock_gpio: 0,
125 },
126 Self::OutputGpio0(_) => eth_mac_clock_config_t__bindgen_ty_2 {
127 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_OUT,
128 clock_gpio: 0,
129 },
130 Self::OutputGpio16(_) => eth_mac_clock_config_t__bindgen_ty_2 {
131 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_OUT,
132 clock_gpio: 16,
133 },
134 Self::OutputInvertedGpio17(_) => eth_mac_clock_config_t__bindgen_ty_2 {
135 clock_mode: emac_rmii_clock_mode_t_EMAC_CLK_OUT,
136 clock_gpio: 17,
137 },
138 };
139
140 eth_mac_clock_config_t { rmii }
141 }
142}
143
144#[cfg(any(
145 esp_idf_eth_spi_ethernet_dm9051,
146 esp_idf_comp_espressif__dm9051_enabled,
147 esp_idf_eth_spi_ethernet_w5500,
148 esp_idf_comp_espressif__w5500_enabled,
149 esp_idf_eth_spi_ethernet_ksz8851snl,
150 esp_idf_comp_espressif__ksz8851snl_enabled
151))]
152#[derive(Copy, Clone, Debug, Eq, PartialEq)]
153pub enum SpiEthChipset {
154 #[cfg(any(
155 esp_idf_eth_spi_ethernet_dm9051,
156 esp_idf_comp_espressif__dm9051_enabled
157 ))]
158 DM9051,
159 #[cfg(any(esp_idf_eth_spi_ethernet_w5500, esp_idf_comp_espressif__w5500_enabled))]
160 W5500,
161 #[cfg(any(
162 esp_idf_eth_spi_ethernet_ksz8851snl,
163 esp_idf_comp_espressif__ksz8851snl_enabled
164 ))]
165 KSZ8851SNL,
166}
167
168#[cfg(any(
184 esp_idf_eth_spi_ethernet_dm9051,
185 esp_idf_comp_espressif__dm9051_enabled,
186 esp_idf_eth_spi_ethernet_w5500,
187 esp_idf_comp_espressif__w5500_enabled,
188 esp_idf_eth_spi_ethernet_ksz8851snl,
189 esp_idf_comp_espressif__ksz8851snl_enabled
190))]
191#[derive(Copy, Clone, Debug, Eq, PartialEq)]
192pub struct SpiEventSource<'d> {
193 #[cfg(not(any(
194 esp_idf_version_major = "4",
195 all(
196 esp_idf_version_major = "5",
197 any(
198 esp_idf_version_minor = "0",
199 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
200 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
201 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
202 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
203 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
204 )
205 ),
206 )))]
207 pub(crate) poll_interval_ms: u32,
208 pub(crate) interrupt_pin: i32,
209 _p: PhantomData<&'d mut ()>,
210}
211
212#[cfg(any(
213 esp_idf_eth_spi_ethernet_dm9051,
214 esp_idf_comp_espressif__dm9051_enabled,
215 esp_idf_eth_spi_ethernet_w5500,
216 esp_idf_comp_espressif__w5500_enabled,
217 esp_idf_eth_spi_ethernet_ksz8851snl,
218 esp_idf_comp_espressif__ksz8851snl_enabled
219))]
220impl<'d> SpiEventSource<'d> {
221 #[cfg(not(any(
227 esp_idf_version_major = "4",
228 all(
229 esp_idf_version_major = "5",
230 any(
231 esp_idf_version_minor = "0",
232 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
233 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
234 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
235 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
236 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
237 )
238 ),
239 )))]
240 pub fn polling(interval: Duration) -> Result<Self, core::num::TryFromIntError> {
241 let poll_interval_ms = interval.as_millis().try_into()?;
242
243 Ok(Self {
244 #[cfg(not(any(
245 esp_idf_version_major = "4",
246 all(
247 esp_idf_version_major = "5",
248 any(
249 esp_idf_version_minor = "0",
250 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
251 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
252 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
253 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
254 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
255 )
256 ),
257 )))]
258 poll_interval_ms,
259 interrupt_pin: -1,
260 _p: PhantomData,
261 })
262 }
263
264 pub fn interrupt(pin: impl gpio::InputPin + 'd) -> Self {
268 Self {
269 #[cfg(not(any(
270 esp_idf_version_major = "4",
271 all(
272 esp_idf_version_major = "5",
273 any(
274 esp_idf_version_minor = "0",
275 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
276 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
277 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
278 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
279 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
280 )
281 ),
282 )))]
283 poll_interval_ms: 0,
284 interrupt_pin: pin.pin() as _,
285 _p: PhantomData,
286 }
287 }
288}
289
290type RawCallback<'a> = Box<dyn FnMut(EthFrame) + Send + 'a>;
291
292struct UnsafeCallback<'a>(*mut RawCallback<'a>);
293
294impl<'a> UnsafeCallback<'a> {
295 #[allow(clippy::type_complexity)]
296 fn from(boxed: &mut Box<RawCallback<'a>>) -> Self {
297 Self(boxed.as_mut())
298 }
299
300 unsafe fn from_ptr(ptr: *mut ffi::c_void) -> Self {
301 Self(ptr.cast())
302 }
303
304 fn as_ptr(&self) -> *mut ffi::c_void {
305 self.0.cast()
306 }
307
308 unsafe fn call(&self, data: EthFrame) {
309 let reference = self.0.as_mut().unwrap();
310
311 (reference)(data);
312 }
313}
314
315#[derive(Copy, Clone, Debug, Eq, PartialEq)]
316enum Status {
317 Stopped,
318 Started,
319 Connected,
320 Disconnected,
321}
322
323pub struct RmiiEth;
324
325pub struct OpenEth;
326
327pub struct SpiEth<T> {
328 _driver: T,
329 device: Option<spi_device_handle_t>,
330}
331
332impl<T> Drop for SpiEth<T> {
333 fn drop(&mut self) {
334 if let Some(device) = self.device {
335 esp!(unsafe { spi_bus_remove_device(device) }).unwrap();
336
337 ::log::info!("SpiEth dropped");
338 }
339 }
340}
341
342pub struct EthDriver<'d, T> {
353 _flavor: T,
354 handle: esp_eth_handle_t,
355 status: Arc<mutex::Mutex<Status>>,
356 _subscription: EspSubscription<'static, System>,
357 callback: Option<Box<RawCallback<'d>>>,
358 _p: PhantomData<&'d mut ()>,
359}
360
361#[cfg(all(esp32, esp_idf_eth_use_esp32_emac))]
362impl<'d> EthDriver<'d, RmiiEth> {
363 #[allow(clippy::too_many_arguments)]
364 pub fn new(
365 mac: crate::hal::mac::MAC<'d>,
366 rmii_rdx0: gpio::Gpio25<'d>,
367 rmii_rdx1: gpio::Gpio26<'d>,
368 rmii_crs_dv: gpio::Gpio27<'d>,
369 rmii_mdc: impl gpio::OutputPin + 'd,
370 rmii_txd1: gpio::Gpio22<'d>,
371 rmii_tx_en: gpio::Gpio21<'d>,
372 rmii_txd0: gpio::Gpio19<'d>,
373 rmii_mdio: impl gpio::InputPin + gpio::OutputPin + 'd,
374 rmii_ref_clk_config: RmiiClockConfig<'d>,
375 rst: Option<impl gpio::OutputPin + 'd>,
376 chipset: RmiiEthChipset,
377 phy_addr: Option<u32>,
378 sysloop: EspSystemEventLoop,
379 ) -> Result<Self, EspError> {
380 Self::new_rmii(
381 mac,
382 rmii_rdx0,
383 rmii_rdx1,
384 rmii_crs_dv,
385 rmii_mdc,
386 rmii_txd1,
387 rmii_tx_en,
388 rmii_txd0,
389 rmii_mdio,
390 rmii_ref_clk_config,
391 rst,
392 chipset,
393 phy_addr,
394 sysloop,
395 )
396 }
397
398 #[allow(clippy::too_many_arguments)]
399 pub fn new_rmii(
400 _mac: crate::hal::mac::MAC<'d>,
401 _rmii_rdx0: gpio::Gpio25<'d>,
402 _rmii_rdx1: gpio::Gpio26<'d>,
403 _rmii_crs_dv: gpio::Gpio27<'d>,
404 rmii_mdc: impl gpio::OutputPin + 'd,
405 _rmii_txd1: gpio::Gpio22<'d>,
406 _rmii_tx_en: gpio::Gpio21<'d>,
407 _rmii_txd0: gpio::Gpio19<'d>,
408 rmii_mdio: impl gpio::InputPin + gpio::OutputPin + 'd,
409 rmii_ref_clk_config: RmiiClockConfig<'d>,
410 rst: Option<impl gpio::OutputPin + 'd>,
411 chipset: RmiiEthChipset,
412 phy_addr: Option<u32>,
413 sysloop: EspSystemEventLoop,
414 ) -> Result<Self, EspError> {
415 let rst = rst.map(|rst| rst.pin() as _);
416
417 let eth = Self::init(
418 Self::rmii_mac(
419 rmii_mdc.pin() as _,
420 rmii_mdio.pin() as _,
421 &rmii_ref_clk_config,
422 ),
423 Self::rmii_phy(chipset, rst, phy_addr)?,
424 None,
425 RmiiEth {},
426 sysloop,
427 )?;
428
429 Ok(eth)
430 }
431
432 fn rmii_phy(
433 chipset: RmiiEthChipset,
434 reset: Option<i32>,
435 phy_addr: Option<u32>,
436 ) -> Result<*mut esp_eth_phy_t, EspError> {
437 let phy_cfg = Self::eth_phy_default_config(reset, phy_addr);
438
439 let phy = match chipset {
444 #[cfg(esp_idf_version_at_least_5_4_0)]
445 RmiiEthChipset::Generic => unsafe { esp_eth_phy_new_generic(&phy_cfg) },
446 #[cfg(any(
447 not(esp_idf_version_at_least_6_0_0),
448 esp_idf_comp_espressif__ip101_enabled
449 ))]
450 RmiiEthChipset::IP101 => unsafe { esp_eth_phy_new_ip101(&phy_cfg) },
451 #[cfg(any(
452 not(esp_idf_version_at_least_6_0_0),
453 esp_idf_comp_espressif__rtl8201_enabled
454 ))]
455 RmiiEthChipset::RTL8201 => unsafe { esp_eth_phy_new_rtl8201(&phy_cfg) },
456 #[cfg(any(
457 not(esp_idf_version_at_least_6_0_0),
458 esp_idf_comp_espressif__lan87xx_enabled
459 ))]
460 RmiiEthChipset::LAN87XX => unsafe { esp_eth_phy_new_lan87xx(&phy_cfg) },
461 #[cfg(any(
462 not(esp_idf_version_at_least_6_0_0),
463 esp_idf_comp_espressif__dp83848_enabled
464 ))]
465 RmiiEthChipset::DP83848 => unsafe { esp_eth_phy_new_dp83848(&phy_cfg) },
466 #[cfg(esp_idf_version_major = "4")]
467 RmiiEthChipset::KSZ8041 => unsafe { esp_eth_phy_new_ksz8041(&phy_cfg) },
468 #[cfg(esp_idf_version = "4.4")]
469 RmiiEthChipset::KSZ8081 => unsafe { esp_eth_phy_new_ksz8081(&phy_cfg) },
470 #[cfg(all(
471 not(esp_idf_version_major = "4"),
472 any(
473 not(esp_idf_version_at_least_6_0_0),
474 esp_idf_comp_espressif__ksz80xx_enabled
475 )
476 ))]
477 RmiiEthChipset::KSZ80XX => unsafe { esp_eth_phy_new_ksz80xx(&phy_cfg) },
478 };
479
480 Ok(phy)
481 }
482
483 fn rmii_mac(mdc: i32, mdio: i32, clk_config: &RmiiClockConfig<'d>) -> *mut esp_eth_mac_t {
484 #[cfg(esp_idf_version_major = "4")]
485 let mac = {
486 let mut config = Self::eth_mac_default_config(mdc, mdio);
487
488 config.clock_config = clk_config.eth_mac_clock_config();
489
490 unsafe { esp_eth_mac_new_esp32(&config) }
491 };
492
493 #[cfg(not(esp_idf_version_major = "4"))]
494 let mac = {
495 let mut esp32_config = Self::eth_esp32_emac_default_config(mdc, mdio);
496 esp32_config.clock_config = clk_config.eth_mac_clock_config();
497
498 let config = Self::eth_mac_default_config(mdc, mdio);
499
500 unsafe { esp_eth_mac_new_esp32(&esp32_config, &config) }
501 };
502
503 mac
504 }
505
506 #[cfg(any(
507 esp_idf_version = "5.0",
508 esp_idf_version = "5.1",
509 esp_idf_version = "5.2"
510 ))]
511 fn eth_esp32_emac_default_config(mdc: i32, mdio: i32) -> eth_esp32_emac_config_t {
512 eth_esp32_emac_config_t {
513 smi_mdc_gpio_num: mdc,
514 smi_mdio_gpio_num: mdio,
515 interface: eth_data_interface_t_EMAC_DATA_INTERFACE_RMII,
516 ..Default::default()
517 }
518 }
519
520 #[cfg(esp_idf_version = "5.3")]
521 fn eth_esp32_emac_default_config(mdc: i32, mdio: i32) -> eth_esp32_emac_config_t {
522 eth_esp32_emac_config_t {
523 __bindgen_anon_1: eth_esp32_emac_config_t__bindgen_ty_1 {
524 __bindgen_anon_1: eth_esp32_emac_config_t__bindgen_ty_1__bindgen_ty_1 {
525 smi_mdc_gpio_num: mdc,
526 smi_mdio_gpio_num: mdio,
527 },
528 },
529 interface: eth_data_interface_t_EMAC_DATA_INTERFACE_RMII,
530 ..Default::default()
531 }
532 }
533
534 #[cfg(all(esp_idf_version_at_least_5_4_0, not(esp_idf_version_at_least_6_0_0)))]
535 fn eth_esp32_emac_default_config(mdc: i32, mdio: i32) -> eth_esp32_emac_config_t {
536 eth_esp32_emac_config_t {
537 __bindgen_anon_1: eth_esp32_emac_config_t__bindgen_ty_1 {
538 smi_gpio: emac_esp_smi_gpio_config_t {
539 mdc_num: mdc,
540 mdio_num: mdio,
541 },
542 },
543 interface: eth_data_interface_t_EMAC_DATA_INTERFACE_RMII,
544 ..Default::default()
545 }
546 }
547
548 #[cfg(esp_idf_version_at_least_6_0_0)]
550 fn eth_esp32_emac_default_config(mdc: i32, mdio: i32) -> eth_esp32_emac_config_t {
551 eth_esp32_emac_config_t {
552 smi_gpio: emac_esp_smi_gpio_config_t {
553 mdc_num: mdc,
554 mdio_num: mdio,
555 },
556 interface: eth_data_interface_t_EMAC_DATA_INTERFACE_RMII,
557 ..Default::default()
558 }
559 }
560}
561
562#[cfg(esp_idf_eth_use_openeth)]
563impl<'d> EthDriver<'d, OpenEth> {
564 pub fn new(
565 mac: crate::hal::mac::MAC<'d>,
566 sysloop: EspSystemEventLoop,
567 ) -> Result<Self, EspError> {
568 Self::new_openeth(mac, sysloop)
569 }
570
571 pub fn new_openeth(
572 _mac: crate::hal::mac::MAC<'d>,
573 sysloop: EspSystemEventLoop,
574 ) -> Result<Self, EspError> {
575 let eth = Self::init(
576 unsafe { esp_eth_mac_new_openeth(&Self::eth_mac_default_config(0, 0)) },
577 unsafe { esp_eth_phy_new_dp83848(&Self::eth_phy_default_config(None, None)) },
578 None,
579 OpenEth {},
580 sysloop,
581 )?;
582
583 Ok(eth)
584 }
585}
586
587#[cfg(any(
588 esp_idf_eth_spi_ethernet_dm9051,
589 esp_idf_comp_espressif__dm9051_enabled,
590 esp_idf_eth_spi_ethernet_w5500,
591 esp_idf_comp_espressif__w5500_enabled,
592 esp_idf_eth_spi_ethernet_ksz8851snl,
593 esp_idf_comp_espressif__ksz8851snl_enabled
594))]
595impl<'d, T> EthDriver<'d, SpiEth<T>>
596where
597 T: core::borrow::Borrow<spi::SpiDriver<'d>>,
598{
599 #[allow(clippy::too_many_arguments)]
600 pub fn new(
601 driver: T,
602 int: impl gpio::InputPin + 'd,
603 cs: Option<impl gpio::OutputPin + 'd>,
604 rst: Option<impl gpio::OutputPin + 'd>,
605 chipset: SpiEthChipset,
606 baudrate: Hertz,
607 mac_addr: Option<&[u8; 6]>,
608 phy_addr: Option<u32>,
609 sysloop: EspSystemEventLoop,
610 ) -> Result<Self, EspError> {
611 Self::new_spi(
612 driver, int, cs, rst, chipset, baudrate, mac_addr, phy_addr, sysloop,
613 )
614 }
615
616 #[allow(clippy::too_many_arguments)]
617 pub fn new_spi(
618 driver: T,
619 int: impl gpio::InputPin + 'd,
620 cs: Option<impl gpio::OutputPin + 'd>,
621 rst: Option<impl gpio::OutputPin + 'd>,
622 chipset: SpiEthChipset,
623 baudrate: Hertz,
624 mac_addr: Option<&[u8; 6]>,
625 phy_addr: Option<u32>,
626 sysloop: EspSystemEventLoop,
627 ) -> Result<Self, EspError> {
628 Self::new_spi_with_event_source(
629 driver,
630 SpiEventSource::interrupt(int),
631 cs,
632 rst,
633 chipset,
634 baudrate,
635 mac_addr,
636 phy_addr,
637 sysloop,
638 )
639 }
640
641 #[allow(clippy::too_many_arguments)]
642 pub fn new_spi_with_event_source(
643 driver: T,
644 event_source: SpiEventSource<'d>,
645 cs: Option<impl gpio::OutputPin + 'd>,
646 rst: Option<impl gpio::OutputPin + 'd>,
647 chipset: SpiEthChipset,
648 baudrate: Hertz,
649 mac_addr: Option<&[u8; 6]>,
650 phy_addr: Option<u32>,
651 sysloop: EspSystemEventLoop,
652 ) -> Result<Self, EspError> {
653 let (mac, phy, device) = Self::init_spi(
654 driver.borrow().host(),
655 chipset,
656 baudrate,
657 event_source.interrupt_pin,
658 #[cfg(not(any(
659 esp_idf_version_major = "4",
660 all(
661 esp_idf_version_major = "5",
662 any(
663 esp_idf_version_minor = "0",
664 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
665 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
666 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
667 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
668 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
669 )
670 ),
671 )))]
672 event_source.poll_interval_ms,
673 cs.map(|pin| pin.pin() as _),
674 rst.map(|pin| pin.pin() as _),
675 phy_addr,
676 )?;
677
678 let eth = Self::init(
679 mac,
680 phy,
681 mac_addr,
682 SpiEth {
683 _driver: driver,
684 device,
685 },
686 sysloop,
687 )?;
688
689 Ok(eth)
690 }
691
692 #[allow(clippy::unnecessary_literal_unwrap, clippy::too_many_arguments)]
693 fn init_spi(
694 host: spi_host_device_t,
695 chipset: SpiEthChipset,
696 baudrate: Hertz,
697 int_gpio_num: i32,
698 #[cfg(not(any(
699 esp_idf_version_major = "4",
700 all(
701 esp_idf_version_major = "5",
702 any(
703 esp_idf_version_minor = "0",
704 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
705 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
706 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
707 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
708 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
709 )
710 ),
711 )))]
712 poll_period_ms: u32,
713 cs: Option<i32>,
714 rst: Option<i32>,
715 phy_addr: Option<u32>,
716 ) -> Result<
717 (
718 *mut esp_eth_mac_t,
719 *mut esp_eth_phy_t,
720 Option<spi_device_handle_t>,
721 ),
722 EspError,
723 > {
724 crate::hal::gpio::enable_isr_service()?;
725
726 let mac_cfg = Self::eth_mac_default_config(0, 0);
727 let phy_cfg = Self::eth_phy_default_config(rst, phy_addr);
728
729 let (mac, phy, spi_handle) = match chipset {
730 #[cfg(any(
731 esp_idf_eth_spi_ethernet_dm9051,
732 esp_idf_comp_espressif__dm9051_enabled
733 ))]
734 SpiEthChipset::DM9051 => {
735 let spi_devcfg = Self::get_spi_conf(cs, 1, 7, baudrate);
736
737 #[cfg(esp_idf_version_major = "4")]
738 let spi_handle = Some(Self::init_spi_device(host, &spi_devcfg)?);
739
740 #[cfg(not(esp_idf_version_major = "4"))]
741 let spi_handle = None;
742
743 #[cfg(esp_idf_version_major = "4")]
744 let dm9051_cfg = eth_dm9051_config_t {
745 spi_hdl: spi_handle.unwrap() as *mut _,
746 int_gpio_num,
747 };
748
749 #[cfg(not(esp_idf_version_major = "4"))]
750 #[allow(clippy::needless_update)]
751 let dm9051_cfg = eth_dm9051_config_t {
752 spi_host_id: host,
753 spi_devcfg: &spi_devcfg as *const _ as *mut _,
754 int_gpio_num,
755 #[cfg(not(any(
756 esp_idf_version_major = "4",
757 all(
758 esp_idf_version_major = "5",
759 any(
760 esp_idf_version_minor = "0",
761 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
762 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
763 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
764 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
765 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
766 )
767 ),
768 )))]
769 poll_period_ms,
770 #[cfg(not(any(
771 esp_idf_version_major = "4",
772 all(
773 esp_idf_version_major = "5",
774 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
775 ),
776 )))]
777 custom_spi_driver: eth_spi_custom_driver_config_t::default(),
778 ..Default::default()
779 };
780
781 let mac = unsafe { esp_eth_mac_new_dm9051(&dm9051_cfg, &mac_cfg) };
782 let phy = unsafe { esp_eth_phy_new_dm9051(&phy_cfg) };
783
784 (mac, phy, spi_handle)
785 }
786 #[cfg(any(esp_idf_eth_spi_ethernet_w5500, esp_idf_comp_espressif__w5500_enabled))]
787 SpiEthChipset::W5500 => {
788 let spi_devcfg = Self::get_spi_conf(cs, 16, 8, baudrate);
789
790 #[cfg(esp_idf_version_major = "4")]
791 let spi_handle = Some(Self::init_spi_device(host, &spi_devcfg)?);
792
793 #[cfg(not(esp_idf_version_major = "4"))]
794 let spi_handle = None;
795
796 #[cfg(esp_idf_version_major = "4")]
797 let w5500_cfg = eth_w5500_config_t {
798 spi_hdl: spi_handle.unwrap() as *mut _,
799 int_gpio_num,
800 };
801
802 #[cfg(not(esp_idf_version_major = "4"))]
803 #[allow(clippy::needless_update)]
804 let w5500_cfg = eth_w5500_config_t {
805 spi_host_id: host,
806 spi_devcfg: &spi_devcfg as *const _ as *mut _,
807 int_gpio_num,
808 #[cfg(not(any(
809 esp_idf_version_major = "4",
810 all(
811 esp_idf_version_major = "5",
812 any(
813 esp_idf_version_minor = "0",
814 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
815 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
816 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
817 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
818 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
819 )
820 ),
821 )))]
822 poll_period_ms,
823 #[cfg(not(any(
824 esp_idf_version_major = "4",
825 all(
826 esp_idf_version_major = "5",
827 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
828 ),
829 )))]
830 custom_spi_driver: eth_spi_custom_driver_config_t::default(),
831 ..Default::default()
832 };
833
834 let mac = unsafe { esp_eth_mac_new_w5500(&w5500_cfg, &mac_cfg) };
835 let phy = unsafe { esp_eth_phy_new_w5500(&phy_cfg) };
836
837 (mac, phy, spi_handle)
838 }
839 #[cfg(any(
840 esp_idf_eth_spi_ethernet_ksz8851snl,
841 esp_idf_comp_espressif__ksz8851snl_enabled
842 ))]
843 SpiEthChipset::KSZ8851SNL => {
844 let spi_devcfg = Self::get_spi_conf(cs, 0, 0, baudrate);
845
846 #[cfg(esp_idf_version_major = "4")]
847 let spi_handle = Some(Self::init_spi_device(host, &spi_devcfg)?);
848
849 #[cfg(not(esp_idf_version_major = "4"))]
850 let spi_handle = None;
851
852 #[cfg(esp_idf_version_major = "4")]
853 let ksz8851snl_cfg = eth_ksz8851snl_config_t {
854 spi_hdl: spi_handle.unwrap() as *mut _,
855 int_gpio_num,
856 };
857
858 #[cfg(not(esp_idf_version_major = "4"))]
859 #[allow(clippy::needless_update)]
860 let ksz8851snl_cfg = eth_ksz8851snl_config_t {
861 spi_host_id: host,
862 spi_devcfg: &spi_devcfg as *const _ as *mut _,
863 int_gpio_num,
864 #[cfg(not(any(
865 esp_idf_version_major = "4",
866 all(
867 esp_idf_version_major = "5",
868 any(
869 esp_idf_version_minor = "0",
870 all(esp_idf_version_minor = "1", esp_idf_version_patch = "0"),
871 all(esp_idf_version_minor = "1", esp_idf_version_patch = "1"),
872 all(esp_idf_version_minor = "1", esp_idf_version_patch = "2"),
873 all(esp_idf_version_minor = "1", esp_idf_version_patch = "3"),
874 all(esp_idf_version_minor = "2", esp_idf_version_patch = "0"),
875 )
876 ),
877 )))]
878 poll_period_ms,
879 #[cfg(not(any(
880 esp_idf_version_major = "4",
881 all(
882 esp_idf_version_major = "5",
883 any(esp_idf_version_minor = "0", esp_idf_version_minor = "1")
884 ),
885 )))]
886 custom_spi_driver: eth_spi_custom_driver_config_t::default(),
887 ..Default::default()
888 };
889
890 let mac = unsafe { esp_eth_mac_new_ksz8851snl(&ksz8851snl_cfg, &mac_cfg) };
891 let phy = unsafe { esp_eth_phy_new_ksz8851snl(&phy_cfg) };
892
893 (mac, phy, spi_handle)
894 }
895 };
896
897 Ok((mac, phy, spi_handle))
898 }
899
900 fn get_spi_conf(
901 cs: Option<i32>,
902 command_bits: u8,
903 address_bits: u8,
904 baudrate: Hertz,
905 ) -> spi_device_interface_config_t {
906 spi_device_interface_config_t {
907 command_bits,
908 address_bits,
909 mode: 0,
910 clock_speed_hz: baudrate.0 as i32,
911 spics_io_num: cs.unwrap_or(-1),
912 queue_size: 20,
913 ..Default::default()
914 }
915 }
916
917 #[cfg(esp_idf_version_major = "4")]
918 fn init_spi_device(
919 host: spi_host_device_t,
920 conf: &spi_device_interface_config_t,
921 ) -> Result<spi_device_handle_t, EspError> {
922 let mut spi_handle: spi_device_handle_t = ptr::null_mut();
923
924 esp!(unsafe { spi_bus_add_device(host, conf, &mut spi_handle) })?;
925
926 Ok(spi_handle)
927 }
928}
929
930impl<'d, T> EthDriver<'d, T> {
931 fn init(
932 mac: *mut esp_eth_mac_t,
933 phy: *mut esp_eth_phy_t,
934 mac_addr: Option<&[u8; 6]>,
935 flavor: T,
936 sysloop: EspSystemEventLoop,
937 ) -> Result<Self, EspError> {
938 let cfg = Self::eth_default_config(mac, phy);
939
940 let mut handle: esp_eth_handle_t = ptr::null_mut();
941 esp!(unsafe { esp_eth_driver_install(&cfg, &mut handle) })?;
942
943 ::log::info!("Driver initialized");
944
945 if let Some(mac_addr) = mac_addr {
946 esp!(unsafe {
947 esp_eth_ioctl(
948 handle,
949 esp_eth_io_cmd_t_ETH_CMD_S_MAC_ADDR,
950 mac_addr.as_ptr() as *mut _,
951 )
952 })?;
953
954 ::log::info!("Attached MAC address: {mac_addr:?}");
955 }
956
957 let (waitable, subscription) = Self::subscribe(handle, &sysloop)?;
958
959 let eth = Self {
960 handle,
961 _flavor: flavor,
962 status: waitable,
963 _subscription: subscription,
964 callback: None,
965 _p: PhantomData,
966 };
967
968 ::log::info!("Initialization complete");
969
970 Ok(eth)
971 }
972
973 fn subscribe(
974 handle: esp_eth_handle_t,
975 sysloop: &EspEventLoop<System>,
976 ) -> Result<(Arc<mutex::Mutex<Status>>, EspSubscription<'static, System>), EspError> {
977 let status = Arc::new(mutex::Mutex::new(Status::Stopped));
978 let s_status = status.clone();
979
980 let handle = handle as usize;
981
982 let subscription = sysloop.subscribe::<EthEvent, _>(move |event| {
983 if event.is_for_handle(handle as _) {
984 let mut guard = s_status.lock();
985
986 match event {
987 EthEvent::Started(_) => *guard = Status::Started,
988 EthEvent::Stopped(_) => *guard = Status::Stopped,
989 EthEvent::Connected(_) => *guard = Status::Connected,
990 EthEvent::Disconnected(_) => *guard = Status::Disconnected,
991 EthEvent::Other(_) => (),
992 }
993 }
994 })?;
995
996 Ok((status, subscription))
997 }
998
999 pub fn is_started(&self) -> Result<bool, EspError> {
1000 let guard = self.status.lock();
1001
1002 Ok(*guard == Status::Started
1003 || *guard == Status::Connected
1004 || *guard == Status::Disconnected)
1005 }
1006
1007 pub fn is_connected(&self) -> Result<bool, EspError> {
1008 let guard = self.status.lock();
1009
1010 Ok(*guard == Status::Connected)
1011 }
1012
1013 pub fn start(&mut self) -> Result<(), EspError> {
1014 esp!(unsafe { esp_eth_start(self.handle) })?;
1015
1016 ::log::info!("Start requested");
1017
1018 Ok(())
1019 }
1020
1021 pub fn stop(&mut self) -> Result<(), EspError> {
1022 ::log::info!("Stopping");
1023
1024 let err = unsafe { esp_eth_stop(self.handle) };
1025 if err != ESP_ERR_INVALID_STATE {
1026 esp!(err)?;
1027 }
1028
1029 ::log::info!("Stop requested");
1030
1031 Ok(())
1032 }
1033
1034 pub fn set_rx_callback<F>(&mut self, callback: F) -> Result<(), EspError>
1035 where
1036 F: FnMut(EthFrame) + Send + 'static,
1037 {
1038 self.internal_set_rx_callback(callback)
1039 }
1040
1041 pub unsafe fn set_nonstatic_rx_callback<F>(&mut self, callback: F) -> Result<(), EspError>
1065 where
1066 F: FnMut(EthFrame) + Send + 'd,
1067 {
1068 self.internal_set_rx_callback(callback)
1069 }
1070
1071 fn internal_set_rx_callback<F>(&mut self, callback: F) -> Result<(), EspError>
1072 where
1073 F: FnMut(EthFrame) + Send + 'd,
1074 {
1075 let _ = self.stop();
1076
1077 let mut callback: Box<RawCallback> = Box::new(Box::new(callback));
1078
1079 let unsafe_callback = UnsafeCallback::from(&mut callback);
1080
1081 esp!(unsafe {
1082 esp_eth_update_input_path(self.handle(), Some(Self::handle), unsafe_callback.as_ptr())
1083 })?;
1084
1085 self.callback = Some(callback);
1086
1087 Ok(())
1088 }
1089
1090 pub fn send(&mut self, frame: &[u8]) -> Result<(), EspError> {
1091 esp!(unsafe {
1092 esp_eth_transmit(self.handle(), frame.as_ptr() as *mut _, frame.len() as _)
1093 })?;
1094
1095 Ok(())
1096 }
1097
1098 unsafe extern "C" fn handle(
1099 _handle: esp_eth_handle_t,
1100 buf: *mut u8,
1101 len: u32,
1102 event_handler_arg: *mut ffi::c_void,
1103 ) -> esp_err_t {
1104 UnsafeCallback::from_ptr(event_handler_arg as *mut _).call(EthFrame::new(buf, len));
1105
1106 ESP_OK
1107 }
1108
1109 fn clear_all(&mut self) -> Result<(), EspError> {
1110 let _ = self.stop(); unsafe {
1113 esp!(esp_eth_driver_uninstall(self.handle))?;
1114 }
1115
1116 ::log::info!("Driver deinitialized");
1117
1118 Ok(())
1119 }
1120
1121 pub fn set_promiscuous(&mut self, state: bool) -> Result<(), EspError> {
1127 esp!(unsafe {
1128 esp_eth_ioctl(
1129 self.handle(),
1130 esp_eth_io_cmd_t_ETH_CMD_S_PROMISCUOUS,
1131 &raw const state as *mut _,
1132 )
1133 })?;
1134
1135 if state {
1136 ::log::info!("Driver set in promiscuous mode");
1137 } else {
1138 ::log::info!("Driver set in non-promiscuous mode");
1139 }
1140
1141 Ok(())
1142 }
1143
1144 fn eth_default_config(mac: *mut esp_eth_mac_t, phy: *mut esp_eth_phy_t) -> esp_eth_config_t {
1145 esp_eth_config_t {
1146 mac,
1147 phy,
1148 check_link_period_ms: 2000,
1149 ..Default::default()
1150 }
1151 }
1152
1153 #[allow(clippy::needless_update)]
1154 fn eth_phy_default_config(reset_pin: Option<i32>, phy_addr: Option<u32>) -> eth_phy_config_t {
1155 eth_phy_config_t {
1156 phy_addr: phy_addr.map(|a| a as i32).unwrap_or(ESP_ETH_PHY_ADDR_AUTO),
1157 reset_timeout_ms: 100,
1158 autonego_timeout_ms: 4000,
1159 reset_gpio_num: reset_pin.unwrap_or(-1),
1160 ..Default::default()
1161 }
1162 }
1163
1164 #[cfg(esp_idf_version_major = "4")]
1165 fn eth_mac_default_config(mdc: i32, mdio: i32) -> eth_mac_config_t {
1166 eth_mac_config_t {
1167 sw_reset_timeout_ms: 100,
1168 rx_task_stack_size: 2048,
1169 rx_task_prio: 15,
1170 smi_mdc_gpio_num: mdc,
1171 smi_mdio_gpio_num: mdio,
1172 flags: 0,
1173 #[cfg(esp_idf_version = "4.4")]
1174 interface: eth_data_interface_t_EMAC_DATA_INTERFACE_RMII,
1175 ..Default::default()
1176 }
1177 }
1178
1179 #[cfg(not(esp_idf_version_major = "4"))]
1180 fn eth_mac_default_config(_mdc: i32, _mdio: i32) -> eth_mac_config_t {
1181 eth_mac_config_t {
1182 sw_reset_timeout_ms: 100,
1183 rx_task_stack_size: 4096,
1184 rx_task_prio: 15,
1185 flags: 0,
1186 }
1187 }
1188}
1189
1190impl<T> Eth for EthDriver<'_, T> {
1191 type Error = EspError;
1192
1193 fn start(&mut self) -> Result<(), Self::Error> {
1194 EthDriver::start(self)
1195 }
1196
1197 fn stop(&mut self) -> Result<(), Self::Error> {
1198 EthDriver::stop(self)
1199 }
1200
1201 fn is_started(&self) -> Result<bool, Self::Error> {
1202 EthDriver::is_started(self)
1203 }
1204
1205 fn is_connected(&self) -> Result<bool, Self::Error> {
1206 EthDriver::is_connected(self)
1207 }
1208}
1209
1210unsafe impl<T> Send for EthDriver<'_, T> {}
1211
1212impl<T> Drop for EthDriver<'_, T> {
1213 fn drop(&mut self) {
1214 self.clear_all().unwrap();
1215
1216 ::log::info!("EthDriver dropped");
1217 }
1218}
1219
1220impl<T> RawHandle for EthDriver<'_, T> {
1221 type Handle = esp_eth_handle_t;
1222
1223 fn handle(&self) -> Self::Handle {
1224 self.handle
1225 }
1226}
1227
1228pub struct EthFrame {
1229 buf: *mut u8,
1230 len: u32,
1231}
1232
1233unsafe impl Send for EthFrame {}
1234
1235impl EthFrame {
1236 const unsafe fn new(buf: *mut u8, len: u32) -> Self {
1237 Self { buf, len }
1238 }
1239
1240 pub const fn as_slice(&self) -> &[u8] {
1241 unsafe { core::slice::from_raw_parts(self.buf, self.len as _) }
1242 }
1243
1244 pub fn as_mut_slice(&mut self) -> &mut [u8] {
1245 unsafe { core::slice::from_raw_parts_mut(self.buf, self.len as _) }
1246 }
1247}
1248
1249impl ops::Deref for EthFrame {
1250 type Target = [u8];
1251
1252 fn deref(&self) -> &[u8] {
1253 unsafe { core::slice::from_raw_parts(self.buf, self.len as _) }
1254 }
1255}
1256
1257impl ops::DerefMut for EthFrame {
1258 fn deref_mut(&mut self) -> &mut [u8] {
1259 unsafe { core::slice::from_raw_parts_mut(self.buf, self.len as _) }
1260 }
1261}
1262
1263impl Drop for EthFrame {
1264 fn drop(&mut self) {
1265 unsafe { free(self.buf.cast()) };
1266 }
1267}
1268
1269#[cfg(esp_idf_comp_esp_netif_enabled)]
1281pub struct EspEth<'d, T> {
1282 glue_handle: *mut esp_eth_netif_glue_t,
1283 netif: EspNetif,
1284 driver: EthDriver<'d, T>,
1285}
1286
1287#[cfg(esp_idf_comp_esp_netif_enabled)]
1288impl<'d, T> EspEth<'d, T> {
1289 pub fn wrap(driver: EthDriver<'d, T>) -> Result<Self, EspError> {
1290 Self::wrap_all(driver, EspNetif::new(NetifStack::Eth)?)
1291 }
1292
1293 pub fn wrap_all(driver: EthDriver<'d, T>, netif: EspNetif) -> Result<Self, EspError> {
1294 let mut this = Self {
1295 driver,
1296 netif,
1297 glue_handle: core::ptr::null_mut(),
1298 };
1299
1300 this.attach_netif()?;
1301
1302 Ok(this)
1303 }
1304
1305 pub fn swap_netif(&mut self, netif: EspNetif) -> Result<EspNetif, EspError> {
1306 self.detach_netif()?;
1307
1308 let old_netif = core::mem::replace(&mut self.netif, netif);
1309
1310 self.attach_netif()?;
1311
1312 Ok(old_netif)
1313 }
1314
1315 pub fn driver(&self) -> &EthDriver<'d, T> {
1316 &self.driver
1317 }
1318
1319 pub fn driver_mut(&mut self) -> &mut EthDriver<'d, T> {
1320 &mut self.driver
1321 }
1322
1323 pub fn netif(&self) -> &EspNetif {
1324 &self.netif
1325 }
1326
1327 pub fn netif_mut(&mut self) -> &mut EspNetif {
1328 &mut self.netif
1329 }
1330
1331 pub fn start(&mut self) -> Result<(), EspError> {
1332 self.driver_mut().start()
1333 }
1334
1335 pub fn stop(&mut self) -> Result<(), EspError> {
1336 self.driver_mut().stop()
1337 }
1338
1339 pub fn is_started(&self) -> Result<bool, EspError> {
1340 self.driver().is_started()
1341 }
1342
1343 pub fn is_connected(&self) -> Result<bool, EspError> {
1344 self.driver().is_connected()
1345 }
1346
1347 pub fn is_up(&self) -> Result<bool, EspError> {
1348 Ok(self.is_connected()? && self.netif().is_up()?)
1349 }
1350
1351 fn attach_netif(&mut self) -> Result<(), EspError> {
1352 let _ = self.driver.stop();
1353
1354 let glue_handle = unsafe { esp_eth_new_netif_glue(self.driver.handle()) };
1355
1356 esp!(unsafe { esp_netif_attach(self.netif.handle(), glue_handle as *mut _) })?;
1357
1358 self.glue_handle = glue_handle;
1359
1360 Ok(())
1361 }
1362
1363 fn detach_netif(&mut self) -> Result<(), EspError> {
1364 let _ = self.driver.stop();
1365
1366 esp!(unsafe { esp_eth_del_netif_glue(self.glue_handle as *mut _) })?;
1367
1368 self.glue_handle = core::ptr::null_mut();
1369
1370 Ok(())
1371 }
1372}
1373
1374#[cfg(esp_idf_comp_esp_netif_enabled)]
1375impl<T> Drop for EspEth<'_, T> {
1376 fn drop(&mut self) {
1377 self.detach_netif().unwrap();
1378
1379 ::log::info!("EspEth dropped");
1380 }
1381}
1382
1383#[cfg(esp_idf_comp_esp_netif_enabled)]
1384unsafe impl<T> Send for EspEth<'_, T> {}
1385
1386#[cfg(esp_idf_comp_esp_netif_enabled)]
1387impl<T> RawHandle for EspEth<'_, T> {
1388 type Handle = *mut esp_eth_netif_glue_t;
1389
1390 fn handle(&self) -> Self::Handle {
1391 self.glue_handle
1392 }
1393}
1394
1395#[cfg(esp_idf_comp_esp_netif_enabled)]
1396impl<T> Eth for EspEth<'_, T> {
1397 type Error = EspError;
1398
1399 fn start(&mut self) -> Result<(), Self::Error> {
1400 EspEth::start(self)
1401 }
1402
1403 fn stop(&mut self) -> Result<(), Self::Error> {
1404 EspEth::stop(self)
1405 }
1406
1407 fn is_started(&self) -> Result<bool, Self::Error> {
1408 EspEth::is_started(self)
1409 }
1410
1411 fn is_connected(&self) -> Result<bool, Self::Error> {
1412 EspEth::is_connected(self)
1413 }
1414}
1415
1416#[cfg(esp_idf_comp_esp_netif_enabled)]
1417impl<T> NetifStatus for EspEth<'_, T> {
1418 fn is_up(&self) -> Result<bool, EspError> {
1419 EspEth::is_up(self)
1420 }
1421}
1422
1423#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1424pub enum EthEvent {
1425 Started(esp_eth_handle_t),
1426 Stopped(esp_eth_handle_t),
1427 Connected(esp_eth_handle_t),
1428 Disconnected(esp_eth_handle_t),
1429
1430 Other(i32),
1436}
1437
1438unsafe impl Send for EthEvent {}
1439
1440impl EthEvent {
1441 pub fn is_for(&self, raw_handle: impl RawHandle<Handle = esp_eth_handle_t>) -> bool {
1442 self.is_for_handle(raw_handle.handle())
1443 }
1444
1445 pub fn is_for_handle(&self, handle: esp_eth_handle_t) -> bool {
1446 core::ptr::eq(self.handle(), handle)
1447 }
1448
1449 pub fn handle(&self) -> esp_eth_handle_t {
1450 let handle = match self {
1451 Self::Started(handle) => *handle,
1452 Self::Stopped(handle) => *handle,
1453 Self::Connected(handle) => *handle,
1454 Self::Disconnected(handle) => *handle,
1455 Self::Other(_) => core::ptr::null_mut(),
1456 };
1457
1458 handle as esp_eth_handle_t
1459 }
1460}
1461
1462unsafe impl EspEventSource for EthEvent {
1463 fn source() -> Option<&'static ffi::CStr> {
1464 Some(unsafe { ffi::CStr::from_ptr(ETH_EVENT) })
1465 }
1466}
1467
1468impl EspEventDeserializer for EthEvent {
1469 type Data<'a> = Self;
1470
1471 #[allow(non_upper_case_globals, non_snake_case)]
1472 fn deserialize(data: &crate::eventloop::EspEvent) -> Self {
1473 let eth_handle_ref =
1474 unsafe { (data.payload.unwrap() as *const _ as *const esp_eth_handle_t).as_ref() };
1475
1476 let event_id = data.event_id as u32;
1477
1478 if event_id == eth_event_t_ETHERNET_EVENT_START {
1479 EthEvent::Started(*eth_handle_ref.unwrap() as _)
1480 } else if event_id == eth_event_t_ETHERNET_EVENT_STOP {
1481 EthEvent::Stopped(*eth_handle_ref.unwrap() as _)
1482 } else if event_id == eth_event_t_ETHERNET_EVENT_CONNECTED {
1483 EthEvent::Connected(*eth_handle_ref.unwrap() as _)
1484 } else if event_id == eth_event_t_ETHERNET_EVENT_DISCONNECTED {
1485 EthEvent::Disconnected(*eth_handle_ref.unwrap() as _)
1486 } else {
1487 ::log::warn!("EthEvent: unknown event ID {event_id}, ignoring");
1488 EthEvent::Other(event_id as i32)
1489 }
1490 }
1491}
1492
1493const CONNECT_TIMEOUT: Duration = Duration::from_secs(15);
1494
1495pub struct BlockingEth<T> {
1496 eth: T,
1497 event_loop: crate::eventloop::EspSystemEventLoop,
1498}
1499
1500impl<T> BlockingEth<T>
1501where
1502 T: Eth<Error = EspError>,
1503{
1504 pub fn wrap(eth: T, event_loop: EspSystemEventLoop) -> Result<Self, EspError> {
1505 Ok(Self { eth, event_loop })
1506 }
1507
1508 pub fn eth(&self) -> &T {
1509 &self.eth
1510 }
1511
1512 pub fn eth_mut(&mut self) -> &mut T {
1513 &mut self.eth
1514 }
1515
1516 pub fn is_started(&self) -> Result<bool, EspError> {
1517 self.eth.is_started()
1518 }
1519
1520 pub fn start(&mut self) -> Result<(), EspError> {
1521 self.eth.start()?;
1522 self.eth_wait_while(|| self.eth.is_started().map(|s| !s), None)
1523 }
1524
1525 pub fn stop(&mut self) -> Result<(), EspError> {
1526 self.eth.stop()?;
1527 self.eth_wait_while(|| self.eth.is_started(), None)
1528 }
1529
1530 pub fn is_connected(&self) -> Result<bool, EspError> {
1531 self.eth.is_connected()
1532 }
1533
1534 pub fn wait_connected(&self) -> Result<(), EspError> {
1535 self.eth_wait_while(
1536 || self.eth.is_connected().map(|s| !s),
1537 Some(CONNECT_TIMEOUT),
1538 )
1539 }
1540
1541 pub fn eth_wait_while<F: Fn() -> Result<bool, EspError>>(
1542 &self,
1543 matcher: F,
1544 timeout: Option<Duration>,
1545 ) -> Result<(), EspError> {
1546 let wait = crate::eventloop::Wait::new::<EthEvent>(&self.event_loop)?;
1547
1548 wait.wait_while(matcher, timeout)
1549 }
1550}
1551
1552#[cfg(esp_idf_comp_esp_netif_enabled)]
1553impl<T> BlockingEth<T>
1554where
1555 T: NetifStatus,
1556{
1557 pub fn is_up(&self) -> Result<bool, EspError> {
1558 self.eth.is_up()
1559 }
1560
1561 pub fn wait_netif_up(&self) -> Result<(), EspError> {
1562 self.ip_wait_while(|| self.eth.is_up().map(|s| !s), Some(CONNECT_TIMEOUT))
1563 }
1564
1565 pub fn ip_wait_while<F: Fn() -> Result<bool, EspError>>(
1566 &self,
1567 matcher: F,
1568 timeout: Option<core::time::Duration>,
1569 ) -> Result<(), EspError> {
1570 let wait = crate::eventloop::Wait::new::<IpEvent>(&self.event_loop)?;
1571
1572 wait.wait_while(matcher, timeout)
1573 }
1574}
1575
1576impl<T> Eth for BlockingEth<T>
1577where
1578 T: Eth<Error = EspError>,
1579{
1580 type Error = EspError;
1581
1582 fn is_started(&self) -> Result<bool, Self::Error> {
1583 BlockingEth::is_started(self)
1584 }
1585
1586 fn is_connected(&self) -> Result<bool, Self::Error> {
1587 BlockingEth::is_connected(self)
1588 }
1589
1590 fn start(&mut self) -> Result<(), Self::Error> {
1591 BlockingEth::start(self)
1592 }
1593
1594 fn stop(&mut self) -> Result<(), Self::Error> {
1595 BlockingEth::stop(self)
1596 }
1597}
1598
1599#[cfg(esp_idf_comp_esp_netif_enabled)]
1600impl<T> NetifStatus for BlockingEth<T>
1601where
1602 T: NetifStatus,
1603{
1604 fn is_up(&self) -> Result<bool, EspError> {
1605 BlockingEth::is_up(self)
1606 }
1607}
1608
1609#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
1610pub struct AsyncEth<T> {
1611 pub(crate) eth: T,
1612 pub(crate) event_loop: crate::eventloop::EspSystemEventLoop,
1613 pub(crate) timer_service: crate::timer::EspTaskTimerService,
1614}
1615
1616#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
1617impl<T> AsyncEth<T>
1618where
1619 T: Eth<Error = EspError>,
1620{
1621 pub fn wrap(
1622 eth: T,
1623 event_loop: EspSystemEventLoop,
1624 timer_service: crate::timer::EspTaskTimerService,
1625 ) -> Result<Self, EspError> {
1626 Ok(Self {
1627 eth,
1628 event_loop,
1629 timer_service,
1630 })
1631 }
1632
1633 pub fn eth(&self) -> &T {
1634 &self.eth
1635 }
1636
1637 pub fn eth_mut(&mut self) -> &mut T {
1638 &mut self.eth
1639 }
1640
1641 pub fn is_started(&self) -> Result<bool, EspError> {
1642 self.eth.is_started()
1643 }
1644
1645 pub fn is_connected(&self) -> Result<bool, EspError> {
1646 self.eth.is_connected()
1647 }
1648
1649 pub async fn start(&mut self) -> Result<(), EspError> {
1650 self.eth.start()?;
1651 self.eth_wait_while(|this| this.eth.is_started().map(|s| !s), None)
1652 .await
1653 }
1654
1655 pub async fn stop(&mut self) -> Result<(), EspError> {
1656 self.eth.stop()?;
1657 self.eth_wait_while(|this| this.eth.is_started(), None)
1658 .await
1659 }
1660
1661 pub async fn wait_connected(&mut self) -> Result<(), EspError> {
1662 self.eth_wait_while(
1663 |this| this.eth.is_connected().map(|s| !s),
1664 Some(CONNECT_TIMEOUT),
1665 )
1666 .await
1667 }
1668
1669 pub async fn eth_wait_while<F: FnMut(&mut Self) -> Result<bool, EspError>>(
1670 &mut self,
1671 mut matcher: F,
1672 timeout: Option<Duration>,
1673 ) -> Result<(), EspError> {
1674 let mut wait =
1675 crate::eventloop::AsyncWait::<EthEvent, _>::new(&self.event_loop, &self.timer_service)?;
1676
1677 wait.wait_while(|| matcher(self), timeout).await
1678 }
1679}
1680
1681#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
1682#[cfg(esp_idf_comp_esp_netif_enabled)]
1683impl<T> AsyncEth<T>
1684where
1685 T: NetifStatus,
1686{
1687 pub fn is_up(&self) -> Result<bool, EspError> {
1688 self.eth.is_up()
1689 }
1690
1691 pub async fn wait_netif_up(&mut self) -> Result<(), EspError> {
1692 self.ip_wait_while(|this| this.eth.is_up().map(|s| !s), Some(CONNECT_TIMEOUT))
1693 .await
1694 }
1695
1696 pub async fn ip_wait_while<F: FnMut(&mut Self) -> Result<bool, EspError>>(
1697 &mut self,
1698 mut matcher: F,
1699 timeout: Option<core::time::Duration>,
1700 ) -> Result<(), EspError> {
1701 let mut wait =
1702 crate::eventloop::AsyncWait::<IpEvent, _>::new(&self.event_loop, &self.timer_service)?;
1703
1704 wait.wait_while(|| matcher(self), timeout).await
1705 }
1706}
1707
1708#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
1709impl<T> embedded_svc::eth::asynch::Eth for AsyncEth<T>
1710where
1711 T: Eth<Error = EspError>,
1712{
1713 type Error = T::Error;
1714
1715 async fn start(&mut self) -> Result<(), Self::Error> {
1716 AsyncEth::start(self).await
1717 }
1718
1719 async fn stop(&mut self) -> Result<(), Self::Error> {
1720 AsyncEth::stop(self).await
1721 }
1722
1723 async fn is_started(&self) -> Result<bool, Self::Error> {
1724 AsyncEth::is_started(self)
1725 }
1726
1727 async fn is_connected(&self) -> Result<bool, Self::Error> {
1728 AsyncEth::is_connected(self)
1729 }
1730}
1731
1732#[cfg(esp_idf_comp_esp_netif_enabled)]
1733impl<T> crate::netif::asynch::NetifStatus for EspEth<'_, T> {
1734 async fn is_up(&self) -> Result<bool, EspError> {
1735 EspEth::is_up(self)
1736 }
1737}
1738
1739#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
1740#[cfg(esp_idf_comp_esp_netif_enabled)]
1741impl<T> crate::netif::asynch::NetifStatus for AsyncEth<T>
1742where
1743 T: NetifStatus,
1744{
1745 async fn is_up(&self) -> Result<bool, EspError> {
1746 AsyncEth::is_up(self)
1747 }
1748}