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