Skip to content

Commit 82d8f9f

Browse files
committed
Merge branch 'feat/esp_netif_status_event' into 'master'
[esp_netif]: Add support for netif status events Closes IDF-13357 See merge request espressif/esp-idf!42143
2 parents 7ab29a8 + 48af385 commit 82d8f9f

File tree

5 files changed

+140
-15
lines changed

5 files changed

+140
-15
lines changed

components/esp_netif/include/esp_netif_types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ typedef enum {
109109
IP_EVENT_PPP_GOT_IP, /*!< PPP interface got IP */
110110
IP_EVENT_PPP_LOST_IP, /*!< PPP interface lost IP */
111111
IP_EVENT_TX_RX, /*!< transmitting/receiving data packet */
112+
IP_EVENT_NETIF_UP, /*!< unified netif status: interface became up */
113+
IP_EVENT_NETIF_DOWN, /*!< unified netif status: interface went down */
112114
} ip_event_t;
113115

114116
/** @brief IP event base declaration */
@@ -198,6 +200,11 @@ typedef struct {
198200
} ip_event_tx_rx_t;
199201
#endif
200202

203+
/** Event structure for IP_EVENT_LINK_UP/DOWN and IP_EVENT_NETIF_UP/DOWN */
204+
typedef struct {
205+
esp_netif_t *esp_netif; /*!< Pointer to the associated netif handle */
206+
} ip_event_netif_status_t;
207+
201208
typedef enum esp_netif_flags {
202209
ESP_NETIF_DHCP_CLIENT = 1 << 0,
203210
ESP_NETIF_DHCP_SERVER = 1 << 1,

components/esp_netif/lwip/esp_netif_lwip.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,35 @@ static void netif_unset_mldv6_flag(esp_netif_t *netif);
137137

138138
static esp_err_t esp_netif_destroy_api(esp_netif_api_msg_t *msg);
139139

140+
static inline esp_netif_t* lwip_get_esp_netif(struct netif *netif)
141+
{
142+
#if LWIP_ESP_NETIF_DATA
143+
return (esp_netif_t*)netif_get_client_data(netif, lwip_netif_client_id);
144+
#else
145+
return (esp_netif_t*)netif->state;
146+
#endif
147+
}
148+
140149
static void netif_callback_fn(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args)
141150
{
142151
#if LWIP_IPV4
143152
if (reason & DHCP_CB_CHANGE) {
144153
esp_netif_internal_dhcpc_cb(netif);
145154
}
146155
#endif /* LWIP_IPV4 */
156+
// Normalize link/admin status: post only NETIF_UP/NETIF_DOWN when the effective state flips
157+
if ((reason & (LWIP_NSC_LINK_CHANGED | LWIP_NSC_STATUS_CHANGED)) != 0) {
158+
esp_netif_t *esp_netif = lwip_get_esp_netif(netif);
159+
if (esp_netif) {
160+
bool now_up = esp_netif_is_netif_up(esp_netif);
161+
if (now_up != esp_netif->last_status_up) {
162+
ip_event_netif_status_t evt = { .esp_netif = esp_netif };
163+
esp_event_post(IP_EVENT, now_up ? IP_EVENT_NETIF_UP : IP_EVENT_NETIF_DOWN,
164+
&evt, sizeof(evt), 0);
165+
esp_netif->last_status_up = now_up;
166+
}
167+
}
168+
}
147169
#if LWIP_IPV6
148170
if ((reason & LWIP_NSC_IPV6_ADDR_STATE_CHANGED) && (args != NULL)) {
149171
s8_t addr_idx = args->ipv6_addr_state_changed.addr_index;
@@ -395,15 +417,6 @@ esp_netif_t *esp_netif_get_default_netif(void)
395417
return s_last_default_esp_netif;
396418
}
397419

398-
static inline esp_netif_t* lwip_get_esp_netif(struct netif *netif)
399-
{
400-
#if LWIP_ESP_NETIF_DATA
401-
return (esp_netif_t*)netif_get_client_data(netif, lwip_netif_client_id);
402-
#else
403-
return (esp_netif_t*)netif->state;
404-
#endif
405-
}
406-
407420
static inline void lwip_set_esp_netif(struct netif *netif, esp_netif_t* esp_netif)
408421
{
409422
#if LWIP_ESP_NETIF_DATA

components/esp_netif/lwip/esp_netif_lwip_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ struct esp_netif_obj {
9999
// event translation
100100
ip_event_t get_ip_event;
101101
ip_event_t lost_ip_event;
102+
bool last_status_up; // last effective up/down state for unified status event
102103
#ifdef CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC
103104
bool tx_rx_events_enabled;
104105
#endif

components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Unlicense OR CC0-1.0
55
*/
@@ -18,6 +18,8 @@
1818
#include "memory_checks.h"
1919
#include "lwip/netif.h"
2020
#include "esp_netif_test.h"
21+
#include "esp_event.h"
22+
#include "freertos/semphr.h"
2123

2224
TEST_GROUP(esp_netif);
2325

@@ -147,6 +149,87 @@ TEST(esp_netif, find_netifs)
147149
}
148150
}
149151

152+
static esp_err_t dummy_transmit(void* hd, void *buf, size_t length)
153+
{
154+
return ESP_OK;
155+
}
156+
157+
static SemaphoreHandle_t s_netif_up_sem;
158+
static SemaphoreHandle_t s_netif_down_sem;
159+
static volatile int s_netif_up_count;
160+
static volatile int s_netif_down_count;
161+
162+
static void netif_status_evt_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
163+
{
164+
(void)arg; (void)base; (void)data;
165+
if (id == IP_EVENT_NETIF_UP) {
166+
s_netif_up_count++;
167+
if (s_netif_up_sem) {
168+
xSemaphoreGive(s_netif_up_sem);
169+
}
170+
} else if (id == IP_EVENT_NETIF_DOWN) {
171+
s_netif_down_count++;
172+
if (s_netif_down_sem) {
173+
xSemaphoreGive(s_netif_down_sem);
174+
}
175+
}
176+
}
177+
178+
TEST(esp_netif, unified_netif_status_event)
179+
{
180+
test_case_uses_tcpip();
181+
TEST_ESP_OK(esp_event_loop_create_default());
182+
183+
s_netif_up_sem = xSemaphoreCreateBinary();
184+
s_netif_down_sem = xSemaphoreCreateBinary();
185+
TEST_ASSERT_NOT_NULL(s_netif_up_sem);
186+
TEST_ASSERT_NOT_NULL(s_netif_down_sem);
187+
s_netif_up_count = 0;
188+
s_netif_down_count = 0;
189+
190+
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_NETIF_UP, &netif_status_evt_handler, NULL));
191+
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_NETIF_DOWN, &netif_status_evt_handler, NULL));
192+
193+
// Create a simple netif (no real driver needed)
194+
esp_netif_driver_ifconfig_t driver_config = { .handle = (void*)1, .transmit = dummy_transmit };
195+
esp_netif_inherent_config_t base_netif_config = { .if_key = "if_status", .if_desc = "if_status" };
196+
esp_netif_config_t cfg = { .base = &base_netif_config, .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA, .driver = &driver_config };
197+
esp_netif_t *netif = esp_netif_new(&cfg);
198+
TEST_ASSERT_NOT_NULL(netif);
199+
200+
// Bring interface up (should emit exactly one NETIF_UP)
201+
esp_netif_action_start(netif, NULL, 0, NULL);
202+
esp_netif_action_connected(netif, NULL, 0, NULL);
203+
TEST_ASSERT_EQUAL(pdTRUE, xQueueSemaphoreTake(s_netif_up_sem, pdMS_TO_TICKS(1000)));
204+
vTaskDelay(pdMS_TO_TICKS(50));
205+
TEST_ASSERT_EQUAL(1, s_netif_up_count);
206+
TEST_ASSERT_EQUAL(0, s_netif_down_count);
207+
208+
// Bring interface down (should emit exactly one NETIF_DOWN)
209+
esp_netif_action_disconnected(netif, NULL, 0, NULL);
210+
TEST_ASSERT_EQUAL(pdTRUE, xQueueSemaphoreTake(s_netif_down_sem, pdMS_TO_TICKS(1000)));
211+
vTaskDelay(pdMS_TO_TICKS(50));
212+
TEST_ASSERT_EQUAL(1, s_netif_down_count);
213+
TEST_ASSERT_EQUAL(1, s_netif_up_count);
214+
215+
// Bring up again (should increment up count to 2)
216+
esp_netif_action_connected(netif, NULL, 0, NULL);
217+
TEST_ASSERT_EQUAL(pdTRUE, xQueueSemaphoreTake(s_netif_up_sem, pdMS_TO_TICKS(1000)));
218+
vTaskDelay(pdMS_TO_TICKS(50));
219+
TEST_ASSERT_EQUAL(2, s_netif_up_count);
220+
TEST_ASSERT_EQUAL(1, s_netif_down_count);
221+
222+
// Cleanup
223+
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_NETIF_UP, &netif_status_evt_handler));
224+
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_NETIF_DOWN, &netif_status_evt_handler));
225+
vSemaphoreDelete(s_netif_up_sem);
226+
vSemaphoreDelete(s_netif_down_sem);
227+
s_netif_up_sem = s_netif_down_sem = NULL;
228+
229+
esp_netif_destroy(netif);
230+
TEST_ESP_OK(esp_event_loop_delete_default());
231+
}
232+
150233
#ifdef CONFIG_ESP_WIFI_ENABLED
151234
/*
152235
* This test creates a default WiFi station and checks all possible transitions
@@ -438,11 +521,6 @@ TEST(esp_netif, get_set_hostname)
438521
}
439522
#endif // CONFIG_ESP_WIFI_ENABLED
440523

441-
static esp_err_t dummy_transmit(void* hd, void *buf, size_t length)
442-
{
443-
return ESP_OK;
444-
}
445-
446524
/*
447525
* This test validates the route priority of multiple netifs. It checks that the default route (default netif)
448526
* is set correctly for the netifs according to their `route_prio` value and `link_up` state.
@@ -610,6 +688,7 @@ TEST_GROUP_RUNNER(esp_netif)
610688
#endif
611689
RUN_TEST_CASE(esp_netif, route_priority)
612690
RUN_TEST_CASE(esp_netif, set_get_dnsserver)
691+
RUN_TEST_CASE(esp_netif, unified_netif_status_event)
613692
}
614693

615694
void app_main(void)

docs/en/api-reference/network/esp_netif_programming.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,31 @@ The event data structure, :cpp:class:`ip_event_tx_rx_t`, contains the following
338338
- :cpp:member:`ip_event_tx_rx_t::len`: Length of the data frame.
339339
- :cpp:member:`ip_event_tx_rx_t::esp_netif`: The network interface on which the packet was sent or received.
340340

341+
IP Events: Netif Status (Unified)
342+
---------------------------------
343+
344+
ESP-NETIF emits unified status events when an interface becomes usable for L3 traffic or goes down. These are derived from the lwIP extended netif callbacks and posted on ``IP_EVENT``:
345+
346+
- ``IP_EVENT_NETIF_UP`` / ``IP_EVENT_NETIF_DOWN``
347+
348+
ESP-IDF normalizes link and administrative state changes into these two events (including PPP). Subscribe as follows:
349+
350+
.. code-block:: c
351+
352+
static void netif_status_handler(void *arg, esp_event_base_t base, int32_t id, void *data)
353+
{
354+
const ip_event_netif_status_t *evt = (const ip_event_netif_status_t *)data;
355+
ESP_LOGI("netif", "status %s on %s", (id == IP_EVENT_NETIF_UP) ? "UP" : "DOWN", esp_netif_get_desc(evt->esp_netif));
356+
}
357+
358+
esp_event_handler_register(IP_EVENT, IP_EVENT_NETIF_UP, &netif_status_handler, NULL);
359+
esp_event_handler_register(IP_EVENT, IP_EVENT_NETIF_DOWN, &netif_status_handler, NULL);
360+
361+
Event Data Structure
362+
^^^^^^^^^^^^^^^^^^^^
363+
364+
The event data structure is :cpp:type:`ip_event_netif_status_t`, which contains the ``esp_netif`` handle of the interface that changed state.
365+
341366

342367
.. _esp_netif_api_reference:
343368

0 commit comments

Comments
 (0)