Skip to content

Commit 472e47e

Browse files
Damian-Nordicnordicjm
authored andcommitted
openthread: rpc: support otMessageRegisterTxCallback
Implement serialization of otMessageRegisterTxCallback() API that allows to register a callback invoked when a message has been transmitted successfully or it has been dropped. Add ot_rpc_callback module to avoid passing the raw function pointer and context to the peer device. Implement "ot udp txcallback enable|disable" shell command to enable testing this feature with "ot udp send" command. Signed-off-by: Damian Krolik <[email protected]>
1 parent d135709 commit 472e47e

File tree

11 files changed

+371
-6
lines changed

11 files changed

+371
-6
lines changed

doc/nrf/libraries/networking/ot_rpc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ OpenThread RPC currently supports the serialization of the following OpenThread
140140
* :c:func:`otMessageGetOffset`
141141
* :c:func:`otMessageGetThreadLinkInfo`
142142
* :c:func:`otMessageRead`
143+
* :c:func:`otMessageRegisterTxCallback`
143144
* :c:func:`otNetDataGet`
144145
* :c:func:`otNetDataGetNextOnMeshPrefix`
145146
* :c:func:`otNetDataGetNextService`

samples/nrf_rpc/protocols_serialization/client/src/ot_shell.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ struct srp_service {
5353
static bool ot_cli_is_initialized;
5454
static otUdpSocket udp_socket;
5555
static const char udp_payload[] = "Hello OpenThread World!";
56+
static bool udp_tx_callback_enabled;
5657
static char srp_client_host_name[OT_DNS_MAX_NAME_SIZE];
5758
static struct srp_service services[SERVICE_NUM];
5859

@@ -2021,6 +2022,13 @@ static int cmd_udp_connect(const struct shell *sh, size_t argc, char *argv[])
20212022
return ot_cli_command_exec(cmd_udp_connect_impl, sh, argc, argv);
20222023
}
20232024

2025+
static void udp_tx_callback(const otMessage *msg, otError error, void *ctx)
2026+
{
2027+
struct shell *sh = ctx;
2028+
2029+
shell_print(sh, "UDP tx result: %d", error);
2030+
}
2031+
20242032
static otError cmd_udp_send_impl(const struct shell *sh, size_t argc, char *argv[])
20252033
{
20262034
otError error;
@@ -2060,6 +2068,10 @@ static otError cmd_udp_send_impl(const struct shell *sh, size_t argc, char *argv
20602068
return error;
20612069
}
20622070

2071+
if (udp_tx_callback_enabled) {
2072+
otMessageRegisterTxCallback(msg, udp_tx_callback, (void *)sh);
2073+
}
2074+
20632075
error = otUdpSend(NULL, &udp_socket, msg, &msg_info);
20642076
if (error != OT_ERROR_NONE) {
20652077
otMessageFree(msg);
@@ -2083,12 +2095,34 @@ static int cmd_udp_close(const struct shell *sh, size_t argc, char *argv[])
20832095
return ot_cli_command_exec(cmd_udp_close_impl, sh, argc, argv);
20842096
}
20852097

2098+
static otError cmd_udp_txcallback_impl(const struct shell *sh, size_t argc, char *argv[])
2099+
{
2100+
if (argc == 1) {
2101+
shell_print(sh, "%s", udp_tx_callback_enabled ? "enabled" : "disabled");
2102+
} else if (strcmp(argv[1], "enable") == 0) {
2103+
udp_tx_callback_enabled = true;
2104+
} else if (strcmp(argv[1], "disable") == 0) {
2105+
udp_tx_callback_enabled = false;
2106+
} else {
2107+
return OT_ERROR_INVALID_ARGS;
2108+
}
2109+
2110+
return OT_ERROR_NONE;
2111+
}
2112+
2113+
static int cmd_udp_txcallback(const struct shell *sh, size_t argc, char *argv[])
2114+
{
2115+
return ot_cli_command_exec(cmd_udp_txcallback_impl, sh, argc, argv);
2116+
}
2117+
20862118
SHELL_STATIC_SUBCMD_SET_CREATE(
20872119
udp_cmds, SHELL_CMD_ARG(open, NULL, "Open socket", cmd_udp_open, 1, 0),
20882120
SHELL_CMD_ARG(bind, NULL, "Bind socket [-u|-b] <addr> <port>", cmd_udp_bind, 3, 1),
20892121
SHELL_CMD_ARG(connect, NULL, "Connect socket <addr> <port>", cmd_udp_connect, 3, 0),
20902122
SHELL_CMD_ARG(send, NULL, "Send message [addr port] <message>", cmd_udp_send, 2, 2),
2091-
SHELL_CMD_ARG(close, NULL, "Close socket", cmd_udp_close, 1, 0), SHELL_SUBCMD_SET_END);
2123+
SHELL_CMD_ARG(close, NULL, "Close socket", cmd_udp_close, 1, 0),
2124+
SHELL_CMD_ARG(txcallback, NULL, "Enable/disable tx callback", cmd_udp_txcallback, 1, 1),
2125+
SHELL_SUBCMD_SET_END);
20922126

20932127
static otError cmd_channel_impl(const struct shell *sh, size_t argc, char *argv[])
20942128
{

subsys/net/openthread/rpc/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ config OPENTHREAD_RPC_CLIENT_NUM_SENT_COAP_REQUESTS
6565
Defines the number of slots for storing a callback along with its context
6666
for ongoing CoAP requests awaiting a response.
6767

68+
config OPENTHREAD_RPC_CLIENT_NUM_TX_CALLBACKS
69+
int "Maximum number of registered message TX callbacks"
70+
default 8
71+
6872
config OPENTHREAD_RPC_CLIENT_RADIO_TIME_REFRESH_PERIOD
6973
int "Radio time sync period [s]"
7074
default 30

subsys/net/openthread/rpc/client/ot_rpc_message.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
*/
66

77
#include <nrf_rpc/nrf_rpc_serialize.h>
8+
#include <ot_rpc_callback.h>
9+
#include <ot_rpc_common.h>
810
#include <ot_rpc_ids.h>
11+
#include <ot_rpc_lock.h>
912
#include <ot_rpc_types.h>
10-
#include <ot_rpc_common.h>
1113

1214
#include <nrf_rpc_cbor.h>
1315

@@ -16,6 +18,9 @@
1618

1719
#include <string.h>
1820

21+
OT_RPC_CALLBACK_TABLE_DEFINE(tx_cb, otMessageTxCallback,
22+
CONFIG_OPENTHREAD_RPC_CLIENT_NUM_TX_CALLBACKS);
23+
1924
otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength)
2025
{
2126
struct nrf_rpc_cbor_ctx ctx;
@@ -177,3 +182,59 @@ otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *
177182

178183
return error;
179184
}
185+
186+
void otMessageRegisterTxCallback(otMessage *aMessage, otMessageTxCallback aCallback, void *aContext)
187+
{
188+
ot_rpc_res_tab_key key = (ot_rpc_res_tab_key)aMessage;
189+
ot_rpc_callback_id cb_id;
190+
struct nrf_rpc_cbor_ctx ctx;
191+
192+
cb_id = ot_rpc_tx_cb_alloc(aCallback, aContext);
193+
194+
if (aCallback != NULL && cb_id == OT_RPC_CALLBACK_ID_NULL) {
195+
/* Failed to allocate a callback entry. */
196+
nrf_rpc_err(-ENOMEM, NRF_RPC_ERR_SRC_SEND, &ot_group,
197+
OT_RPC_CMD_MESSAGE_REGISTER_TX_CALLBACK, NRF_RPC_PACKET_TYPE_CMD);
198+
return;
199+
}
200+
201+
NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 2 + sizeof(key) + sizeof(cb_id));
202+
nrf_rpc_encode_uint(&ctx, key);
203+
nrf_rpc_encode_uint(&ctx, cb_id);
204+
nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_MESSAGE_REGISTER_TX_CALLBACK, &ctx,
205+
nrf_rpc_rsp_decode_void, NULL);
206+
}
207+
208+
static void ot_rpc_cmd_message_tx_cb(const struct nrf_rpc_group *group,
209+
struct nrf_rpc_cbor_ctx *ctx, void *handler_data)
210+
{
211+
ot_rpc_callback_id cb_id;
212+
ot_rpc_res_tab_key msg_key;
213+
otError error;
214+
otMessageTxCallback cb;
215+
void *context;
216+
217+
cb_id = nrf_rpc_decode_uint(ctx);
218+
msg_key = nrf_rpc_decode_uint(ctx);
219+
error = nrf_rpc_decode_uint(ctx);
220+
221+
if (!nrf_rpc_decoding_done_and_check(group, ctx)) {
222+
ot_rpc_report_cmd_decoding_error(OT_RPC_CMD_MESSAGE_TX_CB);
223+
return;
224+
}
225+
226+
ot_rpc_mutex_lock();
227+
228+
cb = ot_rpc_tx_cb_get(cb_id, &context);
229+
230+
if (cb != NULL) {
231+
ot_rpc_tx_cb_free(cb_id);
232+
cb((const otMessage *)msg_key, error, context);
233+
}
234+
235+
ot_rpc_mutex_unlock();
236+
nrf_rpc_rsp_send_void(group);
237+
}
238+
239+
NRF_RPC_CBOR_CMD_DECODER(ot_group, ot_rpc_cmd_message_tx_cb, OT_RPC_CMD_MESSAGE_TX_CB,
240+
ot_rpc_cmd_message_tx_cb, NULL);

subsys/net/openthread/rpc/common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
zephyr_library()
88

99
zephyr_library_sources(
10+
ot_rpc_callback.c
1011
ot_rpc_common.c
1112
ot_rpc_group.c
1213
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include "ot_rpc_callback.h"
8+
9+
ot_rpc_callback_id ot_rpc_callback_alloc(struct ot_rpc_callback *table, size_t table_size,
10+
ot_rpc_callback_func func, void *context)
11+
{
12+
struct ot_rpc_callback *cb;
13+
ot_rpc_callback_id free_id = OT_RPC_CALLBACK_ID_NULL;
14+
15+
if (func == NULL) {
16+
return OT_RPC_CALLBACK_ID_NULL;
17+
}
18+
19+
for (ot_rpc_callback_id id = 1; id <= table_size; id++) {
20+
cb = &table[id - 1];
21+
22+
/* Avoid adding the same function and context twice - reuse the previous entry. */
23+
if (cb->func == func && cb->context == context) {
24+
return id;
25+
}
26+
27+
if (free_id == OT_RPC_CALLBACK_ID_NULL && cb->func == NULL) {
28+
free_id = id;
29+
}
30+
}
31+
32+
if (free_id != OT_RPC_CALLBACK_ID_NULL) {
33+
cb = &table[free_id - 1];
34+
cb->func = func;
35+
cb->context = context;
36+
}
37+
38+
return free_id;
39+
}
40+
41+
ot_rpc_callback_func ot_rpc_callback_get(struct ot_rpc_callback *table, size_t table_size,
42+
ot_rpc_callback_id id, void **context_out)
43+
{
44+
struct ot_rpc_callback *cb;
45+
46+
if (id == OT_RPC_CALLBACK_ID_NULL || id > table_size) {
47+
return NULL;
48+
}
49+
50+
cb = &table[id - 1];
51+
*context_out = cb->context;
52+
53+
return cb->func;
54+
}
55+
56+
void ot_rpc_callback_free(struct ot_rpc_callback *table, size_t table_size, ot_rpc_callback_id id)
57+
{
58+
struct ot_rpc_callback *cb;
59+
60+
if (id == OT_RPC_CALLBACK_ID_NULL || id > table_size) {
61+
return;
62+
}
63+
64+
cb = &table[id - 1];
65+
cb->func = NULL;
66+
cb->context = NULL;
67+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#ifndef OT_RPC_CALLBACK_H
8+
#define OT_RPC_CALLBACK_H
9+
10+
#include <stdint.h>
11+
#include <stddef.h>
12+
13+
#ifndef ARRAY_SIZE
14+
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
15+
#endif
16+
17+
/**
18+
* @brief Generic function pointer type.
19+
*
20+
* A function pointer type that is guaranteed to store a pointer to function of any type.
21+
*/
22+
typedef void (*ot_rpc_callback_func)(void);
23+
24+
/**
25+
* @brief Structure to hold a function pointer of any type and its related context.
26+
*/
27+
struct ot_rpc_callback {
28+
ot_rpc_callback_func func;
29+
void *context;
30+
};
31+
32+
/**
33+
* @brief OpenThread RPC callback ID.
34+
*
35+
* A portable representation of a callback and its related context that can be passed to a remote
36+
* device during remote procedure calls.
37+
*/
38+
typedef uint32_t ot_rpc_callback_id;
39+
40+
enum {
41+
OT_RPC_CALLBACK_ID_NULL = 0,
42+
};
43+
44+
ot_rpc_callback_id ot_rpc_callback_alloc(struct ot_rpc_callback *table, size_t table_size,
45+
ot_rpc_callback_func func, void *context);
46+
ot_rpc_callback_func ot_rpc_callback_get(struct ot_rpc_callback *table, size_t table_size,
47+
ot_rpc_callback_id id, void **context_out);
48+
void ot_rpc_callback_free(struct ot_rpc_callback *table, size_t table_size, ot_rpc_callback_id id);
49+
50+
/**
51+
* @brief Defines a named callback table with the specified function pointer type and size.
52+
*
53+
* This creates an array of callback entries and provides type-safe functions for managing and
54+
* accessing the callback table:
55+
*
56+
* @code
57+
* ot_rpc_callback_id ot_rpc_NAME_alloc(TYPE func, void *context);
58+
* TYPE ot_rpc_NAME_get(ot_rpc_callback_id id, void **context_out);
59+
* void ot_rpc_NAME_free(ot_rpc_callback_id id);
60+
* @endcode
61+
*
62+
* The callback table enables the local device to register a callback on the peer device without
63+
* sending the actual raw function pointer and context. Instead, the sender stores the raw function
64+
* pointer and context in a local callback table and sends only the identifier of the allocated
65+
* entry.
66+
* This approach enhances portability and reduces the risk of errors in callback handling.
67+
*/
68+
#define OT_RPC_CALLBACK_TABLE_DEFINE(name, type, size) \
69+
static struct ot_rpc_callback name[size]; \
70+
\
71+
static inline ot_rpc_callback_id ot_rpc_##name##_alloc(type func, void *context) \
72+
{ \
73+
return ot_rpc_callback_alloc(name, ARRAY_SIZE(name), (ot_rpc_callback_func)func, \
74+
context); \
75+
} \
76+
static inline type ot_rpc_##name##_get(ot_rpc_callback_id id, void **context_out) \
77+
{ \
78+
return (type)ot_rpc_callback_get(name, ARRAY_SIZE(name), id, context_out); \
79+
} \
80+
static inline void ot_rpc_##name##_free(ot_rpc_callback_id id) \
81+
{ \
82+
ot_rpc_callback_free(name, ARRAY_SIZE(name), id); \
83+
}
84+
85+
#endif /* OT_RPC_CALLBACK_H */

subsys/net/openthread/rpc/common/ot_rpc_ids.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum ot_rpc_cmd_client {
2525
OT_RPC_CMD_DNS_ADDRESS_RESPONSE_CB,
2626
OT_RPC_CMD_DNS_BROWSE_RESPONSE_CB,
2727
OT_RPC_CMD_DNS_SERVICE_RESPONSE_CB,
28+
OT_RPC_CMD_MESSAGE_TX_CB,
2829
};
2930

3031
/** @brief Command IDs accepted by the OpenThread over RPC server.
@@ -106,6 +107,7 @@ enum ot_rpc_cmd_server {
106107
OT_RPC_CMD_MESSAGE_GET_OFFSET,
107108
OT_RPC_CMD_MESSAGE_READ,
108109
OT_RPC_CMD_MESSAGE_GET_THREAD_LINK_INFO,
110+
OT_RPC_CMD_MESSAGE_REGISTER_TX_CALLBACK,
109111
OT_RPC_CMD_UDP_OPEN,
110112
OT_RPC_CMD_UDP_IS_OPEN,
111113
OT_RPC_CMD_UDP_SEND,

subsys/net/openthread/rpc/server/ot_rpc_dataset.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66

77
#include <nrf_rpc/nrf_rpc_serialize.h>
8-
#include <nrf_rpc/nrf_rpc_cbkproxy.h>
98
#include <ot_rpc_ids.h>
109
#include <ot_rpc_types.h>
1110
#include <ot_rpc_common.h>

0 commit comments

Comments
 (0)