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