Skip to main content

esp_idf_svc/
netif.rs

1//! Network abstraction
2//!
3//! The purpose of ESP-NETIF library is twofold:
4//!
5//! - It provides an abstraction layer for the application on top of the TCP/IP
6//!   stack. This will allow applications to choose between IP stacks in the
7//!   future.
8//! - The APIs it provides are thread safe, even if the underlying TCP/IP
9//!   stack APIs are not.
10
11use core::num::NonZeroU32;
12use core::{ffi, fmt, ptr};
13
14use crate::ipv4;
15use crate::sys::*;
16
17use ::log::info;
18
19use crate::eventloop::{EspEventDeserializer, EspEventSource};
20use crate::handle::RawHandle;
21use crate::private::common::*;
22use crate::private::cstr::*;
23use crate::private::mutex;
24
25#[cfg(feature = "alloc")]
26pub use driver::*;
27#[cfg(esp_idf_lwip_ppp_support)]
28pub use ppp::*;
29
30#[derive(Copy, Clone, Debug, PartialEq, Eq)]
31#[cfg_attr(feature = "std", derive(Hash))]
32pub enum NetifStack {
33    /// Station mode (WiFi client)
34    Sta,
35    #[cfg(esp_idf_esp_wifi_softap_support)]
36    /// Access point mode (WiFi router)
37    Ap,
38    /// Ethernet
39    Eth,
40    #[cfg(esp_idf_lwip_ppp_support)]
41    /// Point-to-Point Protocol (PPP)
42    Ppp,
43    #[cfg(esp_idf_lwip_slip_support)]
44    /// Serial Line Internet Protocol (SLIP)
45    Slip,
46    #[cfg(all(esp_idf_comp_openthread_enabled, esp_idf_openthread_enabled,))]
47    Thread,
48}
49
50impl NetifStack {
51    /// Initialize the ESP Netif stack
52    ///
53    /// This function is called automatically when a new `EspNetif` instance is created,
54    /// but it can also be called manually by the user - for example, in cases when some
55    /// networking code needs to be started _before_ there is even one active `EspNetif`
56    /// interface.
57    ///
58    /// The function needs to be called only once for the duration of the program - ideally
59    /// early on during the app booststraping process. Once initialized, the netif stack cannot
60    /// be de-initialized.
61    pub fn initialize() -> Result<(), EspError> {
62        initialize_netif_stack()
63    }
64
65    pub fn default_configuration(&self) -> NetifConfiguration {
66        match self {
67            Self::Sta => NetifConfiguration::wifi_default_client(),
68            #[cfg(esp_idf_esp_wifi_softap_support)]
69            Self::Ap => NetifConfiguration::wifi_default_router(),
70            Self::Eth => NetifConfiguration::eth_default_client(),
71            #[cfg(esp_idf_lwip_ppp_support)]
72            Self::Ppp => NetifConfiguration::ppp_default_client(),
73            #[cfg(esp_idf_lwip_slip_support)]
74            Self::Slip => NetifConfiguration::slip_default_client(),
75            #[cfg(all(esp_idf_comp_openthread_enabled, esp_idf_openthread_enabled,))]
76            Self::Thread => NetifConfiguration::thread_default(),
77        }
78    }
79
80    fn default_mac(&self) -> Result<Option<[u8; 6]>, EspError> {
81        if let Some(mac_type) = self.default_mac_raw_type() {
82            let mut mac = [0; 6];
83
84            let result = esp!(unsafe { esp_read_mac(mac.as_mut_ptr() as *mut _, mac_type) });
85
86            // On SoCs without native WiFi hardware (e.g., ESP32-P4 using
87            // esp_wifi_remote), the eFuse has no WiFi MAC address.
88            // esp_read_mac will fail for WiFi MAC types. Return None and let
89            // esp_wifi_start() set the real MAC from the companion chip later.
90            #[cfg(not(esp_idf_soc_wifi_supported))]
91            {
92                if result.is_ok() {
93                    Ok(Some(mac))
94                } else {
95                    Ok(None)
96                }
97            }
98
99            #[cfg(esp_idf_soc_wifi_supported)]
100            {
101                result?;
102
103                Ok(Some(mac))
104            }
105        } else {
106            Ok(None)
107        }
108    }
109
110    fn default_mac_raw_type(&self) -> Option<esp_mac_type_t> {
111        match self {
112            Self::Sta => Some(esp_mac_type_t_ESP_MAC_WIFI_STA),
113            #[cfg(esp_idf_esp_wifi_softap_support)]
114            Self::Ap => Some(esp_mac_type_t_ESP_MAC_WIFI_SOFTAP),
115            Self::Eth => Some(esp_mac_type_t_ESP_MAC_ETH),
116            #[cfg(all(esp_idf_comp_openthread_enabled, esp_idf_openthread_enabled,))]
117            Self::Thread => {
118                #[cfg(esp_idf_soc_ieee802154_supported)]
119                let mac_type = Some(esp_mac_type_t_ESP_MAC_IEEE802154);
120
121                #[cfg(not(esp_idf_soc_ieee802154_supported))]
122                let mac_type = None;
123
124                mac_type
125            }
126            #[cfg(any(esp_idf_lwip_slip_support, esp_idf_lwip_ppp_support))]
127            _ => None,
128        }
129    }
130
131    fn default_raw_stack(&self) -> *const esp_netif_netstack_config_t {
132        unsafe {
133            match self {
134                Self::Sta => _g_esp_netif_netstack_default_wifi_sta,
135                #[cfg(esp_idf_esp_wifi_softap_support)]
136                Self::Ap => _g_esp_netif_netstack_default_wifi_ap,
137                Self::Eth => _g_esp_netif_netstack_default_eth,
138                #[cfg(esp_idf_lwip_ppp_support)]
139                Self::Ppp => _g_esp_netif_netstack_default_ppp,
140                #[cfg(esp_idf_lwip_slip_support)]
141                Self::Slip => _g_esp_netif_netstack_default_slip,
142                #[cfg(all(
143                    esp_idf_comp_openthread_enabled,
144                    esp_idf_openthread_enabled,
145                    esp_idf_version_major = "4"
146                ))]
147                Self::Thread => _g_esp_netif_netstack_default_openthread,
148                #[cfg(all(
149                    esp_idf_comp_openthread_enabled,
150                    esp_idf_openthread_enabled,
151                    not(esp_idf_version_major = "4")
152                ))]
153                Self::Thread => &g_esp_netif_netstack_default_openthread,
154            }
155        }
156    }
157}
158
159#[derive(Clone, Debug, PartialEq, Eq)]
160pub struct NetifConfiguration {
161    pub flags: u32,
162    pub got_ip_event_id: Option<NonZeroU32>,
163    pub lost_ip_event_id: Option<NonZeroU32>,
164    pub key: heapless::String<32>,
165    pub description: heapless::String<8>,
166    pub route_priority: u32,
167    pub ip_configuration: Option<ipv4::Configuration>,
168    pub stack: NetifStack,
169    pub custom_mac: Option<[u8; 6]>,
170}
171
172impl NetifConfiguration {
173    pub fn eth_default_client() -> Self {
174        Self {
175            flags: esp_netif_flags_ESP_NETIF_FLAG_GARP
176                | esp_netif_flags_ESP_NETIF_FLAG_EVENT_IP_MODIFIED,
177            got_ip_event_id: NonZeroU32::new(ip_event_t_IP_EVENT_ETH_GOT_IP as _),
178            lost_ip_event_id: NonZeroU32::new(ip_event_t_IP_EVENT_ETH_LOST_IP as _),
179            key: "ETH_DEF".try_into().unwrap(),
180            description: "eth".try_into().unwrap(),
181            route_priority: 60,
182            ip_configuration: Some(ipv4::Configuration::Client(Default::default())),
183            stack: NetifStack::Eth,
184            custom_mac: None,
185        }
186    }
187
188    pub fn eth_default_router() -> Self {
189        Self {
190            flags: 0,
191            got_ip_event_id: None,
192            lost_ip_event_id: None,
193            key: "ETH_RT_DEF".try_into().unwrap(),
194            description: "ethrt".try_into().unwrap(),
195            route_priority: 50,
196            ip_configuration: Some(ipv4::Configuration::Router(Default::default())),
197            stack: NetifStack::Eth,
198            custom_mac: None,
199        }
200    }
201
202    pub fn wifi_default_client() -> Self {
203        Self {
204            flags: esp_netif_flags_ESP_NETIF_FLAG_GARP
205                | esp_netif_flags_ESP_NETIF_FLAG_EVENT_IP_MODIFIED,
206            got_ip_event_id: NonZeroU32::new(ip_event_t_IP_EVENT_STA_GOT_IP as _),
207            lost_ip_event_id: NonZeroU32::new(ip_event_t_IP_EVENT_STA_LOST_IP as _),
208            key: "WIFI_STA_DEF".try_into().unwrap(),
209            description: "sta".try_into().unwrap(),
210            route_priority: 100,
211            ip_configuration: Some(ipv4::Configuration::Client(Default::default())),
212            stack: NetifStack::Sta,
213            custom_mac: None,
214        }
215    }
216
217    #[cfg(esp_idf_esp_wifi_softap_support)]
218    pub fn wifi_default_router() -> Self {
219        Self {
220            flags: 0,
221            got_ip_event_id: None,
222            lost_ip_event_id: None,
223            key: "WIFI_AP_DEF".try_into().unwrap(),
224            description: "ap".try_into().unwrap(),
225            route_priority: 10,
226            ip_configuration: Some(ipv4::Configuration::Router(Default::default())),
227            stack: NetifStack::Ap,
228            custom_mac: None,
229        }
230    }
231
232    #[cfg(esp_idf_lwip_ppp_support)]
233    pub fn ppp_default_client() -> Self {
234        Self {
235            flags: esp_netif_flags_ESP_NETIF_FLAG_IS_PPP,
236            got_ip_event_id: NonZeroU32::new(ip_event_t_IP_EVENT_PPP_GOT_IP as _),
237            lost_ip_event_id: NonZeroU32::new(ip_event_t_IP_EVENT_PPP_LOST_IP as _),
238            key: "PPP_CL_DEF".try_into().unwrap(),
239            description: "ppp".try_into().unwrap(),
240            route_priority: 30,
241            ip_configuration: Some(ipv4::Configuration::Client(Default::default())),
242            stack: NetifStack::Ppp,
243            custom_mac: None,
244        }
245    }
246
247    #[cfg(esp_idf_lwip_ppp_support)]
248    pub fn ppp_default_router() -> Self {
249        Self {
250            flags: esp_netif_flags_ESP_NETIF_FLAG_IS_PPP,
251            got_ip_event_id: None,
252            lost_ip_event_id: None,
253            key: "PPP_RT_DEF".try_into().unwrap(),
254            description: "ppprt".try_into().unwrap(),
255            route_priority: 20,
256            ip_configuration: Some(ipv4::Configuration::Router(Default::default())),
257            stack: NetifStack::Ppp,
258            custom_mac: None,
259        }
260    }
261
262    #[cfg(esp_idf_lwip_slip_support)]
263    pub fn slip_default_client() -> Self {
264        Self {
265            flags: 0,
266            get_ip_event: None,
267            lost_ip_event: None,
268            key: "SLIP_CL_DEF".try_into().unwrap(),
269            description: "slip".try_into().unwrap(),
270            route_priority: 35,
271            ip_configuration: Some(ipv4::Configuration::Client(Default::default())),
272            stack: NetifStack::Slip,
273            custom_mac: None,
274        }
275    }
276
277    #[cfg(esp_idf_lwip_slip_support)]
278    pub fn slip_default_router() -> Self {
279        Self {
280            flags: 0,
281            get_ip_event: None,
282            lost_ip_event: None,
283            key: "SLIP_RT_DEF".try_into().unwrap(),
284            description: "sliprt".try_into().unwrap(),
285            route_priority: 25,
286            ip_configuration: Some(ipv4::Configuration::Router(Default::default())),
287            stack: NetifStack::Slip,
288            custom_mac: None,
289        }
290    }
291
292    #[cfg(all(esp_idf_comp_openthread_enabled, esp_idf_openthread_enabled,))]
293    pub fn thread_default() -> Self {
294        Self {
295            flags: 0,
296            got_ip_event_id: None,
297            lost_ip_event_id: None,
298            key: "OT_DEF".try_into().unwrap(),
299            description: "thread".try_into().unwrap(),
300            route_priority: 15,
301            ip_configuration: None,
302            stack: NetifStack::Thread,
303            custom_mac: None,
304        }
305    }
306}
307
308static INITALIZED: mutex::Mutex<bool> = mutex::Mutex::new(false);
309
310fn initialize_netif_stack() -> Result<(), EspError> {
311    let mut guard = INITALIZED.lock();
312
313    if !*guard {
314        esp!(unsafe { esp_netif_init() })?;
315
316        *guard = true;
317    }
318
319    Ok(())
320}
321
322#[derive(Debug)]
323pub struct EspNetif {
324    handle: *mut esp_netif_t,
325    _got_ip_event_id: Option<NonZeroU32>,
326    _lost_ip_event_id: Option<NonZeroU32>,
327}
328
329impl EspNetif {
330    pub fn new(stack: NetifStack) -> Result<Self, EspError> {
331        Self::new_with_conf(&stack.default_configuration())
332    }
333
334    pub fn new_with_conf(conf: &NetifConfiguration) -> Result<Self, EspError> {
335        initialize_netif_stack()?;
336
337        let c_if_key = to_cstring_arg(conf.key.as_str())?;
338        let c_if_description = to_cstring_arg(conf.description.as_str())?;
339
340        let initial_mac = if let Some(custom_mac) = conf.custom_mac {
341            custom_mac
342        } else {
343            conf.stack.default_mac()?.unwrap_or([0; 6])
344        };
345
346        let (mut esp_inherent_config, ip_info, dhcps, dns, secondary_dns, hostname) = match conf
347            .ip_configuration
348        {
349            Some(ipv4::Configuration::Client(ref ip_conf)) => (
350                esp_netif_inherent_config_t {
351                    flags: conf.flags
352                        | (if matches!(ip_conf, ipv4::ClientConfiguration::DHCP(_)) {
353                            esp_netif_flags_ESP_NETIF_DHCP_CLIENT
354                        } else {
355                            0
356                        }),
357                    mac: initial_mac,
358                    ip_info: ptr::null(),
359                    get_ip_event: conf.got_ip_event_id.map(NonZeroU32::get).unwrap_or(0),
360                    lost_ip_event: conf.lost_ip_event_id.map(NonZeroU32::get).unwrap_or(0),
361                    if_key: c_if_key.as_c_str().as_ptr() as _,
362                    if_desc: c_if_description.as_c_str().as_ptr() as _,
363                    route_prio: conf.route_priority as _,
364                    #[cfg(not(esp_idf_version_major = "4"))]
365                    bridge_info: ptr::null_mut(),
366                    #[cfg(esp_idf_version_at_least_6_0_0)]
367                    mtu: 0,
368                },
369                match ip_conf {
370                    ipv4::ClientConfiguration::DHCP(_) => None,
371                    ipv4::ClientConfiguration::Fixed(ref fixed_conf) => Some(esp_netif_ip_info_t {
372                        ip: Newtype::<esp_ip4_addr_t>::from(fixed_conf.ip).0,
373                        netmask: Newtype::<esp_ip4_addr_t>::from(fixed_conf.subnet.mask).0,
374                        gw: Newtype::<esp_ip4_addr_t>::from(fixed_conf.subnet.gateway).0,
375                    }),
376                },
377                false,
378                match ip_conf {
379                    ipv4::ClientConfiguration::DHCP(_) => None,
380                    ipv4::ClientConfiguration::Fixed(ref fixed_conf) => fixed_conf.dns,
381                },
382                match ip_conf {
383                    ipv4::ClientConfiguration::DHCP(_) => None,
384                    ipv4::ClientConfiguration::Fixed(ref fixed_conf) => fixed_conf.secondary_dns,
385                },
386                match ip_conf {
387                    ipv4::ClientConfiguration::DHCP(ref dhcp_conf) => dhcp_conf.hostname.as_ref(),
388                    ipv4::ClientConfiguration::Fixed(_) => None,
389                },
390            ),
391            Some(ipv4::Configuration::Router(ref ip_conf)) => (
392                esp_netif_inherent_config_t {
393                    flags: conf.flags
394                        | (if ip_conf.dhcp_enabled {
395                            esp_netif_flags_ESP_NETIF_DHCP_SERVER
396                        } else {
397                            0
398                        })
399                        | esp_netif_flags_ESP_NETIF_FLAG_AUTOUP,
400                    mac: initial_mac,
401                    ip_info: ptr::null(),
402                    get_ip_event: conf.got_ip_event_id.map(NonZeroU32::get).unwrap_or(0),
403                    lost_ip_event: conf.lost_ip_event_id.map(NonZeroU32::get).unwrap_or(0),
404                    if_key: c_if_key.as_c_str().as_ptr() as _,
405                    if_desc: c_if_description.as_c_str().as_ptr() as _,
406                    route_prio: conf.route_priority as _,
407                    #[cfg(not(esp_idf_version_major = "4"))]
408                    bridge_info: ptr::null_mut(),
409                    #[cfg(esp_idf_version_at_least_6_0_0)]
410                    mtu: 0,
411                },
412                Some(esp_netif_ip_info_t {
413                    ip: Newtype::<esp_ip4_addr_t>::from(ip_conf.subnet.gateway).0,
414                    netmask: Newtype::<esp_ip4_addr_t>::from(ip_conf.subnet.mask).0,
415                    gw: Newtype::<esp_ip4_addr_t>::from(ip_conf.subnet.gateway).0,
416                }),
417                ip_conf.dhcp_enabled,
418                ip_conf.dns,
419                None, /* For APs, ESP-IDF supports setting a primary DNS only ip_conf.secondary_dns */
420                None,
421            ),
422            None => (
423                esp_netif_inherent_config_t {
424                    flags: conf.flags | esp_netif_flags_ESP_NETIF_FLAG_AUTOUP,
425                    mac: initial_mac,
426                    ip_info: ptr::null(),
427                    get_ip_event: conf.got_ip_event_id.map(NonZeroU32::get).unwrap_or(0),
428                    lost_ip_event: conf.lost_ip_event_id.map(NonZeroU32::get).unwrap_or(0),
429                    if_key: c_if_key.as_c_str().as_ptr() as _,
430                    if_desc: c_if_description.as_c_str().as_ptr() as _,
431                    route_prio: conf.route_priority as _,
432                    #[cfg(not(esp_idf_version_major = "4"))]
433                    bridge_info: ptr::null_mut(),
434                    #[cfg(esp_idf_version_at_least_6_0_0)]
435                    mtu: 0,
436                },
437                None,
438                false,
439                None,
440                None,
441                None,
442            ),
443        };
444
445        if let Some(ip_info) = ip_info.as_ref() {
446            esp_inherent_config.ip_info = ip_info;
447        }
448
449        let cfg = esp_netif_config_t {
450            base: &esp_inherent_config,
451            driver: ptr::null(),
452            stack: conf.stack.default_raw_stack(),
453        };
454
455        let mut netif = Self {
456            handle: unsafe { esp_netif_new(&cfg).as_mut() }
457                .ok_or(EspError::from_infallible::<ESP_ERR_INVALID_ARG>())?,
458            _got_ip_event_id: conf.got_ip_event_id,
459            _lost_ip_event_id: conf.lost_ip_event_id,
460        };
461
462        if let Some(dns) = dns {
463            netif.set_dns(dns);
464
465            if dhcps {
466                #[cfg(esp_idf_version_major = "4")]
467                let mut dhcps_dns_value: dhcps_offer_t = dhcps_offer_option_OFFER_DNS as _;
468
469                // Strangely dhcps_offer_t and dhcps_offer_option_* are not included in ESP-IDF V5's bindings
470                #[cfg(not(esp_idf_version_major = "4"))]
471                let mut dhcps_dns_value: u8 = 2_u8;
472
473                esp!(unsafe {
474                    esp_netif_dhcps_option(
475                        netif.handle,
476                        esp_netif_dhcp_option_mode_t_ESP_NETIF_OP_SET,
477                        esp_netif_dhcp_option_id_t_ESP_NETIF_DOMAIN_NAME_SERVER,
478                        &mut dhcps_dns_value as *mut _ as *mut _,
479                        core::mem::size_of_val(&dhcps_dns_value) as u32,
480                    )
481                })?;
482            }
483        }
484
485        if let Some(secondary_dns) = secondary_dns {
486            netif.set_secondary_dns(secondary_dns);
487        }
488
489        if let Some(hostname) = hostname {
490            netif.set_hostname(hostname)?;
491        }
492
493        Ok(netif)
494    }
495
496    pub fn is_netif_up(&self) -> Result<bool, EspError> {
497        Ok(unsafe { esp_netif_is_netif_up(self.handle) })
498    }
499
500    // TODO: Copy and rename to `is_up_ipv4` and deprecate the `is_up` variant in future
501    pub fn is_up(&self) -> Result<bool, EspError> {
502        if !self.is_netif_up()? {
503            Ok(false)
504        } else {
505            let mut ip_info = Default::default();
506            unsafe { esp!(esp_netif_get_ip_info(self.handle, &mut ip_info)) }?;
507
508            Ok(ipv4::IpInfo::from(Newtype(ip_info)).ip != ipv4::Ipv4Addr::new(0, 0, 0, 0))
509        }
510    }
511
512    pub fn get_ip_info(&self) -> Result<ipv4::IpInfo, EspError> {
513        let mut ip_info = Default::default();
514        unsafe { esp!(esp_netif_get_ip_info(self.handle, &mut ip_info)) }?;
515
516        Ok(ipv4::IpInfo {
517            // Get the DNS information
518            dns: Some(self.get_dns()),
519            secondary_dns: Some(self.get_secondary_dns()),
520            ..Newtype(ip_info).into()
521        })
522    }
523
524    pub fn get_key(&self) -> heapless::String<32> {
525        unsafe { from_cstr_ptr(esp_netif_get_ifkey(self.handle)) }
526            .try_into()
527            .unwrap()
528    }
529
530    pub fn get_index(&self) -> u32 {
531        unsafe { esp_netif_get_netif_impl_index(self.handle) as _ }
532    }
533
534    pub fn get_name(&self) -> heapless::String<6> {
535        let mut netif_name = [0u8; 7];
536
537        esp!(unsafe {
538            esp_netif_get_netif_impl_name(self.handle, netif_name.as_mut_ptr() as *mut _)
539        })
540        .unwrap();
541
542        from_cstr(&netif_name).try_into().unwrap()
543    }
544
545    pub fn get_mac(&self) -> Result<[u8; 6], EspError> {
546        let mut mac = [0u8; 6];
547
548        esp!(unsafe { esp_netif_get_mac(self.handle, mac.as_mut_ptr() as *mut _) })?;
549        Ok(mac)
550    }
551
552    pub fn set_mac(&mut self, mac: &[u8; 6]) -> Result<(), EspError> {
553        esp!(unsafe { esp_netif_set_mac(self.handle, mac.as_ptr() as *mut _) })?;
554        Ok(())
555    }
556
557    pub fn get_dns(&self) -> ipv4::Ipv4Addr {
558        let mut dns_info = Default::default();
559
560        unsafe {
561            esp!(esp_netif_get_dns_info(
562                self.handle,
563                esp_netif_dns_type_t_ESP_NETIF_DNS_MAIN,
564                &mut dns_info
565            ))
566            .unwrap();
567
568            Newtype(dns_info.ip.u_addr.ip4).into()
569        }
570    }
571
572    fn set_dns(&mut self, dns: ipv4::Ipv4Addr) {
573        let mut dns_info: esp_netif_dns_info_t = Default::default();
574
575        unsafe {
576            dns_info.ip.u_addr.ip4 = Newtype::<esp_ip4_addr_t>::from(dns).0;
577
578            esp!(esp_netif_set_dns_info(
579                self.handle,
580                esp_netif_dns_type_t_ESP_NETIF_DNS_MAIN,
581                &mut dns_info
582            ))
583            .unwrap();
584        }
585    }
586
587    pub fn get_secondary_dns(&self) -> ipv4::Ipv4Addr {
588        let mut dns_info = Default::default();
589
590        unsafe {
591            esp!(esp_netif_get_dns_info(
592                self.handle,
593                esp_netif_dns_type_t_ESP_NETIF_DNS_BACKUP,
594                &mut dns_info
595            ))
596            .unwrap();
597
598            Newtype(dns_info.ip.u_addr.ip4).into()
599        }
600    }
601
602    fn set_secondary_dns(&mut self, secondary_dns: ipv4::Ipv4Addr) {
603        let mut dns_info: esp_netif_dns_info_t = Default::default();
604
605        unsafe {
606            dns_info.ip.u_addr.ip4 = Newtype::<esp_ip4_addr_t>::from(secondary_dns).0;
607
608            esp!(esp_netif_set_dns_info(
609                self.handle,
610                esp_netif_dns_type_t_ESP_NETIF_DNS_BACKUP,
611                &mut dns_info
612            ))
613            .unwrap();
614        }
615    }
616
617    pub fn get_hostname(&self) -> Result<heapless::String<30>, EspError> {
618        let mut ptr: *const ffi::c_char = ptr::null();
619        esp!(unsafe { esp_netif_get_hostname(self.handle, &mut ptr) })?;
620
621        Ok(unsafe { from_cstr_ptr(ptr) }.try_into().unwrap())
622    }
623
624    fn set_hostname(&mut self, hostname: &str) -> Result<(), EspError> {
625        let hostname = to_cstring_arg(hostname)?;
626
627        esp!(unsafe { esp_netif_set_hostname(self.handle, hostname.as_ptr() as *const _) })?;
628
629        Ok(())
630    }
631
632    #[cfg(esp_idf_lwip_ipv4_napt)]
633    pub fn enable_napt(&mut self, enable: bool) {
634        unsafe {
635            crate::sys::ip_napt_enable_no(
636                (esp_netif_get_netif_impl_index(self.handle) - 1) as u8,
637                if enable { 1 } else { 0 },
638            )
639        };
640    }
641}
642
643impl Drop for EspNetif {
644    fn drop(&mut self) {
645        unsafe { esp_netif_destroy(self.handle) };
646
647        info!("Dropped");
648    }
649}
650
651unsafe impl Send for EspNetif {}
652
653impl RawHandle for EspNetif {
654    type Handle = *mut esp_netif_t;
655
656    fn handle(&self) -> Self::Handle {
657        self.handle
658    }
659}
660
661#[derive(Copy, Clone)]
662pub struct ApStaIpAssignment<'a>(&'a ip_event_ap_staipassigned_t);
663
664impl ApStaIpAssignment<'_> {
665    #[cfg(not(esp_idf_version_major = "4"))]
666    pub fn netif_handle(&self) -> *mut esp_netif_t {
667        self.0.esp_netif
668    }
669
670    pub fn ip(&self) -> ipv4::Ipv4Addr {
671        ipv4::Ipv4Addr::from(Newtype(self.0.ip))
672    }
673
674    #[cfg(not(esp_idf_version_major = "4"))]
675    pub fn mac(&self) -> [u8; 6] {
676        self.0.mac
677    }
678}
679
680impl fmt::Debug for ApStaIpAssignment<'_> {
681    #[cfg(esp_idf_version_major = "4")]
682    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683        f.debug_struct("ApStaIpAssignment")
684            .field("ip", &self.ip())
685            .finish()
686    }
687
688    #[cfg(not(esp_idf_version_major = "4"))]
689    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690        f.debug_struct("ApStaIpAssignment")
691            .field("ip", &self.ip())
692            .field("mac", &self.mac())
693            .finish()
694    }
695}
696
697#[derive(Copy, Clone)]
698pub struct DhcpIpAssignment<'a>(&'a ip_event_got_ip_t);
699
700impl DhcpIpAssignment<'_> {
701    pub fn netif_handle(&self) -> *mut esp_netif_t {
702        self.0.esp_netif
703    }
704
705    pub fn ip(&self) -> ipv4::Ipv4Addr {
706        ipv4::Ipv4Addr::from(Newtype(self.0.ip_info.ip))
707    }
708
709    pub fn gateway(&self) -> ipv4::Ipv4Addr {
710        ipv4::Ipv4Addr::from(Newtype(self.0.ip_info.gw))
711    }
712
713    pub fn mask(&self) -> ipv4::Mask {
714        Newtype(self.0.ip_info.netmask).try_into().unwrap()
715    }
716
717    pub fn ip_info(&self) -> ipv4::IpInfo {
718        ipv4::IpInfo {
719            ip: self.ip(),
720            subnet: ipv4::Subnet {
721                gateway: self.gateway(),
722                mask: self.mask(),
723            },
724            dns: None,           // TODO
725            secondary_dns: None, // TODO
726        }
727    }
728
729    pub fn is_ip_changed(&self) -> bool {
730        self.0.ip_changed
731    }
732}
733
734impl fmt::Debug for DhcpIpAssignment<'_> {
735    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
736        f.debug_struct("DhcpIpAssignment")
737            .field("netif_handle", &self.netif_handle())
738            .field("ip", &self.ip())
739            .field("gateway", &self.gateway())
740            .field("mask", &self.mask())
741            .field("is_ip_changed", &self.is_ip_changed())
742            .finish()
743    }
744}
745
746#[derive(Copy, Clone)]
747pub struct DhcpIp6Assignment<'a>(&'a ip_event_got_ip6_t);
748
749impl DhcpIp6Assignment<'_> {
750    pub fn netif_handle(&self) -> *mut esp_netif_t {
751        self.0.esp_netif
752    }
753
754    pub fn addr(&self) -> core::net::Ipv6Addr {
755        Newtype(self.0.ip6_info.ip).into()
756    }
757
758    pub fn ip(&self) -> [u32; 4] {
759        self.0.ip6_info.ip.addr
760    }
761
762    pub fn ip_zone(&self) -> u8 {
763        self.0.ip6_info.ip.zone
764    }
765
766    pub fn ip_index(&self) -> u32 {
767        self.0.ip_index as _
768    }
769}
770
771impl fmt::Debug for DhcpIp6Assignment<'_> {
772    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
773        f.debug_struct("DhcpIp6Assignment")
774            .field("netif_handle", &self.netif_handle())
775            .field("addr", &self.addr())
776            .field("ip_zone", &self.ip_zone())
777            .field("ip_index", &self.ip_index())
778            .finish()
779    }
780}
781
782#[derive(Copy, Clone, Debug)]
783pub enum IpEvent<'a> {
784    ApStaIpAssigned(ApStaIpAssignment<'a>),
785    DhcpIpAssigned(DhcpIpAssignment<'a>),
786    DhcpIp6Assigned(DhcpIp6Assignment<'a>),
787    DhcpIpDeassigned(*mut esp_netif_t),
788
789    /// An event ID not recognised by this version of the library was received.
790    ///
791    /// This variant is produced instead of panicking when an unknown event ID
792    /// arrives, allowing applications to remain forward-compatible with
793    /// ESP-IDF versions that introduce new IP events.
794    Other(i32),
795}
796
797unsafe impl Send for IpEvent<'_> {}
798
799impl IpEvent<'_> {
800    pub fn is_for(&self, raw_handle: &impl RawHandle<Handle = *mut esp_netif_t>) -> bool {
801        self.is_for_handle(raw_handle.handle())
802    }
803
804    pub fn is_for_handle(&self, handle: *mut esp_netif_t) -> bool {
805        self.handle()
806            .map(|event_handle| core::ptr::eq(event_handle, handle))
807            .unwrap_or(false)
808    }
809
810    pub fn handle(&self) -> Option<*mut esp_netif_t> {
811        match self {
812            #[cfg(not(esp_idf_version_major = "4"))]
813            Self::ApStaIpAssigned(assignment) => Some(assignment.netif_handle()),
814            #[cfg(esp_idf_version_major = "4")]
815            Self::ApStaIpAssigned(_) => None,
816            Self::DhcpIpAssigned(assignment) => Some(assignment.netif_handle()),
817            Self::DhcpIp6Assigned(assignment) => Some(assignment.netif_handle()),
818            Self::DhcpIpDeassigned(handle) => Some(*handle),
819            Self::Other(_) => None,
820        }
821    }
822}
823
824unsafe impl EspEventSource for IpEvent<'_> {
825    fn source() -> Option<&'static ffi::CStr> {
826        Some(unsafe { CStr::from_ptr(IP_EVENT) })
827    }
828}
829
830impl EspEventDeserializer for IpEvent<'_> {
831    type Data<'d> = IpEvent<'d>;
832
833    #[allow(non_upper_case_globals, non_snake_case)]
834    fn deserialize<'d>(data: &crate::eventloop::EspEvent<'d>) -> IpEvent<'d> {
835        let event_id = data.event_id as u32;
836
837        if event_id == ip_event_t_IP_EVENT_AP_STAIPASSIGNED {
838            let event = unsafe {
839                (data.payload.unwrap() as *const _ as *const ip_event_ap_staipassigned_t)
840                    .as_ref()
841                    .unwrap()
842            };
843
844            IpEvent::ApStaIpAssigned(ApStaIpAssignment(event))
845        } else if event_id == ip_event_t_IP_EVENT_STA_GOT_IP
846            || event_id == ip_event_t_IP_EVENT_ETH_GOT_IP
847            || event_id == ip_event_t_IP_EVENT_PPP_GOT_IP
848        {
849            let event = unsafe {
850                (data.payload.unwrap() as *const _ as *const ip_event_got_ip_t)
851                    .as_ref()
852                    .unwrap()
853            };
854
855            IpEvent::DhcpIpAssigned(DhcpIpAssignment(event))
856        } else if event_id == ip_event_t_IP_EVENT_GOT_IP6 {
857            let event = unsafe {
858                (data.payload.unwrap() as *const _ as *const ip_event_got_ip6_t)
859                    .as_ref()
860                    .unwrap()
861            };
862
863            IpEvent::DhcpIp6Assigned(DhcpIp6Assignment(event))
864        } else if event_id == ip_event_t_IP_EVENT_STA_LOST_IP
865            || event_id == ip_event_t_IP_EVENT_PPP_LOST_IP
866            || event_id == ip_event_t_IP_EVENT_ETH_LOST_IP
867        {
868            let netif_handle_mut = unsafe {
869                (data.payload.unwrap() as *const _ as *mut esp_netif_t)
870                    .as_mut()
871                    .unwrap()
872            };
873
874            IpEvent::DhcpIpDeassigned(netif_handle_mut as *mut _)
875        } else {
876            ::log::warn!("IpEvent: unknown event ID {event_id}, ignoring");
877            IpEvent::Other(event_id as i32)
878        }
879    }
880}
881
882pub trait NetifStatus {
883    fn is_up(&self) -> Result<bool, EspError>;
884}
885
886impl<T> NetifStatus for &T
887where
888    T: NetifStatus,
889{
890    fn is_up(&self) -> Result<bool, EspError> {
891        (**self).is_up()
892    }
893}
894
895impl<T> NetifStatus for &mut T
896where
897    T: NetifStatus,
898{
899    fn is_up(&self) -> Result<bool, EspError> {
900        (**self).is_up()
901    }
902}
903
904impl NetifStatus for EspNetif {
905    fn is_up(&self) -> Result<bool, EspError> {
906        EspNetif::is_up(self)
907    }
908}
909
910const UP_TIMEOUT: core::time::Duration = core::time::Duration::from_secs(15);
911
912pub struct BlockingNetif<T> {
913    netif: T,
914    event_loop: crate::eventloop::EspSystemEventLoop,
915}
916
917impl<T> BlockingNetif<T>
918where
919    T: NetifStatus,
920{
921    pub fn wrap(netif: T, event_loop: crate::eventloop::EspSystemEventLoop) -> Self {
922        Self { netif, event_loop }
923    }
924
925    pub fn is_up(&self) -> Result<bool, EspError> {
926        self.netif.is_up()
927    }
928
929    pub fn wait_netif_up(&self) -> Result<(), EspError> {
930        self.ip_wait_while(|| self.netif.is_up().map(|s| !s), Some(UP_TIMEOUT))
931    }
932
933    pub fn ip_wait_while<F: Fn() -> Result<bool, EspError>>(
934        &self,
935        matcher: F,
936        timeout: Option<core::time::Duration>,
937    ) -> Result<(), EspError> {
938        let wait = crate::eventloop::Wait::new::<IpEvent>(&self.event_loop)?;
939
940        wait.wait_while(matcher, timeout)
941    }
942}
943
944impl<T> NetifStatus for BlockingNetif<T>
945where
946    T: NetifStatus,
947{
948    fn is_up(&self) -> Result<bool, EspError> {
949        BlockingNetif::is_up(self)
950    }
951}
952
953#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
954pub struct AsyncNetif<T> {
955    netif: T,
956    event_loop: crate::eventloop::EspSystemEventLoop,
957    timer_service: crate::timer::EspTaskTimerService,
958}
959
960#[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
961impl<T> AsyncNetif<T>
962where
963    T: NetifStatus,
964{
965    pub fn wrap(
966        netif: T,
967        event_loop: crate::eventloop::EspSystemEventLoop,
968        timer_service: crate::timer::EspTaskTimerService,
969    ) -> Self {
970        Self {
971            netif,
972            event_loop,
973            timer_service,
974        }
975    }
976
977    pub fn is_up(&self) -> Result<bool, EspError> {
978        self.netif.is_up()
979    }
980
981    pub async fn wait_netif_up(&mut self) -> Result<(), EspError> {
982        self.ip_wait_while(|this| this.netif.is_up().map(|s| !s), Some(UP_TIMEOUT))
983            .await
984    }
985
986    pub async fn ip_wait_while<F: FnMut(&mut Self) -> Result<bool, EspError>>(
987        &mut self,
988        mut matcher: F,
989        timeout: Option<core::time::Duration>,
990    ) -> Result<(), EspError> {
991        let mut wait =
992            crate::eventloop::AsyncWait::<IpEvent, _>::new(&self.event_loop, &self.timer_service)?;
993
994        wait.wait_while(|| matcher(self), timeout).await
995    }
996}
997
998#[cfg(feature = "alloc")]
999mod driver {
1000    use core::borrow::BorrowMut;
1001
1002    use ::log::debug;
1003
1004    use crate::handle::RawHandle;
1005    use crate::sys::*;
1006
1007    use super::EspNetif;
1008
1009    pub struct EspNetifDriver<'d, T>
1010    where
1011        T: BorrowMut<EspNetif>,
1012    {
1013        inner: alloc::boxed::Box<EspNetifDriverInner<'d, T>>,
1014        started: bool,
1015    }
1016
1017    impl<T> EspNetifDriver<'static, T>
1018    where
1019        T: BorrowMut<EspNetif>,
1020    {
1021        /// Create a new netif driver around the provided `EspNetif` instance.
1022        ///
1023        /// The driver transport is represented by:
1024        /// - The `tx` callback that the driver would call when it wants to ingest a packet
1025        ///   into the underlying transport
1026        /// - `EsNetifDriver::rx`, which should be called by the transport when a new packet
1027        ///   has arrived that has to be ingested in the driver
1028        ///   
1029        /// The transport can be anything, but with - say - PPP netif - it would typically be UART,
1030        /// and the `tx` callback implementation is simply expected to write the PPP packet into UART.
1031        ///
1032        /// Arguments:
1033        /// - `netif` is the `EspNetif` instance that the driver will manage
1034        /// - `got_ip_event_id` and `lost_ip_event_id` are the event IDs that the driver
1035        ///   will listen to so that it can connect/disconnect the netif upon receival
1036        ///   / loss of IP
1037        /// - `post_attach_cfg` is a netif-specific configuration that will be executed
1038        ///   after the netif is attached. For example, for a PPP netif, the post attach
1039        ///   configuration might want to invoke `EspNetif::set_ppp_conf`.
1040        /// - `tx` is the callback that the driver will call when it wants to ingest a packet
1041        ///   into the underlying transport
1042        ///
1043        /// Example:
1044        /// ```ignore
1045        /// let (uart_rx, uart_tx) = uart.into_split();
1046        ///
1047        /// let mut driver = EspNetifDriver::new(
1048        ///     EspNetif::new(NetifStack::Ppp)?,
1049        ///     |netif| netif.set_ppp_conf(&PppConfiguration {
1050        ///         phase_events_enabled: false,
1051        ///         ..Default::default()
1052        ///     }),
1053        ///     move |data| uart_tx.write_all(data),
1054        /// )?;
1055        ///
1056        /// loop {
1057        ///     let mut buffer = [0; 128];
1058        ///     let len = uart_rx.read(&mut buffer)?;
1059        ///     driver.rx(&buffer[..len])?;
1060        /// }
1061        /// ```
1062        ///
1063        pub fn new<P, F>(netif: T, post_attach_cfg: P, tx: F) -> Result<Self, EspError>
1064        where
1065            P: FnMut(&mut EspNetif) -> Result<(), EspError> + Send + 'static,
1066            F: FnMut(&[u8]) -> Result<(), EspError> + Send + 'static,
1067        {
1068            Self::new_nonstatic(netif, post_attach_cfg, tx)
1069        }
1070    }
1071
1072    impl<'d, T> EspNetifDriver<'d, T>
1073    where
1074        T: BorrowMut<EspNetif>,
1075    {
1076        /// Create a new netif driver around the provided `EspNetif` instance.
1077        ///
1078        /// The driver transport is represented by:
1079        /// - The `tx` callback that the driver would call when it wants to ingest a packet
1080        ///   into the underlying transport
1081        /// - `EsNetifDriver::rx`, which should be called by the transport when a new packet
1082        ///   has arrived that has to be ingested in the driver
1083        ///   
1084        /// The transport can be anything, but with - say - PPP netif - it would typically be UART,
1085        /// and the `tx` callback implementation is simply expected to write the PPP packet into UART.
1086        ///
1087        /// Arguments:
1088        /// - `netif` is the `EspNetif` instance that the driver will manage
1089        /// - `got_ip_event_id` and `lost_ip_event_id` are the event IDs that the driver
1090        ///   will listen to so that it can connect/disconnect the netif upon receival
1091        ///   / loss of IP
1092        /// - `post_attach_cfg` is a netif-specific configuration that will be executed
1093        ///   after the netif is attached. For example, for a PPP netif, the post attach
1094        ///   configuration might want to invoke `EspNetif::set_ppp_conf`.
1095        /// - `tx` is the callback that the driver will call when it wants to ingest a packet
1096        ///   into the underlying transport
1097        ///
1098        /// Example:
1099        /// ```ignore
1100        /// let (uart_rx, uart_tx) = uart.into_split();
1101        ///
1102        /// let mut driver = EspNetifDriver::new(
1103        ///     EspNetif::new(NetifStack::Ppp)?,
1104        ///     |netif| netif.set_ppp_conf(&PppConfiguration {
1105        ///         phase_events_enabled: false,
1106        ///         ..Default::default()
1107        ///     }),
1108        ///     move |data| uart_tx.write_all(data),
1109        /// )?;
1110        ///
1111        /// loop {
1112        ///     let mut buffer = [0; 128];
1113        ///     let len = uart_rx.read(&mut buffer)?;
1114        ///     driver.rx(&buffer[..len])?;
1115        /// }
1116        /// ```
1117        ///
1118        /// # Safety
1119        ///
1120        /// This method - in contrast to method `new` - allows the user to pass
1121        /// non-static callbacks/closures. This enables users to borrow
1122        /// - in the closure - variables that live on the stack - or more generally - in the same
1123        ///   scope where the service is created.
1124        ///
1125        /// HOWEVER: care should be taken NOT to call `core::mem::forget()` on the service,
1126        /// as that would immediately lead to an UB (crash).
1127        /// Also note that forgetting the service might happen with `Rc` and `Arc`
1128        /// when circular references are introduced: <https://github.com/rust-lang/rust/issues/24456>
1129        ///
1130        /// The reason is that the closure is actually sent to a hidden ESP IDF thread.
1131        /// This means that if the service is forgotten, Rust is free to e.g. unwind the stack
1132        /// and the closure now owned by this other thread will end up with references to variables that no longer exist.
1133        ///
1134        /// The destructor of the service takes care - prior to the service being dropped and e.g.
1135        /// the stack being unwind - to remove the closure from the hidden thread and destroy it.
1136        /// Unfortunately, when the service is forgotten, the un-subscription does not happen
1137        /// and invalid references are left dangling.
1138        ///
1139        /// This "local borrowing" will only be possible to express in a safe way once/if `!Leak` types
1140        /// are introduced to Rust (i.e. the impossibility to "forget" a type and thus not call its destructor).
1141        pub fn new_nonstatic<P, F>(netif: T, post_attach_cfg: P, tx: F) -> Result<Self, EspError>
1142        where
1143            P: FnMut(&mut EspNetif) -> Result<(), EspError> + Send + 'd,
1144            F: FnMut(&[u8]) -> Result<(), EspError> + Send + 'd,
1145        {
1146            let mut inner = alloc::boxed::Box::new(EspNetifDriverInner {
1147                base: esp_netif_driver_base_t {
1148                    netif: netif.borrow().handle(),
1149                    post_attach: Some(EspNetifDriverInner::<T>::raw_post_attach),
1150                },
1151                netif,
1152                post_attach_cfg: alloc::boxed::Box::new(post_attach_cfg),
1153                tx: alloc::boxed::Box::new(tx),
1154            });
1155
1156            let inner_ptr = inner.as_mut() as *mut _ as *mut core::ffi::c_void;
1157
1158            if let Some(got_ip_event_id) = inner.netif.borrow()._got_ip_event_id {
1159                esp!(unsafe {
1160                    esp_event_handler_register(
1161                        IP_EVENT,
1162                        got_ip_event_id.get() as _,
1163                        Some(esp_netif_action_connected),
1164                        inner.netif.borrow().handle() as *mut core::ffi::c_void,
1165                    )
1166                })?;
1167            }
1168
1169            if let Some(lost_ip_event_id) = inner.netif.borrow()._lost_ip_event_id {
1170                esp!(unsafe {
1171                    esp_event_handler_register(
1172                        IP_EVENT,
1173                        lost_ip_event_id.get() as _,
1174                        Some(esp_netif_action_disconnected),
1175                        inner.netif.borrow().handle() as *mut core::ffi::c_void,
1176                    )
1177                })?;
1178            }
1179
1180            esp!(unsafe { esp_netif_attach(inner.netif.borrow().handle(), inner_ptr) })?;
1181
1182            Ok(Self {
1183                inner,
1184                started: false,
1185            })
1186        }
1187
1188        /// Ingest a packet into the driver
1189        ///
1190        /// The packet can arrive from anywhere, but with say - a PPP netif -
1191        /// it would be a PPP packet arriving typically from UART, by reading from it.
1192        pub fn rx(&self, data: &[u8]) -> Result<(), EspError> {
1193            esp!(unsafe {
1194                esp_netif_receive(
1195                    self.inner.netif.borrow().handle(),
1196                    data.as_ptr() as *mut core::ffi::c_void,
1197                    data.len() as _,
1198                    core::ptr::null_mut(),
1199                )
1200            })
1201        }
1202
1203        /// Start the driver
1204        pub fn start(&mut self) -> Result<(), EspError> {
1205            if self.started {
1206                return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
1207            }
1208
1209            unsafe {
1210                esp_netif_action_start(
1211                    self.inner.netif.borrow().handle() as *mut core::ffi::c_void,
1212                    core::ptr::null_mut(),
1213                    0,
1214                    core::ptr::null_mut(),
1215                );
1216            }
1217
1218            Ok(())
1219        }
1220
1221        /// Stop the driver
1222        pub fn stop(&mut self) -> Result<(), EspError> {
1223            if !self.started {
1224                return Err(EspError::from_infallible::<ESP_ERR_INVALID_STATE>());
1225            }
1226
1227            unsafe {
1228                esp_netif_action_stop(
1229                    self.inner.netif.borrow().handle() as *mut core::ffi::c_void,
1230                    core::ptr::null_mut(),
1231                    0,
1232                    core::ptr::null_mut(),
1233                );
1234            }
1235
1236            Ok(())
1237        }
1238
1239        /// Check if the driver is started
1240        pub fn is_started(&self) -> Result<bool, EspError> {
1241            Ok(self.started)
1242        }
1243
1244        /// Get a reference to the underlying `EspNetif` instance
1245        pub fn netif(&self) -> &EspNetif {
1246            self.inner.netif.borrow()
1247        }
1248
1249        /// Get a mutable reference to the underlying `EspNetif` instance
1250        pub fn netif_mut(&mut self) -> &mut EspNetif {
1251            self.inner.netif.borrow_mut()
1252        }
1253    }
1254
1255    impl<T> Drop for EspNetifDriver<'_, T>
1256    where
1257        T: BorrowMut<EspNetif>,
1258    {
1259        fn drop(&mut self) {
1260            let _ = self.stop();
1261
1262            if let Some(got_ip_event_id) = self.inner.netif.borrow()._got_ip_event_id {
1263                esp!(unsafe {
1264                    esp_event_handler_unregister(
1265                        IP_EVENT,
1266                        got_ip_event_id.get() as _,
1267                        Some(esp_netif_action_connected),
1268                    )
1269                })
1270                .unwrap();
1271            }
1272
1273            if let Some(lost_ip_event_id) = self.inner.netif.borrow()._lost_ip_event_id {
1274                esp!(unsafe {
1275                    esp_event_handler_unregister(
1276                        IP_EVENT,
1277                        lost_ip_event_id.get() as _,
1278                        Some(esp_netif_action_disconnected),
1279                    )
1280                })
1281                .unwrap();
1282            }
1283        }
1284    }
1285
1286    #[repr(C)]
1287    struct EspNetifDriverInner<'d, T>
1288    where
1289        T: BorrowMut<EspNetif>,
1290    {
1291        base: esp_netif_driver_base_t,
1292        netif: T,
1293        #[allow(clippy::type_complexity)]
1294        tx: alloc::boxed::Box<dyn FnMut(&[u8]) -> Result<(), EspError> + Send + 'd>,
1295        #[allow(clippy::type_complexity)]
1296        post_attach_cfg:
1297            alloc::boxed::Box<dyn FnMut(&mut EspNetif) -> Result<(), EspError> + Send + 'd>,
1298    }
1299
1300    impl<T> EspNetifDriverInner<'_, T>
1301    where
1302        T: BorrowMut<EspNetif>,
1303    {
1304        fn post_attach(&mut self, netif_handle: *mut esp_netif_obj) -> Result<(), EspError> {
1305            let driver_ifconfig = esp_netif_driver_ifconfig_t {
1306                transmit: Some(Self::raw_tx),
1307                handle: self as *mut _ as *mut core::ffi::c_void,
1308                ..Default::default()
1309            };
1310
1311            debug!("Post attach ifconfig: {driver_ifconfig:?}");
1312
1313            // d->base.netif = esp_netif; TODO: This is weird; the netif in base is already set on constructor?
1314
1315            esp!(unsafe { esp_netif_set_driver_config(netif_handle, &driver_ifconfig) })?;
1316
1317            (self.post_attach_cfg)(self.netif.borrow_mut())?;
1318
1319            Ok(())
1320        }
1321
1322        fn tx(&mut self, data: &[u8]) -> Result<(), EspError> {
1323            (self.tx)(data)
1324        }
1325
1326        unsafe extern "C" fn raw_tx(
1327            h: *mut core::ffi::c_void,
1328            buffer: *mut core::ffi::c_void,
1329            len: usize,
1330        ) -> i32 {
1331            let this = unsafe { (h as *mut Self).as_mut() }.unwrap();
1332            let data = core::slice::from_raw_parts(buffer as *mut u8, len);
1333
1334            #[allow(clippy::let_and_return)]
1335            let result = match this.tx(data) {
1336                Ok(_) => ESP_OK,
1337                Err(e) => e.code(),
1338            };
1339
1340            // TODO: Might not be necessary, but if I remember correctly, the Netif API
1341            // wanted that _we_ free the buffer; in any case needs to be compared with the C ESP Modem code
1342            // free(buffer);
1343
1344            result
1345        }
1346
1347        unsafe extern "C" fn raw_post_attach(
1348            netif: *mut esp_netif_obj,
1349            args: *mut core::ffi::c_void,
1350        ) -> i32 {
1351            let this = { (args as *mut Self).as_mut() }.unwrap();
1352            match this.post_attach(netif) {
1353                Ok(_) => ESP_OK,
1354                Err(e) => e.code(),
1355            }
1356        }
1357    }
1358}
1359
1360#[cfg(esp_idf_lwip_ppp_support)]
1361mod ppp {
1362    use core::ffi::{self, CStr};
1363
1364    use enumset::{EnumSet, EnumSetType};
1365
1366    use crate::eventloop::{EspEventDeserializer, EspEventSource};
1367    use crate::handle::RawHandle;
1368    use crate::sys::*;
1369
1370    /// Represents a PPP event on the system event loop
1371    #[derive(Copy, Clone, Debug)]
1372    pub enum PppEvent {
1373        /// No error
1374        NoError,
1375        /// Invalid parameter
1376        ParameterError,
1377        /// Unable to open PPP session
1378        OpenError,
1379        /// Invalid I/O device for PPP
1380        DeviceError,
1381        /// Unable to allocate resources
1382        AllocError,
1383        /// User interrupt
1384        UserError,
1385        /// Connection lost
1386        DisconnectError,
1387        /// Failed authentication challenge
1388        AuthFailError,
1389        /// Failed to meet protocol
1390        ProtocolError,
1391        /// Connection timeout
1392        PeerDeadError,
1393        /// Idle Timeout
1394        IdleTimeoutError,
1395        /// Max connect time reached
1396        MaxConnectTimeoutError,
1397        /// Loopback detected
1398        LoopbackError,
1399        PhaseDead,
1400        PhaseMaster,
1401        PhaseHoldoff,
1402        PhaseInitialize,
1403        PhaseSerialConnection,
1404        PhaseDormant,
1405        PhaseEstablish,
1406        PhaseAuthenticate,
1407        PhaseCallback,
1408        PhaseNetwork,
1409        PhaseRunning,
1410        PhaseTerminate,
1411        PhaseDisconnect,
1412        PhaseFailed,
1413
1414        /// An event ID not recognised by this version of the library was received.
1415        ///
1416        /// This variant is produced instead of panicking when an unknown event ID
1417        /// arrives, allowing applications to remain forward-compatible with
1418        /// ESP-IDF versions that introduce new PPP events.
1419        Other(i32),
1420    }
1421
1422    unsafe impl EspEventSource for PppEvent {
1423        fn source() -> Option<&'static core::ffi::CStr> {
1424            Some(unsafe { ffi::CStr::from_ptr(NETIF_PPP_STATUS) })
1425        }
1426    }
1427
1428    impl EspEventDeserializer for PppEvent {
1429        type Data<'a> = PppEvent;
1430
1431        #[allow(non_upper_case_globals, non_snake_case)]
1432        fn deserialize<'a>(data: &crate::eventloop::EspEvent<'a>) -> Self::Data<'a> {
1433            let event_id = data.event_id as u32;
1434
1435            match event_id {
1436                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORNONE => PppEvent::NoError,
1437                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORPARAM => PppEvent::ParameterError,
1438                esp_netif_ppp_status_event_t_NETIF_PPP_ERROROPEN => PppEvent::OpenError,
1439                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORDEVICE => PppEvent::DeviceError,
1440                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORALLOC => PppEvent::AllocError,
1441                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORUSER => PppEvent::UserError,
1442                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORCONNECT => PppEvent::DisconnectError,
1443                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORAUTHFAIL => PppEvent::AuthFailError,
1444                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORPROTOCOL => PppEvent::ProtocolError,
1445                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORPEERDEAD => PppEvent::PeerDeadError,
1446                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORIDLETIMEOUT => {
1447                    PppEvent::IdleTimeoutError
1448                }
1449                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORCONNECTTIME => {
1450                    PppEvent::MaxConnectTimeoutError
1451                }
1452                esp_netif_ppp_status_event_t_NETIF_PPP_ERRORLOOPBACK => PppEvent::LoopbackError,
1453                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_DEAD => PppEvent::PhaseDead,
1454                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_MASTER => PppEvent::PhaseMaster,
1455                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_HOLDOFF => PppEvent::PhaseHoldoff,
1456                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_INITIALIZE => {
1457                    PppEvent::PhaseInitialize
1458                }
1459                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_SERIALCONN => {
1460                    PppEvent::PhaseSerialConnection
1461                }
1462                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_DORMANT => PppEvent::PhaseDormant,
1463                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_ESTABLISH => PppEvent::PhaseEstablish,
1464                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_AUTHENTICATE => {
1465                    PppEvent::PhaseAuthenticate
1466                }
1467                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_CALLBACK => PppEvent::PhaseCallback,
1468                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_NETWORK => PppEvent::PhaseNetwork,
1469                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_RUNNING => PppEvent::PhaseRunning,
1470                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_TERMINATE => PppEvent::PhaseTerminate,
1471                esp_netif_ppp_status_event_t_NETIF_PPP_PHASE_DISCONNECT => {
1472                    PppEvent::PhaseDisconnect
1473                }
1474                esp_netif_ppp_status_event_t_NETIF_PPP_CONNECT_FAILED => PppEvent::PhaseFailed,
1475                _ => {
1476                    ::log::warn!("PppEvent: unknown event ID {event_id}, ignoring");
1477                    PppEvent::Other(event_id as i32)
1478                }
1479            }
1480        }
1481    }
1482
1483    #[derive(Clone, Debug, Eq, PartialEq, Hash)]
1484    pub struct PppConfiguration {
1485        /// Enables events coming from PPP PHASE change
1486        pub phase_events_enabled: bool,
1487        /// Enables events from main PPP state machine producing errors
1488        pub error_events_enabled: bool,
1489        /// Allows to temporarily disable LCP keepalive (runtime, if enabled compile time)
1490        /// When LCP echo is enabled in menuconfig, this option can be used to override the setting,
1491        /// if we have to relax LCP keepalive criteria during runtime operation, for example before OTA update.
1492        /// The current session must be closed, settings will be applied upon connecting.
1493        #[cfg(esp_idf_lwip_enable_lcp_echo)]
1494        pub lcp_echo_disabled: bool,
1495        /// Set our preferred address, typically used when we're the PPP server
1496        #[cfg(esp_idf_lwip_ppp_server_support)]
1497        our_ip4_addr: core::net::Ipv4Addr,
1498        /// Set our preferred address, typically used when we're the PPP server        
1499        #[cfg(esp_idf_lwip_ppp_server_support)]
1500        their_ip4_addr: core::net::Ipv4Addr,
1501    }
1502
1503    impl PppConfiguration {
1504        pub const fn new() -> Self {
1505            Self {
1506                phase_events_enabled: true,
1507                error_events_enabled: true,
1508                #[cfg(esp_idf_lwip_enable_lcp_echo)]
1509                lcp_echo_disabled: false,
1510                #[cfg(esp_idf_lwip_ppp_server_support)]
1511                our_ip4_addr: core::net::Ipv4Addr::UNSPECIFIED,
1512                #[cfg(esp_idf_lwip_ppp_server_support)]
1513                their_ip4_addr: core::net::Ipv4Addr::UNSPECIFIED,
1514            }
1515        }
1516    }
1517
1518    impl Default for PppConfiguration {
1519        fn default() -> Self {
1520            Self::new()
1521        }
1522    }
1523
1524    impl From<esp_netif_ppp_config_t> for PppConfiguration {
1525        fn from(cfg: esp_netif_ppp_config_t) -> Self {
1526            Self {
1527                phase_events_enabled: cfg.ppp_phase_event_enabled,
1528                error_events_enabled: cfg.ppp_error_event_enabled,
1529                #[cfg(esp_idf_lwip_enable_lcp_echo)]
1530                lcp_echo_disabled: cfg.lcp_echo_disabled,
1531                #[cfg(esp_idf_lwip_ppp_server_support)]
1532                our_ip4_addr: Newtype::<core::net::Ipv4Addr>::from(cfg.our_ip4_addr),
1533                #[cfg(esp_idf_lwip_ppp_server_support)]
1534                their_ip4_addr: Newtype::<core::net::Ipv4Addr>::from(cfg.their_ip4_addr),
1535            }
1536        }
1537    }
1538
1539    impl From<&PppConfiguration> for esp_netif_ppp_config_t {
1540        fn from(cfg: &PppConfiguration) -> Self {
1541            Self {
1542                ppp_phase_event_enabled: cfg.phase_events_enabled,
1543                ppp_error_event_enabled: cfg.error_events_enabled,
1544                #[cfg(esp_idf_lwip_enable_lcp_echo)]
1545                lcp_echo_disabled: cfg.lcp_echo_disabled,
1546                #[cfg(esp_idf_lwip_ppp_server_support)]
1547                our_ip4_addr: Newtype::<esp_ip4_addr_t>::from(cfg.our_ip4_addr),
1548                #[cfg(esp_idf_lwip_ppp_server_support)]
1549                their_ip4_addr: Newtype::<esp_ip4_addr_t>::from(cfg.their_ip4_addr),
1550            }
1551        }
1552    }
1553
1554    #[derive(Debug, EnumSetType)]
1555    #[enumset(repr = "u32")]
1556    pub enum PppAuthentication {
1557        Pap,
1558        Chap,
1559        MsChap,
1560        MsChapV2,
1561        Eap,
1562    }
1563
1564    /// PPP-specific configuration of a Netif
1565    impl super::EspNetif {
1566        /// Get the current PPP configuration
1567        #[cfg(not(esp_idf_version_major = "4"))]
1568        pub fn get_ppp_conf(&self) -> Result<PppConfiguration, EspError> {
1569            let mut ppp_config = Default::default();
1570
1571            esp!(unsafe { esp_netif_ppp_get_params(self.handle(), &mut ppp_config) })?;
1572
1573            Ok(ppp_config.into())
1574        }
1575
1576        /// Set the PPP configuration
1577        pub fn set_ppp_conf(&mut self, conf: &PppConfiguration) -> Result<(), EspError> {
1578            let ppp_config: esp_netif_ppp_config_t = conf.into();
1579
1580            esp!(unsafe { esp_netif_ppp_set_params(self.handle(), &ppp_config) })
1581        }
1582
1583        /// Set the PPP authentication
1584        /// Arguments:
1585        /// - `auth` is the set of all authentication methods to allow; if empty, no authentication will be used
1586        /// - `username` is the username to use for authentication; only relevant when the `auth` enumset is non-empty
1587        /// - `password` is the password to use for authentication; only relevant when the `auth` enumset is non-empty
1588        pub fn set_ppp_auth(
1589            &mut self,
1590            auth: EnumSet<PppAuthentication>,
1591            username: &CStr,
1592            password: &CStr,
1593        ) -> Result<(), EspError> {
1594            esp!(unsafe {
1595                esp_netif_ppp_set_auth(
1596                    self.handle(),
1597                    auth.as_repr(),
1598                    username.as_ptr() as _,
1599                    password.as_ptr() as _,
1600                )
1601            })
1602        }
1603    }
1604}
1605
1606pub mod asynch {
1607    use crate::sys::EspError;
1608
1609    pub trait NetifStatus {
1610        async fn is_up(&self) -> Result<bool, EspError>;
1611    }
1612
1613    impl<T> NetifStatus for &T
1614    where
1615        T: NetifStatus,
1616    {
1617        async fn is_up(&self) -> Result<bool, EspError> {
1618            (**self).is_up().await
1619        }
1620    }
1621
1622    impl<T> NetifStatus for &mut T
1623    where
1624        T: NetifStatus,
1625    {
1626        async fn is_up(&self) -> Result<bool, EspError> {
1627            (**self).is_up().await
1628        }
1629    }
1630
1631    impl NetifStatus for super::EspNetif {
1632        async fn is_up(&self) -> Result<bool, EspError> {
1633            super::EspNetif::is_up(self)
1634        }
1635    }
1636
1637    #[cfg(all(feature = "alloc", esp_idf_comp_esp_timer_enabled))]
1638    impl<T> NetifStatus for super::AsyncNetif<T>
1639    where
1640        T: super::NetifStatus,
1641    {
1642        async fn is_up(&self) -> Result<bool, EspError> {
1643            super::AsyncNetif::is_up(self)
1644        }
1645    }
1646}