Skip to content

Commit ba2b0fd

Browse files
committed
Merge branch 'feat/optimize_ot_trel_receive' into 'master'
feat(openthread): optimize trel reception See merge request espressif/esp-idf!39624
2 parents 5590a74 + 92b84d6 commit ba2b0fd

File tree

7 files changed

+165
-74
lines changed

7 files changed

+165
-74
lines changed

components/openthread/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ menu "OpenThread"
137137
default 12390
138138
help
139139
Configure the port number of TREL service.
140+
141+
config OPENTHREAD_TREL_BUFFER_SIZE
142+
int "The receive buffer size of openthread trel"
143+
depends on OPENTHREAD_RADIO_TREL
144+
range 10 255
145+
default 50
146+
help
147+
Configure the receive buffer size of TREL service.
140148
endmenu
141149

142150
menu "Thread 15.4 Radio Link"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#include <stdatomic.h>
10+
11+
typedef struct {
12+
uint8_t head;
13+
uint8_t tail;
14+
atomic_uint_fast8_t used;
15+
} esp_openthread_circular_queue_info_t;

components/openthread/private_include/esp_openthread_radio.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,27 @@ void esp_openthread_set_coex_config(esp_ieee802154_coex_config_t config);
8484
*
8585
*/
8686
esp_ieee802154_coex_config_t esp_openthread_get_coex_config(void);
87+
88+
/**
89+
* @brief This function updates the TREL fds and timeouts to the main loop.
90+
*
91+
* @param[inout] mainloop The main loop context.
92+
*
93+
*/
94+
void esp_openthread_trel_update(esp_openthread_mainloop_context_t *mainloop);
95+
96+
/**
97+
* @brief This function performs the OpenThread TREL process.
98+
*
99+
* @param[in] instance The OpenThread instance.
100+
* @param[in] mainloop The main loop context.
101+
*
102+
* @return
103+
* - ESP_OK on success
104+
* - ESP_FAIL on failure
105+
*
106+
*/
107+
esp_err_t esp_openthread_trel_process(otInstance *aInstance, const esp_openthread_mainloop_context_t *mainloop);
87108
#endif
88109

89110
#ifdef __cplusplus

components/openthread/src/port/esp_openthread_radio.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
#include <stdatomic.h>
87
#include "esp_openthread_radio.h"
98

109
#include "esp_err.h"
@@ -13,6 +12,7 @@
1312
#include "esp_ieee802154.h"
1413
#include "esp_ieee802154_types.h"
1514
#include "esp_mac.h"
15+
#include "esp_openthread_common.h"
1616
#include "esp_openthread_common_macro.h"
1717
#include "esp_openthread_platform.h"
1818
#include "esp_openthread_types.h"
@@ -52,12 +52,6 @@ typedef struct {
5252
uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE];
5353
} esp_openthread_radio_tx_psdu;
5454

55-
typedef struct {
56-
uint8_t head;
57-
uint8_t tail;
58-
atomic_uint_fast8_t used;
59-
} esp_openthread_circular_queue_info_t;
60-
6155
static otRadioFrame s_transmit_frame;
6256

6357
static esp_openthread_radio_tx_psdu s_transmit_psdu;

components/openthread/src/port/esp_openthread_trel.c

Lines changed: 106 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
#include "esp_netif_ip_addr.h"
1919
#include "esp_openthread.h"
2020
#include "esp_openthread_border_router.h"
21+
#include "esp_openthread_common.h"
2122
#include "esp_openthread_common_macro.h"
2223
#include "esp_openthread_lock.h"
24+
#include "esp_openthread_platform.h"
2325
#include "esp_openthread_radio.h"
24-
#include "esp_openthread_task_queue.h"
26+
#include "esp_vfs_eventfd.h"
2527
#include "lwip/pbuf.h"
2628
#include "lwip/tcpip.h"
2729
#include "lwip/udp.h"
@@ -54,6 +56,10 @@ typedef struct {
5456

5557
static ot_trel_t s_ot_trel = {CONFIG_OPENTHREAD_TREL_PORT, NULL};
5658
static bool s_is_service_registered = false;
59+
static ot_trel_recv_task_t s_trel_receive_buffer[CONFIG_OPENTHREAD_TREL_BUFFER_SIZE];
60+
static esp_openthread_circular_queue_info_t s_recv_queue = {.head = 0, .tail = 0, .used = 0};
61+
static const char *s_trel_workflow = "trel";
62+
static int s_trel_event_fd = -1;
5763

5864
static void trel_browse_notifier(mdns_result_t *result)
5965
{
@@ -89,55 +95,27 @@ static void trel_browse_notifier(mdns_result_t *result)
8995
}
9096
}
9197

92-
static void trel_recv_task(void *ctx)
93-
{
94-
esp_err_t ret = ESP_OK;
95-
OT_UNUSED_VARIABLE(ret);
96-
ot_trel_recv_task_t *task_ctx = (ot_trel_recv_task_t *)ctx;
97-
struct pbuf *recv_buf = task_ctx->p;
98-
uint8_t *data_buf = (uint8_t *)recv_buf->payload;
99-
uint8_t *data_buf_to_free = NULL;
100-
uint16_t length = recv_buf->len;
101-
102-
if (recv_buf->next != NULL) {
103-
data_buf = (uint8_t *)malloc(recv_buf->tot_len);
104-
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");
105-
length = recv_buf->tot_len;
106-
data_buf_to_free = data_buf;
107-
pbuf_copy_partial(recv_buf, data_buf, recv_buf->tot_len, 0);
108-
}
109-
otPlatTrelHandleReceived(esp_openthread_get_instance(), data_buf, length, task_ctx->source_addr);
110-
111-
exit:
112-
if (recv_buf) {
113-
pbuf_free(recv_buf);
114-
}
115-
free(data_buf_to_free);
116-
free(task_ctx->source_addr);
117-
free(task_ctx);
118-
}
119-
120-
// TZ-1704
12198
static void handle_trel_udp_recv(void *ctx, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, uint16_t port)
12299
{
123100
esp_err_t ret = ESP_OK;
101+
otSockAddr *source_addr = NULL;
102+
uint64_t event_trel_rx = 1;
124103
ESP_LOGD(OT_PLAT_LOG_TAG, "Receive from %s:%d", ip6addr_ntoa(&(addr->u_addr.ip6)), port);
125-
126-
ot_trel_recv_task_t *task_ctx = (ot_trel_recv_task_t *)malloc(sizeof(ot_trel_recv_task_t));
127-
ESP_GOTO_ON_FALSE(task_ctx, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL");
128-
task_ctx->p = p;
129-
task_ctx->source_addr = (otSockAddr *)malloc(sizeof(otSockAddr));
130-
ESP_GOTO_ON_FALSE(task_ctx->source_addr, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL");
131-
memset(task_ctx->source_addr, 0, sizeof(otSockAddr));
132-
task_ctx->source_addr->mPort = port;
133-
memcpy(&task_ctx->source_addr->mAddress.mFields.m32, addr->u_addr.ip6.addr, sizeof(addr->u_addr.ip6.addr));
134-
135-
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");
104+
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!");
105+
source_addr = (otSockAddr *)malloc(sizeof(otSockAddr));
106+
ESP_GOTO_ON_FALSE(source_addr, ESP_ERR_NO_MEM, exit, OT_PLAT_LOG_TAG, "Failed to allocate buf for Thread TREL");
107+
108+
memset(source_addr, 0, sizeof(otSockAddr));
109+
source_addr->mPort = port;
110+
memcpy(&source_addr->mAddress.mFields.m32, addr->u_addr.ip6.addr, sizeof(addr->u_addr.ip6.addr));
111+
s_trel_receive_buffer[s_recv_queue.tail].source_addr = source_addr;
112+
s_trel_receive_buffer[s_recv_queue.tail].p = p;
113+
s_recv_queue.tail = (s_recv_queue.tail + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE;
114+
atomic_fetch_add(&s_recv_queue.used, 1);
115+
assert(write(s_trel_event_fd, &event_trel_rx, sizeof(event_trel_rx)) == sizeof(event_trel_rx));
136116

137117
exit:
138118
if (ret != ESP_OK) {
139-
free(task_ctx->source_addr);
140-
free(task_ctx);
141119
if (p) {
142120
pbuf_free(p);
143121
}
@@ -146,25 +124,76 @@ static void handle_trel_udp_recv(void *ctx, struct udp_pcb *pcb, struct pbuf *p,
146124

147125
static esp_err_t ot_new_trel(void *ctx)
148126
{
149-
ot_trel_t *task = (ot_trel_t *)ctx;
127+
s_ot_trel.trel_pcb = udp_new();
128+
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");
129+
udp_bind(s_ot_trel.trel_pcb, IP6_ADDR_ANY, s_ot_trel.port);
130+
udp_recv(s_ot_trel.trel_pcb, handle_trel_udp_recv, NULL);
131+
return ESP_OK;
132+
}
133+
134+
void esp_openthread_trel_update(esp_openthread_mainloop_context_t *mainloop)
135+
{
136+
FD_SET(s_trel_event_fd, &mainloop->read_fds);
137+
if (s_trel_event_fd > mainloop->max_fd) {
138+
mainloop->max_fd = s_trel_event_fd;
139+
}
140+
}
141+
142+
esp_err_t esp_openthread_trel_process(otInstance *aInstance, const esp_openthread_mainloop_context_t *mainloop)
143+
{
144+
uint64_t event_read = 0;
145+
assert(read(s_trel_event_fd, &event_read, sizeof(event_read)) == sizeof(event_read));
146+
struct pbuf *recv_buf = NULL;
147+
uint8_t *data_buf = NULL;
148+
uint16_t length = 0;
149+
otSockAddr *source_addr = NULL;
150+
151+
while (atomic_load(&s_recv_queue.used)) {
152+
if (s_trel_receive_buffer[s_recv_queue.head].p != NULL) {
153+
154+
uint8_t *data_buf_to_free = NULL;
155+
bool should_handle = true;
156+
recv_buf = s_trel_receive_buffer[s_recv_queue.head].p;
157+
data_buf = (uint8_t *)recv_buf->payload;
158+
length = recv_buf->len;
159+
source_addr = s_trel_receive_buffer[s_recv_queue.head].source_addr;
160+
161+
if (recv_buf->next != NULL) {
162+
data_buf = (uint8_t *)malloc(recv_buf->tot_len);
163+
if (data_buf) {
164+
pbuf_copy_partial(recv_buf, data_buf, recv_buf->tot_len, 0);
165+
} else {
166+
should_handle = false;
167+
}
168+
length = recv_buf->tot_len;
169+
data_buf_to_free = data_buf;
170+
}
171+
if (should_handle) {
172+
otPlatTrelHandleReceived(aInstance, data_buf, length, source_addr);
173+
}
174+
pbuf_free(recv_buf);
175+
free(data_buf_to_free);
176+
free(source_addr);
177+
178+
s_recv_queue.head = (s_recv_queue.head + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE;
179+
atomic_fetch_sub(&s_recv_queue.used, 1);
180+
}
181+
}
150182

151-
task->trel_pcb = udp_new();
152-
ESP_RETURN_ON_FALSE(task->trel_pcb != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, "Failed to create a new UDP pcb");
153-
udp_bind(task->trel_pcb, IP6_ADDR_ANY, task->port);
154-
udp_recv(task->trel_pcb, handle_trel_udp_recv, NULL);
155183
return ESP_OK;
156184
}
157185

158186
void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)
159187
{
188+
ESP_RETURN_ON_FALSE(s_trel_event_fd == -1, , OT_PLAT_LOG_TAG, "ot trel has been initialized.");
189+
s_trel_event_fd = eventfd(0, 0);
190+
assert(s_trel_event_fd >= 0);
160191
*aUdpPort = s_ot_trel.port;
161192
esp_openthread_task_switching_lock_release();
162-
esp_err_t err = esp_netif_tcpip_exec(ot_new_trel, &s_ot_trel);
163-
if (err != ESP_OK) {
164-
ESP_LOGE(OT_PLAT_LOG_TAG, "Fail to create trel udp");
165-
}
193+
ESP_ERROR_CHECK(esp_netif_tcpip_exec(ot_new_trel, NULL));
166194
mdns_browse_new(TREL_MDNS_TYPE, TREL_MDNS_PROTO, trel_browse_notifier);
167195
esp_openthread_task_switching_lock_acquire(portMAX_DELAY);
196+
ESP_ERROR_CHECK(esp_openthread_platform_workflow_register(&esp_openthread_trel_update, &esp_openthread_trel_process, s_trel_workflow));
168197
}
169198

170199
static esp_err_t trel_send_task(void *ctx)
@@ -263,23 +292,41 @@ void otPlatTrelResetCounters(otInstance *aInstance)
263292
memset(&s_trel_counters, 0, sizeof(otPlatTrelCounters));
264293
}
265294

266-
static void trel_disable_task(void *ctx)
295+
static esp_err_t trel_disable_task(void *ctx)
267296
{
268-
struct udp_pcb *pcb = (struct udp_pcb *)ctx;
269-
udp_remove(pcb);
297+
if (ctx) {
298+
struct udp_pcb *pcb = (struct udp_pcb *)ctx;
299+
udp_remove(pcb);
300+
}
301+
return ESP_OK;
302+
}
303+
304+
static void free_all_buffer(void)
305+
{
306+
while (atomic_load(&s_recv_queue.used)) {
307+
if (s_trel_receive_buffer[s_recv_queue.head].p != NULL) {
308+
pbuf_free(s_trel_receive_buffer[s_recv_queue.head].p);
309+
free(s_trel_receive_buffer[s_recv_queue.head].source_addr);
310+
s_recv_queue.head = (s_recv_queue.head + 1) % CONFIG_OPENTHREAD_TREL_BUFFER_SIZE;
311+
atomic_fetch_sub(&s_recv_queue.used, 1);
312+
}
313+
}
270314
}
271315

272316
void otPlatTrelDisable(otInstance *aInstance)
273317
{
318+
ESP_RETURN_ON_FALSE(s_trel_event_fd >= 0, , OT_PLAT_LOG_TAG, "ot trel is not initialized.");
274319
esp_openthread_task_switching_lock_release();
275-
if (s_ot_trel.trel_pcb) {
276-
tcpip_callback(trel_disable_task, s_ot_trel.trel_pcb);
277-
}
320+
esp_netif_tcpip_exec(trel_disable_task, s_ot_trel.trel_pcb);
321+
s_ot_trel.trel_pcb = NULL;
278322
mdns_service_remove(TREL_MDNS_TYPE, TREL_MDNS_PROTO);
279323
s_is_service_registered = false;
280324
mdns_browse_delete(TREL_MDNS_TYPE, TREL_MDNS_PROTO);
281325
esp_openthread_task_switching_lock_acquire(portMAX_DELAY);
282-
s_ot_trel.trel_pcb = NULL;
326+
esp_openthread_platform_workflow_unregister(s_trel_workflow);
327+
free_all_buffer();
328+
close(s_trel_event_fd);
329+
s_trel_event_fd = -1;
283330
}
284331

285332
const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance)

examples/openthread/ot_br/main/esp_ot_br.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,15 +199,20 @@ void app_main(void)
199199
// * netif
200200
// * task queue
201201
// * border router
202-
esp_vfs_eventfd_config_t eventfd_config = {
202+
size_t max_eventfd = 3;
203+
203204
#if CONFIG_OPENTHREAD_RADIO_NATIVE || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
204-
// * radio driver (A native radio device needs a eventfd for radio driver.)
205-
// * SpiSpinelInterface (The Spi Spinel Interface needs a eventfd.)
206-
// The above will not exist at the same time.
207-
.max_fds = 4,
208-
#else
209-
.max_fds = 3,
205+
// * radio driver (A native radio device needs a eventfd for radio driver.)
206+
// * SpiSpinelInterface (The Spi Spinel Interface needs a eventfd.)
207+
// The above will not exist at the same time.
208+
max_eventfd++;
210209
#endif
210+
#if CONFIG_OPENTHREAD_RADIO_TREL
211+
// * TREL reception (The Thread Radio Encapsulation Link needs a eventfd for reception.)
212+
max_eventfd++;
213+
#endif
214+
esp_vfs_eventfd_config_t eventfd_config = {
215+
.max_fds = max_eventfd,
211216
};
212217
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
213218
ESP_ERROR_CHECK(nvs_flash_init());

examples/openthread/ot_trel/main/esp_ot_trel.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ void app_main(void)
125125
// Used eventfds:
126126
// * netif
127127
// * ot task queue
128+
// * ot trel
128129
esp_vfs_eventfd_config_t eventfd_config = {
129-
.max_fds = 2,
130+
.max_fds = 3,
130131
};
131132

132133
ESP_ERROR_CHECK(nvs_flash_init());

0 commit comments

Comments
 (0)