Merge branch 'feat/call_meshcop_mdns_publish_in_idf_v5.2' into 'release/v5.2'

Handle MeshCoP mDNS service in state change callback, update OpenThread upstream (v5.2)

See merge request espressif/esp-idf!40085
This commit is contained in:
Shu Chen
2025-07-01 02:55:13 +00:00
14 changed files with 243 additions and 77 deletions

View File

@@ -130,6 +130,14 @@ menu "OpenThread"
default 12390 default 12390
help help
Configure the port number of TREL service. Configure the port number of TREL service.
config OPENTHREAD_TREL_BUFFER_SIZE
int "The receive buffer size of openthread trel"
depends on OPENTHREAD_RADIO_TREL
range 10 255
default 50
help
Configure the receive buffer size of TREL service.
endmenu endmenu
menu "Thread 15.4 Radio Link" menu "Thread 15.4 Radio Link"

View File

@@ -76,6 +76,14 @@ esp_netif_t *esp_openthread_get_backbone_netif(void);
*/ */
esp_err_t esp_openthread_set_meshcop_instance_name(const char *instance_name); esp_err_t esp_openthread_set_meshcop_instance_name(const char *instance_name);
/**
* @brief Gets the meshcop(e) instance name.
*
* @return The instance name.
*
*/
const char* esp_openthread_get_meshcop_instance_name(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -0,0 +1,41 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_openthread.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Publishes the OpenThread meshcop service in mDNS
*
* @param[in] instance_name Instance name of meshcop mDNS service
*
* @return
* - ESP_OK success
* - ESP_ERR_NO_MEM memory error
* - ESP_FAIL failed to add service
*
*/
esp_err_t esp_openthread_publish_meshcop_mdns(const char *instance_name);
/**
* @brief Removes the OpenThread meshcop service in mDNS
*
* @return
* - ESP_OK success
* - ESP_ERR_NO_MEM memory error
* - ESP_FAIL failed to remove service
*
*/
esp_err_t esp_openthread_remove_meshcop_mdns(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdatomic.h>
typedef struct {
uint8_t head;
uint8_t tail;
atomic_uint_fast8_t used;
} esp_openthread_circular_queue_info_t;

View File

@@ -84,6 +84,27 @@ void esp_openthread_set_coex_config(esp_ieee802154_coex_config_t config);
* *
*/ */
esp_ieee802154_coex_config_t esp_openthread_get_coex_config(void); esp_ieee802154_coex_config_t esp_openthread_get_coex_config(void);
/**
* @brief This function updates the TREL fds and timeouts to the main loop.
*
* @param[inout] mainloop The main loop context.
*
*/
void esp_openthread_trel_update(esp_openthread_mainloop_context_t *mainloop);
/**
* @brief This function performs the OpenThread TREL process.
*
* @param[in] instance The OpenThread instance.
* @param[in] mainloop The main loop context.
*
* @return
* - ESP_OK on success
* - ESP_FAIL on failure
*
*/
esp_err_t esp_openthread_trel_process(otInstance *aInstance, const esp_openthread_mainloop_context_t *mainloop);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,6 +10,11 @@
#include <esp_openthread.h> #include <esp_openthread.h>
#include <esp_openthread_types.h> #include <esp_openthread_types.h>
#define ESP_OPENTHREAD_BORDER_ROUTER_FLAG_OF_INTEREST \
(OT_CHANGED_THREAD_ROLE | OT_CHANGED_THREAD_EXT_PANID | OT_CHANGED_THREAD_NETWORK_NAME | \
OT_CHANGED_ACTIVE_DATASET | OT_CHANGED_THREAD_PARTITION_ID | OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE | \
OT_CHANGED_PSKC)
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@@ -5,4 +5,4 @@ supplier: 'Organization: Espressif Systems (Shanghai) CO LTD'
originator: 'Organization: Google LLC' originator: 'Organization: Google LLC'
description: OpenThread released by Google is an open-source implementation of the Thread networking description: OpenThread released by Google is an open-source implementation of the Thread networking
url: https://github.com/espressif/openthread url: https://github.com/espressif/openthread
hash: ec2b0d487356d2955346457a6515201039140037 hash: b945928d722177cd9caeab2e1025499628c101ef

View File

@@ -4,7 +4,6 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include <stdatomic.h>
#include "esp_openthread_radio.h" #include "esp_openthread_radio.h"
#include "esp_err.h" #include "esp_err.h"
@@ -13,6 +12,7 @@
#include "esp_ieee802154.h" #include "esp_ieee802154.h"
#include "esp_ieee802154_types.h" #include "esp_ieee802154_types.h"
#include "esp_mac.h" #include "esp_mac.h"
#include "esp_openthread_common.h"
#include "esp_openthread_common_macro.h" #include "esp_openthread_common_macro.h"
#include "esp_openthread_platform.h" #include "esp_openthread_platform.h"
#include "esp_openthread_types.h" #include "esp_openthread_types.h"
@@ -52,12 +52,6 @@ typedef struct {
uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE];
} esp_openthread_radio_tx_psdu; } esp_openthread_radio_tx_psdu;
typedef struct {
uint8_t head;
uint8_t tail;
atomic_uint_fast8_t used;
} esp_openthread_circular_queue_info_t;
static otRadioFrame s_transmit_frame; static otRadioFrame s_transmit_frame;
static esp_openthread_radio_tx_psdu s_transmit_psdu; static esp_openthread_radio_tx_psdu s_transmit_psdu;

View File

@@ -10,7 +10,9 @@
#include <esp_check.h> #include <esp_check.h>
#include <esp_event.h> #include <esp_event.h>
#include <esp_log.h> #include <esp_log.h>
#include <esp_openthread_border_router.h>
#include <esp_openthread_dns64.h> #include <esp_openthread_dns64.h>
#include <esp_openthread_meshcop_mdns.h>
#include <esp_openthread_netif_glue_priv.h> #include <esp_openthread_netif_glue_priv.h>
#include <esp_openthread_radio.h> #include <esp_openthread_radio.h>
#include <esp_openthread_state.h> #include <esp_openthread_state.h>
@@ -19,6 +21,19 @@
#define TAG "OT_STATE" #define TAG "OT_STATE"
#if CONFIG_OPENTHREAD_BORDER_ROUTER
static void handle_ot_border_router_state_changed(otInstance* instance)
{
otDeviceRole role = otThreadGetDeviceRole(esp_openthread_get_instance());
if (role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER) {
esp_openthread_publish_meshcop_mdns(esp_openthread_get_meshcop_instance_name());
} else {
esp_openthread_remove_meshcop_mdns();
}
}
#endif
static void handle_ot_netif_state_change(otInstance* instance) static void handle_ot_netif_state_change(otInstance* instance)
{ {
if (otIp6IsEnabled(instance)) { if (otIp6IsEnabled(instance)) {
@@ -123,6 +138,12 @@ static void ot_state_change_callback(otChangedFlags changed_flags, void* ctx)
return; return;
} }
#if CONFIG_OPENTHREAD_BORDER_ROUTER
if (changed_flags & ESP_OPENTHREAD_BORDER_ROUTER_FLAG_OF_INTEREST) {
handle_ot_border_router_state_changed(instance);
}
#endif
if (changed_flags & OT_CHANGED_THREAD_ROLE) { if (changed_flags & OT_CHANGED_THREAD_ROLE) {
handle_ot_role_change(instance); handle_ot_role_change(instance);
} }

View File

@@ -18,10 +18,12 @@
#include "esp_netif_ip_addr.h" #include "esp_netif_ip_addr.h"
#include "esp_openthread.h" #include "esp_openthread.h"
#include "esp_openthread_border_router.h" #include "esp_openthread_border_router.h"
#include "esp_openthread_common.h"
#include "esp_openthread_common_macro.h" #include "esp_openthread_common_macro.h"
#include "esp_openthread_lock.h" #include "esp_openthread_lock.h"
#include "esp_openthread_platform.h"
#include "esp_openthread_radio.h" #include "esp_openthread_radio.h"
#include "esp_openthread_task_queue.h" #include "esp_vfs_eventfd.h"
#include "lwip/pbuf.h" #include "lwip/pbuf.h"
#include "lwip/tcpip.h" #include "lwip/tcpip.h"
#include "lwip/udp.h" #include "lwip/udp.h"
@@ -54,6 +56,10 @@ typedef struct {
static ot_trel_t s_ot_trel = {CONFIG_OPENTHREAD_TREL_PORT, NULL}; static ot_trel_t s_ot_trel = {CONFIG_OPENTHREAD_TREL_PORT, NULL};
static bool s_is_service_registered = false; static bool s_is_service_registered = false;
static ot_trel_recv_task_t s_trel_receive_buffer[CONFIG_OPENTHREAD_TREL_BUFFER_SIZE];
static esp_openthread_circular_queue_info_t s_recv_queue = {.head = 0, .tail = 0, .used = 0};
static const char *s_trel_workflow = "trel";
static int s_trel_event_fd = -1;
static void trel_browse_notifier(mdns_result_t *result) static void trel_browse_notifier(mdns_result_t *result)
{ {
@@ -89,55 +95,27 @@ static void trel_browse_notifier(mdns_result_t *result)
} }
} }
static void trel_recv_task(void *ctx)
{
esp_err_t ret = ESP_OK;
OT_UNUSED_VARIABLE(ret);
ot_trel_recv_task_t *task_ctx = (ot_trel_recv_task_t *)ctx;
struct pbuf *recv_buf = task_ctx->p;
uint8_t *data_buf = (uint8_t *)recv_buf->payload;
uint8_t *data_buf_to_free = NULL;
uint16_t length = recv_buf->len;
if (recv_buf->next != NULL) {
data_buf = (uint8_t *)malloc(recv_buf->tot_len);
ESP_GOTO_ON_FALSE(data_buf, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate data buf when receiving Thread TREL message");
length = recv_buf->tot_len;
data_buf_to_free = data_buf;
pbuf_copy_partial(recv_buf, data_buf, recv_buf->tot_len, 0);
}
otPlatTrelHandleReceived(esp_openthread_get_instance(), data_buf, length, task_ctx->source_addr);
exit:
if (recv_buf) {
pbuf_free(recv_buf);
}
free(data_buf_to_free);
free(task_ctx->source_addr);
free(task_ctx);
}
// TZ-1704
static void handle_trel_udp_recv(void *ctx, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, uint16_t port) static void handle_trel_udp_recv(void *ctx, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, uint16_t port)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
otSockAddr *source_addr = NULL;
uint64_t event_trel_rx = 1;
ESP_LOGD(OT_PLAT_LOG_TAG, "Receive from %s:%d", ip6addr_ntoa(&(addr->u_addr.ip6)), port); ESP_LOGD(OT_PLAT_LOG_TAG, "Receive from %s:%d", ip6addr_ntoa(&(addr->u_addr.ip6)), port);
ESP_GOTO_ON_FALSE(atomic_load(&s_recv_queue.used) < CONFIG_OPENTHREAD_TREL_BUFFER_SIZE, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "trel receive buffer full!");
source_addr = (otSockAddr *)malloc(sizeof(otSockAddr));
ESP_GOTO_ON_FALSE(source_addr, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL");
ot_trel_recv_task_t *task_ctx = (ot_trel_recv_task_t *)malloc(sizeof(ot_trel_recv_task_t)); memset(source_addr, 0, sizeof(otSockAddr));
ESP_GOTO_ON_FALSE(task_ctx, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL"); source_addr->mPort = port;
task_ctx->p = p; memcpy(&source_addr->mAddress.mFields.m32, addr->u_addr.ip6.addr, sizeof(addr->u_addr.ip6.addr));
task_ctx->source_addr = (otSockAddr *)malloc(sizeof(otSockAddr)); s_trel_receive_buffer[s_recv_queue.tail].source_addr = source_addr;
ESP_GOTO_ON_FALSE(task_ctx->source_addr, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL"); s_trel_receive_buffer[s_recv_queue.tail].p = p;
memset(task_ctx->source_addr, 0, sizeof(otSockAddr)); s_recv_queue.tail = (s_recv_queue.tail + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE;
task_ctx->source_addr->mPort = port; atomic_fetch_add(&s_recv_queue.used, 1);
memcpy(&task_ctx->source_addr->mAddress.mFields.m32, addr->u_addr.ip6.addr, sizeof(addr->u_addr.ip6.addr)); assert(write(s_trel_event_fd, &event_trel_rx, sizeof(event_trel_rx)) == sizeof(event_trel_rx));
ESP_GOTO_ON_ERROR(esp_openthread_task_queue_post(trel_recv_task, task_ctx), exit, OT_PLAT_LOG_TAG, "Failed to receive OpenThread TREL message");
exit: exit:
if (ret != ESP_OK) { if (ret != ESP_OK) {
free(task_ctx->source_addr);
free(task_ctx);
if (p) { if (p) {
pbuf_free(p); pbuf_free(p);
} }
@@ -146,25 +124,76 @@ exit:
static esp_err_t ot_new_trel(void *ctx) static esp_err_t ot_new_trel(void *ctx)
{ {
ot_trel_t *task = (ot_trel_t *)ctx; s_ot_trel.trel_pcb = udp_new();
ESP_RETURN_ON_FALSE(s_ot_trel.trel_pcb != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create a new UDP pcb");
udp_bind(s_ot_trel.trel_pcb, IP6_ADDR_ANY, s_ot_trel.port);
udp_recv(s_ot_trel.trel_pcb, handle_trel_udp_recv, NULL);
return ESP_OK;
}
void esp_openthread_trel_update(esp_openthread_mainloop_context_t *mainloop)
{
FD_SET(s_trel_event_fd, &mainloop->read_fds);
if (s_trel_event_fd > mainloop->max_fd) {
mainloop->max_fd = s_trel_event_fd;
}
}
esp_err_t esp_openthread_trel_process(otInstance *aInstance, const esp_openthread_mainloop_context_t *mainloop)
{
uint64_t event_read = 0;
assert(read(s_trel_event_fd, &event_read, sizeof(event_read)) == sizeof(event_read));
struct pbuf *recv_buf = NULL;
uint8_t *data_buf = NULL;
uint16_t length = 0;
otSockAddr *source_addr = NULL;
while (atomic_load(&s_recv_queue.used)) {
if (s_trel_receive_buffer[s_recv_queue.head].p != NULL) {
uint8_t *data_buf_to_free = NULL;
bool should_handle = true;
recv_buf = s_trel_receive_buffer[s_recv_queue.head].p;
data_buf = (uint8_t *)recv_buf->payload;
length = recv_buf->len;
source_addr = s_trel_receive_buffer[s_recv_queue.head].source_addr;
if (recv_buf->next != NULL) {
data_buf = (uint8_t *)malloc(recv_buf->tot_len);
if (data_buf) {
pbuf_copy_partial(recv_buf, data_buf, recv_buf->tot_len, 0);
} else {
should_handle = false;
}
length = recv_buf->tot_len;
data_buf_to_free = data_buf;
}
if (should_handle) {
otPlatTrelHandleReceived(aInstance, data_buf, length, source_addr);
}
pbuf_free(recv_buf);
free(data_buf_to_free);
free(source_addr);
s_recv_queue.head = (s_recv_queue.head + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE;
atomic_fetch_sub(&s_recv_queue.used, 1);
}
}
task->trel_pcb = udp_new();
ESP_RETURN_ON_FALSE(task->trel_pcb != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create a new UDP pcb");
udp_bind(task->trel_pcb, IP6_ADDR_ANY, task->port);
udp_recv(task->trel_pcb, handle_trel_udp_recv, NULL);
return ESP_OK; return ESP_OK;
} }
void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort) void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)
{ {
ESP_RETURN_ON_FALSE(s_trel_event_fd == -1, , OT_PLAT_LOG_TAG, "ot trel has been initialized.");
s_trel_event_fd = eventfd(0, 0);
assert(s_trel_event_fd >= 0);
*aUdpPort = s_ot_trel.port; *aUdpPort = s_ot_trel.port;
esp_openthread_task_switching_lock_release(); esp_openthread_task_switching_lock_release();
esp_err_t err = esp_netif_tcpip_exec(ot_new_trel, &s_ot_trel); ESP_ERROR_CHECK(esp_netif_tcpip_exec(ot_new_trel, NULL));
if (err != ESP_OK) {
ESP_LOGE(OT_PLAT_LOG_TAG, "Fail to create trel udp");
}
mdns_browse_new(TREL_MDNS_TYPE, TREL_MDNS_PROTO, trel_browse_notifier); mdns_browse_new(TREL_MDNS_TYPE, TREL_MDNS_PROTO, trel_browse_notifier);
esp_openthread_task_switching_lock_acquire(portMAX_DELAY); esp_openthread_task_switching_lock_acquire(portMAX_DELAY);
ESP_ERROR_CHECK(esp_openthread_platform_workflow_register(&esp_openthread_trel_update, &esp_openthread_trel_process, s_trel_workflow));
} }
static esp_err_t trel_send_task(void *ctx) static esp_err_t trel_send_task(void *ctx)
@@ -263,23 +292,41 @@ void otPlatTrelResetCounters(otInstance *aInstance)
memset(&s_trel_counters, 0, sizeof(otPlatTrelCounters)); memset(&s_trel_counters, 0, sizeof(otPlatTrelCounters));
} }
static void trel_disable_task(void *ctx) static esp_err_t trel_disable_task(void *ctx)
{ {
if (ctx) {
struct udp_pcb *pcb = (struct udp_pcb *)ctx; struct udp_pcb *pcb = (struct udp_pcb *)ctx;
udp_remove(pcb); udp_remove(pcb);
}
return ESP_OK;
}
static void free_all_buffer(void)
{
while (atomic_load(&s_recv_queue.used)) {
if (s_trel_receive_buffer[s_recv_queue.head].p != NULL) {
pbuf_free(s_trel_receive_buffer[s_recv_queue.head].p);
free(s_trel_receive_buffer[s_recv_queue.head].source_addr);
s_recv_queue.head = (s_recv_queue.head + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE;
atomic_fetch_sub(&s_recv_queue.used, 1);
}
}
} }
void otPlatTrelDisable(otInstance *aInstance) void otPlatTrelDisable(otInstance *aInstance)
{ {
ESP_RETURN_ON_FALSE(s_trel_event_fd >= 0, , OT_PLAT_LOG_TAG, "ot trel is not initialized.");
esp_openthread_task_switching_lock_release(); esp_openthread_task_switching_lock_release();
if (s_ot_trel.trel_pcb) { esp_netif_tcpip_exec(trel_disable_task, s_ot_trel.trel_pcb);
tcpip_callback(trel_disable_task, s_ot_trel.trel_pcb); s_ot_trel.trel_pcb = NULL;
}
mdns_service_remove(TREL_MDNS_TYPE, TREL_MDNS_PROTO); mdns_service_remove(TREL_MDNS_TYPE, TREL_MDNS_PROTO);
s_is_service_registered = false; s_is_service_registered = false;
mdns_browse_delete(TREL_MDNS_TYPE, TREL_MDNS_PROTO); mdns_browse_delete(TREL_MDNS_TYPE, TREL_MDNS_PROTO);
esp_openthread_task_switching_lock_acquire(portMAX_DELAY); esp_openthread_task_switching_lock_acquire(portMAX_DELAY);
s_ot_trel.trel_pcb = NULL; esp_openthread_platform_workflow_unregister(s_trel_workflow);
free_all_buffer();
close(s_trel_event_fd);
s_trel_event_fd = -1;
} }
const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance) const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance)

View File

@@ -199,15 +199,20 @@ void app_main(void)
// * netif // * netif
// * task queue // * task queue
// * border router // * border router
esp_vfs_eventfd_config_t eventfd_config = { size_t max_eventfd = 3;
#if CONFIG_OPENTHREAD_RADIO_NATIVE || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI #if CONFIG_OPENTHREAD_RADIO_NATIVE || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
// * radio driver (A native radio device needs a eventfd for radio driver.) // * radio driver (A native radio device needs a eventfd for radio driver.)
// * SpiSpinelInterface (The Spi Spinel Interface needs a eventfd.) // * SpiSpinelInterface (The Spi Spinel Interface needs a eventfd.)
// The above will not exist at the same time. // The above will not exist at the same time.
.max_fds = 4, max_eventfd++;
#else
.max_fds = 3,
#endif #endif
#if CONFIG_OPENTHREAD_RADIO_TREL
// * TREL reception (The Thread Radio Encapsulation Link needs a eventfd for reception.)
max_eventfd++;
#endif
esp_vfs_eventfd_config_t eventfd_config = {
.max_fds = max_eventfd,
}; };
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(nvs_flash_init());

View File

@@ -125,8 +125,9 @@ void app_main(void)
// Used eventfds: // Used eventfds:
// * netif // * netif
// * ot task queue // * ot task queue
// * ot trel
esp_vfs_eventfd_config_t eventfd_config = { esp_vfs_eventfd_config_t eventfd_config = {
.max_fds = 2, .max_fds = 3,
}; };
ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(nvs_flash_init());