1 /*
2 * Copyright (c) 2025 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * This file implements the OpenThread module initialization and state change handling.
10 *
11 */
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(net_openthread_platform, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL);
15
16 #include <zephyr/kernel.h>
17 #include <zephyr/init.h>
18 #include <zephyr/version.h>
19 #include <zephyr/sys/check.h>
20
21 #include "platform/platform-zephyr.h"
22
23 #include <openthread.h>
24 #include <openthread_utils.h>
25
26 #include <openthread/child_supervision.h>
27 #include <openthread/cli.h>
28 #include <openthread/ip6.h>
29 #include <openthread/link.h>
30 #include <openthread/link_raw.h>
31 #include <openthread/ncp.h>
32 #include <openthread/message.h>
33 #include <openthread/platform/diag.h>
34 #include <openthread/tasklet.h>
35 #include <openthread/thread.h>
36 #include <openthread/thread_ftd.h>
37 #include <openthread/dataset.h>
38 #include <openthread/joiner.h>
39 #include <openthread-system.h>
40 #include <utils/uart.h>
41
42 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
43 #include <openthread/nat64.h>
44 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
45
46 #if defined(CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER)
47 #include "openthread_border_router.h"
48 #endif /* CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER */
49
50 #define OT_STACK_SIZE (CONFIG_OPENTHREAD_THREAD_STACK_SIZE)
51
52 #if defined(CONFIG_OPENTHREAD_THREAD_PREEMPTIVE)
53 #define OT_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY)
54 #else
55 #define OT_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)
56 #endif
57
58 #if defined(CONFIG_OPENTHREAD_NETWORK_NAME)
59 #define OT_NETWORK_NAME CONFIG_OPENTHREAD_NETWORK_NAME
60 #else
61 #define OT_NETWORK_NAME ""
62 #endif
63
64 #if defined(CONFIG_OPENTHREAD_CHANNEL)
65 #define OT_CHANNEL CONFIG_OPENTHREAD_CHANNEL
66 #else
67 #define OT_CHANNEL 0
68 #endif
69
70 #if defined(CONFIG_OPENTHREAD_PANID)
71 #define OT_PANID CONFIG_OPENTHREAD_PANID
72 #else
73 #define OT_PANID 0
74 #endif
75
76 #if defined(CONFIG_OPENTHREAD_XPANID)
77 #define OT_XPANID CONFIG_OPENTHREAD_XPANID
78 #else
79 #define OT_XPANID ""
80 #endif
81
82 #if defined(CONFIG_OPENTHREAD_NETWORKKEY)
83 #define OT_NETWORKKEY CONFIG_OPENTHREAD_NETWORKKEY
84 #else
85 #define OT_NETWORKKEY ""
86 #endif
87
88 #if defined(CONFIG_OPENTHREAD_JOINER_PSKD)
89 #define OT_JOINER_PSKD CONFIG_OPENTHREAD_JOINER_PSKD
90 #else
91 #define OT_JOINER_PSKD ""
92 #endif
93
94 #if defined(CONFIG_OPENTHREAD_PLATFORM_INFO)
95 #define OT_PLATFORM_INFO CONFIG_OPENTHREAD_PLATFORM_INFO
96 #else
97 #define OT_PLATFORM_INFO ""
98 #endif
99
100 #if defined(CONFIG_OPENTHREAD_POLL_PERIOD)
101 #define OT_POLL_PERIOD CONFIG_OPENTHREAD_POLL_PERIOD
102 #else
103 #define OT_POLL_PERIOD 0
104 #endif
105
106 #if defined(CONFIG_OPENTHREAD_ROUTER_SELECTION_JITTER)
107 #define OT_ROUTER_SELECTION_JITTER CONFIG_OPENTHREAD_ROUTER_SELECTION_JITTER
108 #else
109 #define OT_ROUTER_SELECTION_JITTER 0
110 #endif
111
112 #define ZEPHYR_PACKAGE_NAME "Zephyr"
113 #define PACKAGE_VERSION KERNEL_VERSION_STRING
114
115 static void openthread_process(struct k_work *work);
116
117 /* Global variables to store the OpenThread module context */
118 static otInstance *openthread_instance;
119 static sys_slist_t openthread_state_change_cbs = SYS_SLIST_STATIC_INIT(openthread_state_change_cbs);
120 static struct k_work_q openthread_work_q;
121
122 static K_WORK_DEFINE(openthread_work, openthread_process);
123 static K_MUTEX_DEFINE(openthread_lock);
124 K_KERNEL_STACK_DEFINE(ot_stack_area, OT_STACK_SIZE);
125
openthread_thread_id_get(void)126 k_tid_t openthread_thread_id_get(void)
127 {
128 return (k_tid_t)&openthread_work_q.thread;
129 }
130
ncp_hdlc_send(const uint8_t * buf,uint16_t len)131 static int ncp_hdlc_send(const uint8_t *buf, uint16_t len)
132 {
133 otError err = OT_ERROR_NONE;
134
135 err = otPlatUartSend(buf, len);
136 if (err != OT_ERROR_NONE) {
137 return 0;
138 }
139
140 return len;
141 }
142
openthread_process(struct k_work * work)143 static void openthread_process(struct k_work *work)
144 {
145 ARG_UNUSED(work);
146
147 openthread_mutex_lock();
148
149 while (otTaskletsArePending(openthread_instance)) {
150 otTaskletsProcess(openthread_instance);
151 }
152
153 otSysProcessDrivers(openthread_instance);
154
155 openthread_mutex_unlock();
156 }
157
ot_joiner_start_handler(otError error,void * context)158 static void ot_joiner_start_handler(otError error, void *context)
159 {
160 ARG_UNUSED(context);
161
162 if (error != OT_ERROR_NONE) {
163 LOG_ERR("Join failed [%d]", error);
164 } else {
165 LOG_INF("Join success");
166 error = otThreadSetEnabled(openthread_instance, true);
167 if (error != OT_ERROR_NONE) {
168 LOG_ERR("Failed to start the OpenThread network [%d]", error);
169 }
170 }
171 }
172
ot_configure_instance(void)173 static void ot_configure_instance(void)
174 {
175 #ifndef CONFIG_OPENTHREAD_COPROCESSOR_RCP
176 /* Configure Child Supervision and MLE Child timeouts. */
177 otChildSupervisionSetInterval(openthread_instance,
178 CONFIG_OPENTHREAD_CHILD_SUPERVISION_INTERVAL);
179 otChildSupervisionSetCheckTimeout(openthread_instance,
180 CONFIG_OPENTHREAD_CHILD_SUPERVISION_CHECK_TIMEOUT);
181 otThreadSetChildTimeout(openthread_instance, CONFIG_OPENTHREAD_MLE_CHILD_TIMEOUT);
182
183 if (IS_ENABLED(CONFIG_OPENTHREAD_ROUTER_SELECTION_JITTER_OVERRIDE)) {
184 otThreadSetRouterSelectionJitter(openthread_instance, OT_ROUTER_SELECTION_JITTER);
185 }
186 #endif
187 }
188
ot_setup_default_configuration(void)189 static bool ot_setup_default_configuration(void)
190 {
191 otExtendedPanId xpanid = {0};
192 otNetworkKey networkKey = {0};
193 otError error = OT_ERROR_NONE;
194
195 error = otThreadSetNetworkName(openthread_instance, OT_NETWORK_NAME);
196 if (error != OT_ERROR_NONE) {
197 LOG_ERR("Failed to set %s [%d]", "network name", error);
198 return false;
199 }
200
201 error = otLinkSetChannel(openthread_instance, OT_CHANNEL);
202 if (error != OT_ERROR_NONE) {
203 LOG_ERR("Failed to set %s [%d]", "channel", error);
204 return false;
205 }
206
207 error = otLinkSetPanId(openthread_instance, OT_PANID);
208 if (error != OT_ERROR_NONE) {
209 LOG_ERR("Failed to set %s [%d]", "PAN ID", error);
210 return false;
211 }
212
213 if (bytes_from_str(xpanid.m8, 8, (char *)OT_XPANID) != 0) {
214 LOG_ERR("Failed to parse extended PAN ID");
215 return false;
216 }
217 error = otThreadSetExtendedPanId(openthread_instance, &xpanid);
218 if (error != OT_ERROR_NONE) {
219 LOG_ERR("Failed to set %s [%d]", "ext PAN ID", error);
220 return false;
221 }
222
223 if (strlen(OT_NETWORKKEY)) {
224 if (bytes_from_str(networkKey.m8, OT_NETWORK_KEY_SIZE, (char *)OT_NETWORKKEY) !=
225 0) {
226 LOG_ERR("Failed to parse network key");
227 return false;
228 }
229 error = otThreadSetNetworkKey(openthread_instance, &networkKey);
230 if (error != OT_ERROR_NONE) {
231 LOG_ERR("Failed to set %s [%d]", "network key", error);
232 return false;
233 }
234 }
235
236 return true;
237 }
238
ot_state_changed_handler(uint32_t flags,void * context)239 static void ot_state_changed_handler(uint32_t flags, void *context)
240 {
241 ARG_UNUSED(context);
242
243 struct openthread_state_changed_callback *entry, *next;
244
245 bool is_up = otIp6IsEnabled(openthread_instance);
246
247 LOG_INF("State changed! Flags: 0x%08" PRIx32 " Current role: %s Ip6: %s", flags,
248 otThreadDeviceRoleToString(otThreadGetDeviceRole(openthread_instance)),
249 (is_up ? "up" : "down"));
250
251 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&openthread_state_change_cbs, entry, next, node) {
252 if (entry->otCallback != NULL) {
253 entry->otCallback(flags, entry->user_data);
254 }
255 }
256 }
257
otTaskletsSignalPending(otInstance * instance)258 void otTaskletsSignalPending(otInstance *instance)
259 {
260 ARG_UNUSED(instance);
261
262 int error = k_work_submit_to_queue(&openthread_work_q, &openthread_work);
263
264 if (error < 0) {
265 LOG_ERR("Failed to submit work to queue, error: %d", error);
266 }
267 }
268
otSysEventSignalPending(void)269 void otSysEventSignalPending(void)
270 {
271 otTaskletsSignalPending(NULL);
272 }
273
openthread_state_changed_callback_register(struct openthread_state_changed_callback * cb)274 int openthread_state_changed_callback_register(struct openthread_state_changed_callback *cb)
275 {
276 CHECKIF(cb == NULL || cb->otCallback == NULL) {
277 return -EINVAL;
278 }
279
280 openthread_mutex_lock();
281 sys_slist_append(&openthread_state_change_cbs, &cb->node);
282 openthread_mutex_unlock();
283
284 return 0;
285 }
286
openthread_state_changed_callback_unregister(struct openthread_state_changed_callback * cb)287 int openthread_state_changed_callback_unregister(struct openthread_state_changed_callback *cb)
288 {
289 bool removed = false;
290
291 CHECKIF(cb == NULL) {
292 return -EINVAL;
293 }
294
295 openthread_mutex_lock();
296 removed = sys_slist_find_and_remove(&openthread_state_change_cbs, &cb->node);
297 openthread_mutex_unlock();
298
299 if (!removed) {
300 return -EALREADY;
301 }
302
303 return 0;
304 }
305
openthread_get_default_instance(void)306 struct otInstance *openthread_get_default_instance(void)
307 {
308 __ASSERT(openthread_instance, "OT instance is not initialized");
309 return openthread_instance;
310 }
311
openthread_init(void)312 int openthread_init(void)
313 {
314 struct k_work_queue_config q_cfg = {
315 .name = "openthread",
316 .no_yield = true,
317 };
318 otError error = OT_ERROR_NONE;
319
320 /* Prevent multiple initializations */
321 if (openthread_instance) {
322 return 0;
323 }
324
325 /* Initialize the OpenThread work queue */
326 k_work_queue_init(&openthread_work_q);
327
328 /* Start work queue for the OpenThread module */
329 k_work_queue_start(&openthread_work_q, ot_stack_area,
330 K_KERNEL_STACK_SIZEOF(ot_stack_area),
331 OT_PRIORITY, &q_cfg);
332
333 openthread_mutex_lock();
334
335 otSysInit(0, NULL);
336 openthread_instance = otInstanceInitSingle();
337
338 __ASSERT(openthread_instance, "OT instance initialization failed");
339
340 if (IS_ENABLED(CONFIG_OPENTHREAD_SHELL)) {
341 platformShellInit(openthread_instance);
342 }
343
344 if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
345 error = otPlatUartEnable();
346 if (error != OT_ERROR_NONE) {
347 LOG_ERR("Failed to enable UART: [%d]", error);
348 }
349
350 otNcpHdlcInit(openthread_instance, ncp_hdlc_send);
351 } else {
352 otIp6SetReceiveFilterEnabled(openthread_instance, true);
353
354 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) && !defined(CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER)
355
356 otIp4Cidr nat64_cidr;
357
358 if (otIp4CidrFromString(CONFIG_OPENTHREAD_NAT64_CIDR, &nat64_cidr) ==
359 OT_ERROR_NONE) {
360 if (otNat64SetIp4Cidr(openthread_instance, &nat64_cidr) != OT_ERROR_NONE) {
361 LOG_ERR("Incorrect NAT64 CIDR");
362 return -EIO;
363 }
364 } else {
365 LOG_ERR("Failed to parse NAT64 CIDR");
366 return -EIO;
367 }
368 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR && !CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER */
369
370 error = otSetStateChangedCallback(openthread_instance, &ot_state_changed_handler,
371 NULL);
372 if (error != OT_ERROR_NONE) {
373 LOG_ERR("Could not set state changed callback: %d", error);
374 return -EIO;
375 }
376 }
377
378 ot_configure_instance();
379 openthread_mutex_unlock();
380
381 (void)k_work_submit_to_queue(&openthread_work_q, &openthread_work);
382
383 return error == OT_ERROR_NONE ? 0 : -EIO;
384 }
385
openthread_run(void)386 int openthread_run(void)
387 {
388 openthread_mutex_lock();
389 otError error = OT_ERROR_NONE;
390
391 LOG_INF("OpenThread version: %s", otGetVersionString());
392
393 if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
394 LOG_DBG("OpenThread co-processor.");
395 goto exit;
396 }
397
398 error = otIp6SetEnabled(openthread_instance, true);
399 if (error != OT_ERROR_NONE) {
400 LOG_ERR("Failed to set %s [%d]", "IPv6 support", error);
401 goto exit;
402 }
403
404 /* Sleepy End Device specific configuration. */
405 if (IS_ENABLED(CONFIG_OPENTHREAD_MTD_SED)) {
406 otLinkModeConfig ot_mode = otThreadGetLinkMode(openthread_instance);
407
408 /* A SED should always attach the network as a SED to indicate
409 * increased buffer requirement to a parent.
410 */
411 ot_mode.mRxOnWhenIdle = false;
412
413 error = otThreadSetLinkMode(openthread_instance, ot_mode);
414 if (error != OT_ERROR_NONE) {
415 LOG_ERR("Failed to set %s [%d]", "link mode", error);
416 goto exit;
417 }
418
419 error = otLinkSetPollPeriod(openthread_instance, OT_POLL_PERIOD);
420 if (error != OT_ERROR_NONE) {
421 LOG_ERR("Failed to set %s [%d]", "poll period", error);
422 goto exit;
423 }
424 }
425
426 if (otDatasetIsCommissioned(openthread_instance)) {
427 /* OpenThread already has dataset stored - skip the
428 * configuration.
429 */
430 LOG_DBG("OpenThread already commissioned.");
431 } else if (IS_ENABLED(CONFIG_OPENTHREAD_JOINER_AUTOSTART)) {
432 /* No dataset - initiate network join procedure. */
433 LOG_DBG("Starting OpenThread join procedure.");
434
435 error = otJoinerStart(openthread_instance, OT_JOINER_PSKD, NULL,
436 ZEPHYR_PACKAGE_NAME, OT_PLATFORM_INFO, PACKAGE_VERSION, NULL,
437 &ot_joiner_start_handler, NULL);
438
439 if (error != OT_ERROR_NONE) {
440 LOG_ERR("Failed to start joiner [%d]", error);
441 }
442
443 goto exit;
444 } else {
445 /* No dataset - load the default configuration. */
446 LOG_DBG("Loading OpenThread default configuration.");
447
448 if (!ot_setup_default_configuration()) {
449 goto exit;
450 }
451 }
452
453 LOG_INF("Network name: %s", otThreadGetNetworkName(openthread_instance));
454
455 /* Start the network. */
456 error = otThreadSetEnabled(openthread_instance, true);
457 if (error != OT_ERROR_NONE) {
458 LOG_ERR("Failed to start the OpenThread network [%d]", error);
459 }
460
461 exit:
462
463 openthread_mutex_unlock();
464
465 return error == OT_ERROR_NONE ? 0 : -EIO;
466 }
467
openthread_stop(void)468 int openthread_stop(void)
469 {
470 otError error = OT_ERROR_NONE;
471
472 if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
473 return 0;
474 }
475
476 openthread_mutex_lock();
477
478 error = otThreadSetEnabled(openthread_instance, false);
479 if (error == OT_ERROR_INVALID_STATE) {
480 LOG_DBG("Openthread interface was not up [%d]", error);
481 }
482
483 openthread_mutex_unlock();
484
485 return 0;
486 }
487
openthread_set_receive_cb(openthread_receive_cb cb,void * context)488 void openthread_set_receive_cb(openthread_receive_cb cb, void *context)
489 {
490 __ASSERT(cb != NULL, "Receive callback is not set");
491 __ASSERT(openthread_instance != NULL, "OpenThread instance is not initialized");
492
493 if (!IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
494 openthread_mutex_lock();
495 otIp6SetReceiveCallback(openthread_instance, cb, context);
496
497 openthread_mutex_unlock();
498 }
499 }
500
501 #if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
openthread_set_nat64_receive_cb(openthread_receive_cb cb,void * context)502 void openthread_set_nat64_receive_cb(openthread_receive_cb cb, void *context)
503 {
504 __ASSERT(cb != NULL, "NAT64 receive callback is not set");
505 __ASSERT(openthread_instance != NULL, "OpenThread instance is not initialized");
506
507 if (!IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
508 openthread_mutex_lock();
509
510 otNat64SetReceiveIp4Callback(openthread_instance, cb, context);
511
512 openthread_mutex_unlock();
513 }
514 }
515 #endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
516
openthread_mutex_lock(void)517 void openthread_mutex_lock(void)
518 {
519 (void)k_mutex_lock(&openthread_lock, K_FOREVER);
520 }
521
openthread_mutex_try_lock(void)522 int openthread_mutex_try_lock(void)
523 {
524 return k_mutex_lock(&openthread_lock, K_NO_WAIT);
525 }
526
openthread_mutex_unlock(void)527 void openthread_mutex_unlock(void)
528 {
529 (void)k_mutex_unlock(&openthread_lock);
530 }
531
532 #if defined(CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER)
openthread_notify_border_router_work(void)533 void openthread_notify_border_router_work(void)
534 {
535 int error = k_work_submit_to_queue(&openthread_work_q, &openthread_border_router_work);
536
537 if (error < 0) {
538 LOG_ERR("Failed to submit work to queue, error: %d", error);
539 }
540 }
541 #endif /* CONFIG_OPENTHREAD_ZEPHYR_BORDER_ROUTER */
542
543 #ifdef CONFIG_OPENTHREAD_SYS_INIT
openthread_sys_init(void)544 static int openthread_sys_init(void)
545 {
546 int error = openthread_init();
547
548 if (error == 0) {
549 #ifndef CONFIG_OPENTHREAD_MANUAL_START
550 error = openthread_run();
551 #endif
552 }
553
554 return error;
555 }
556
557 SYS_INIT(openthread_sys_init, POST_KERNEL, CONFIG_OPENTHREAD_SYS_INIT_PRIORITY);
558 #endif /* CONFIG_OPENTHREAD_SYS_INIT */
559