1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_l2_openthread, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
9
10 #include <zephyr/net/net_core.h>
11 #include <zephyr/net/net_pkt.h>
12 #include <zephyr/net/net_mgmt.h>
13 #include <zephyr/net/openthread.h>
14
15 #include <net_private.h>
16
17 #include <zephyr/init.h>
18 #include <zephyr/sys/check.h>
19 #include <zephyr/sys/slist.h>
20 #include <zephyr/sys/util.h>
21 #include <zephyr/sys/__assert.h>
22 #include <version.h>
23
24 #include <openthread/cli.h>
25 #include <openthread/ip6.h>
26 #include <openthread/link.h>
27 #include <openthread/link_raw.h>
28 #include <openthread/ncp.h>
29 #include <openthread/message.h>
30 #include <openthread/platform/diag.h>
31 #include <openthread/tasklet.h>
32 #include <openthread/thread.h>
33 #include <openthread/dataset.h>
34 #include <openthread/joiner.h>
35 #include <openthread-system.h>
36 #include <utils/uart.h>
37
38 #include <platform-zephyr.h>
39
40 #include "openthread_utils.h"
41
42 #define OT_STACK_SIZE (CONFIG_OPENTHREAD_THREAD_STACK_SIZE)
43
44 #if defined(CONFIG_OPENTHREAD_THREAD_PREEMPTIVE)
45 #define OT_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY)
46 #else
47 #define OT_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)
48 #endif
49
50 #if defined(CONFIG_OPENTHREAD_NETWORK_NAME)
51 #define OT_NETWORK_NAME CONFIG_OPENTHREAD_NETWORK_NAME
52 #else
53 #define OT_NETWORK_NAME ""
54 #endif
55
56 #if defined(CONFIG_OPENTHREAD_CHANNEL)
57 #define OT_CHANNEL CONFIG_OPENTHREAD_CHANNEL
58 #else
59 #define OT_CHANNEL 0
60 #endif
61
62 #if defined(CONFIG_OPENTHREAD_PANID)
63 #define OT_PANID CONFIG_OPENTHREAD_PANID
64 #else
65 #define OT_PANID 0
66 #endif
67
68 #if defined(CONFIG_OPENTHREAD_XPANID)
69 #define OT_XPANID CONFIG_OPENTHREAD_XPANID
70 #else
71 #define OT_XPANID ""
72 #endif
73
74 #if defined(CONFIG_OPENTHREAD_NETWORKKEY)
75 #define OT_NETWORKKEY CONFIG_OPENTHREAD_NETWORKKEY
76 #else
77 #define OT_NETWORKKEY ""
78 #endif
79
80 #if defined(CONFIG_OPENTHREAD_JOINER_PSKD)
81 #define OT_JOINER_PSKD CONFIG_OPENTHREAD_JOINER_PSKD
82 #else
83 #define OT_JOINER_PSKD ""
84 #endif
85
86 #if defined(CONFIG_OPENTHREAD_PLATFORM_INFO)
87 #define OT_PLATFORM_INFO CONFIG_OPENTHREAD_PLATFORM_INFO
88 #else
89 #define OT_PLATFORM_INFO ""
90 #endif
91
92 #if defined(CONFIG_OPENTHREAD_POLL_PERIOD)
93 #define OT_POLL_PERIOD CONFIG_OPENTHREAD_POLL_PERIOD
94 #else
95 #define OT_POLL_PERIOD 0
96 #endif
97
98 #define PACKAGE_NAME "Zephyr"
99 #define PACKAGE_VERSION KERNEL_VERSION_STRING
100
101 extern void platformShellInit(otInstance *aInstance);
102
103 K_KERNEL_STACK_DEFINE(ot_stack_area, OT_STACK_SIZE);
104
105 static struct net_linkaddr *ll_addr;
106 static otStateChangedCallback state_changed_cb;
107
openthread_thread_id_get(void)108 k_tid_t openthread_thread_id_get(void)
109 {
110 struct openthread_context *ot_context = openthread_get_default_context();
111
112 return ot_context ? (k_tid_t)&ot_context->work_q.thread : 0;
113 }
114
115 #ifdef CONFIG_NET_MGMT_EVENT
116 static struct net_mgmt_event_callback ip6_addr_cb;
117
ipv6_addr_event_handler(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)118 static void ipv6_addr_event_handler(struct net_mgmt_event_callback *cb,
119 uint32_t mgmt_event, struct net_if *iface)
120 {
121 if (net_if_l2(iface) != &NET_L2_GET_NAME(OPENTHREAD)) {
122 return;
123 }
124
125 #ifdef CONFIG_NET_MGMT_EVENT_INFO
126 struct openthread_context *ot_context = net_if_l2_data(iface);
127
128 if (cb->info == NULL || cb->info_length != sizeof(struct in6_addr)) {
129 return;
130 }
131
132 if (mgmt_event == NET_EVENT_IPV6_ADDR_ADD) {
133 add_ipv6_addr_to_ot(ot_context, (const struct in6_addr *)cb->info);
134 } else if (mgmt_event == NET_EVENT_IPV6_MADDR_ADD) {
135 add_ipv6_maddr_to_ot(ot_context, (const struct in6_addr *)cb->info);
136 }
137 #else
138 NET_WARN("No address info provided with event, "
139 "please enable CONFIG_NET_MGMT_EVENT_INFO");
140 #endif /* CONFIG_NET_MGMT_EVENT_INFO */
141 }
142 #endif /* CONFIG_NET_MGMT_EVENT */
143
ncp_hdlc_send(const uint8_t * buf,uint16_t len)144 static int ncp_hdlc_send(const uint8_t *buf, uint16_t len)
145 {
146 otError err;
147
148 err = otPlatUartSend(buf, len);
149 if (err != OT_ERROR_NONE) {
150 return 0;
151 }
152
153 return len;
154 }
155
otPlatRadioGetIeeeEui64(otInstance * instance,uint8_t * ieee_eui64)156 void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64)
157 {
158 ARG_UNUSED(instance);
159
160 memcpy(ieee_eui64, ll_addr->addr, ll_addr->len);
161 }
162
otTaskletsSignalPending(otInstance * instance)163 void otTaskletsSignalPending(otInstance *instance)
164 {
165 struct openthread_context *ot_context = openthread_get_default_context();
166
167 if (ot_context) {
168 k_work_submit_to_queue(&ot_context->work_q, &ot_context->api_work);
169 }
170 }
171
otSysEventSignalPending(void)172 void otSysEventSignalPending(void)
173 {
174 otTaskletsSignalPending(NULL);
175 }
176
ot_state_changed_handler(uint32_t flags,void * context)177 static void ot_state_changed_handler(uint32_t flags, void *context)
178 {
179 struct openthread_state_changed_cb *entry, *next;
180 struct openthread_context *ot_context = context;
181
182 NET_INFO("State changed! Flags: 0x%08" PRIx32 " Current role: %s",
183 flags,
184 otThreadDeviceRoleToString(otThreadGetDeviceRole(ot_context->instance))
185 );
186
187 if (flags & OT_CHANGED_THREAD_ROLE) {
188 switch (otThreadGetDeviceRole(ot_context->instance)) {
189 case OT_DEVICE_ROLE_CHILD:
190 case OT_DEVICE_ROLE_ROUTER:
191 case OT_DEVICE_ROLE_LEADER:
192 net_if_dormant_off(ot_context->iface);
193 break;
194
195 case OT_DEVICE_ROLE_DISABLED:
196 case OT_DEVICE_ROLE_DETACHED:
197 default:
198 net_if_dormant_on(ot_context->iface);
199 break;
200 }
201 }
202
203 if (flags & OT_CHANGED_IP6_ADDRESS_REMOVED) {
204 NET_DBG("Ipv6 address removed");
205 rm_ipv6_addr_from_zephyr(ot_context);
206 }
207
208 if (flags & OT_CHANGED_IP6_ADDRESS_ADDED) {
209 NET_DBG("Ipv6 address added");
210 add_ipv6_addr_to_zephyr(ot_context);
211 }
212
213 if (flags & OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED) {
214 NET_DBG("Ipv6 multicast address removed");
215 rm_ipv6_maddr_from_zephyr(ot_context);
216 }
217
218 if (flags & OT_CHANGED_IP6_MULTICAST_SUBSCRIBED) {
219 NET_DBG("Ipv6 multicast address added");
220 add_ipv6_maddr_to_zephyr(ot_context);
221 }
222
223 if (state_changed_cb) {
224 state_changed_cb(flags, context);
225 }
226
227 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&ot_context->state_change_cbs, entry, next, node) {
228 if (entry->state_changed_cb != NULL) {
229 entry->state_changed_cb(flags, ot_context, entry->user_data);
230 }
231 }
232 }
233
ot_receive_handler(otMessage * aMessage,void * context)234 static void ot_receive_handler(otMessage *aMessage, void *context)
235 {
236 struct openthread_context *ot_context = context;
237
238 uint16_t offset = 0U;
239 uint16_t read_len;
240 struct net_pkt *pkt;
241 struct net_buf *pkt_buf;
242
243 pkt = net_pkt_rx_alloc_with_buffer(ot_context->iface,
244 otMessageGetLength(aMessage),
245 AF_UNSPEC, 0, K_NO_WAIT);
246 if (!pkt) {
247 NET_ERR("Failed to reserve net pkt");
248 goto out;
249 }
250
251 pkt_buf = pkt->buffer;
252
253 while (1) {
254 read_len = otMessageRead(aMessage, offset, pkt_buf->data,
255 net_buf_tailroom(pkt_buf));
256 if (!read_len) {
257 break;
258 }
259
260 net_buf_add(pkt_buf, read_len);
261
262 offset += read_len;
263
264 if (!net_buf_tailroom(pkt_buf)) {
265 pkt_buf = pkt_buf->frags;
266 if (!pkt_buf) {
267 break;
268 }
269 }
270 }
271
272 NET_DBG("Injecting Ip6 packet to Zephyr net stack");
273
274 if (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
275 net_pkt_hexdump(pkt, "Received IPv6 packet");
276 }
277
278 if (!pkt_list_is_full(ot_context)) {
279 if (pkt_list_add(ot_context, pkt) != 0) {
280 NET_ERR("pkt_list_add failed");
281 goto out;
282 }
283
284 if (net_recv_data(ot_context->iface, pkt) < 0) {
285 NET_ERR("net_recv_data failed");
286 pkt_list_remove_first(ot_context);
287 goto out;
288 }
289
290 pkt = NULL;
291 } else {
292 NET_INFO("Packet list is full");
293 }
294 out:
295 if (pkt) {
296 net_pkt_unref(pkt);
297 }
298
299 otMessageFree(aMessage);
300 }
301
ot_joiner_start_handler(otError error,void * context)302 static void ot_joiner_start_handler(otError error, void *context)
303 {
304 struct openthread_context *ot_context = context;
305
306 switch (error) {
307 case OT_ERROR_NONE:
308 NET_INFO("Join success");
309 otThreadSetEnabled(ot_context->instance, true);
310 break;
311 default:
312 NET_ERR("Join failed [%d]", error);
313 break;
314 }
315 }
316
openthread_process(struct k_work * work)317 static void openthread_process(struct k_work *work)
318 {
319 struct openthread_context *ot_context
320 = CONTAINER_OF(work, struct openthread_context, api_work);
321
322 openthread_api_mutex_lock(ot_context);
323
324 while (otTaskletsArePending(ot_context->instance)) {
325 otTaskletsProcess(ot_context->instance);
326 }
327
328 otSysProcessDrivers(ot_context->instance);
329
330 openthread_api_mutex_unlock(ot_context);
331 }
332
is_ipv6_frag(struct net_pkt * pkt)333 static bool is_ipv6_frag(struct net_pkt *pkt)
334 {
335 NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr);
336 struct net_ipv6_hdr *hdr;
337
338 hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &ipv6_access);
339 if (!hdr) {
340 return false;
341 }
342
343 return hdr->nexthdr == NET_IPV6_NEXTHDR_FRAG ? true : false;
344 }
345
openthread_recv(struct net_if * iface,struct net_pkt * pkt)346 static enum net_verdict openthread_recv(struct net_if *iface, struct net_pkt *pkt)
347 {
348 struct openthread_context *ot_context = net_if_l2_data(iface);
349
350 if (pkt_list_peek(ot_context) == pkt) {
351 pkt_list_remove_last(ot_context);
352 NET_DBG("Got injected Ip6 packet, sending to upper layers");
353
354 if (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
355 net_pkt_hexdump(pkt, "Injected IPv6 packet");
356 }
357
358 if (IS_ENABLED(CONFIG_OPENTHREAD_IP6_FRAGM) && is_ipv6_frag(pkt)) {
359 return NET_DROP;
360 }
361
362 return NET_CONTINUE;
363 }
364
365 NET_DBG("Got 802.15.4 packet, sending to OT");
366
367 if (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
368 net_pkt_hexdump(pkt, "Received 802.15.4 frame");
369 }
370
371 if (notify_new_rx_frame(pkt) != 0) {
372 NET_ERR("Failed to queue RX packet for OpenThread");
373 return NET_DROP;
374 }
375
376 return NET_OK;
377 }
378
openthread_send(struct net_if * iface,struct net_pkt * pkt)379 int openthread_send(struct net_if *iface, struct net_pkt *pkt)
380 {
381 int len = net_pkt_get_len(pkt);
382
383 if (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
384 net_pkt_hexdump(pkt, "IPv6 packet to send");
385 }
386
387 net_capture_pkt(iface, pkt);
388
389 if (notify_new_tx_frame(pkt) != 0) {
390 net_pkt_unref(pkt);
391 }
392
393 return len;
394 }
395
openthread_start(struct openthread_context * ot_context)396 int openthread_start(struct openthread_context *ot_context)
397 {
398 otInstance *ot_instance = ot_context->instance;
399 otError error = OT_ERROR_NONE;
400
401 openthread_api_mutex_lock(ot_context);
402
403 NET_INFO("OpenThread version: %s", otGetVersionString());
404
405 if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
406 NET_DBG("OpenThread co-processor.");
407 goto exit;
408 }
409
410 otIp6SetEnabled(ot_context->instance, true);
411
412 /* Sleepy End Device specific configuration. */
413 if (IS_ENABLED(CONFIG_OPENTHREAD_MTD_SED)) {
414 otLinkModeConfig ot_mode = otThreadGetLinkMode(ot_instance);
415
416 /* A SED should always attach the network as a SED to indicate
417 * increased buffer requirement to a parent.
418 */
419 ot_mode.mRxOnWhenIdle = false;
420
421 otThreadSetLinkMode(ot_context->instance, ot_mode);
422 otLinkSetPollPeriod(ot_context->instance, OT_POLL_PERIOD);
423 }
424
425 if (otDatasetIsCommissioned(ot_instance)) {
426 /* OpenThread already has dataset stored - skip the
427 * configuration.
428 */
429 NET_DBG("OpenThread already commissioned.");
430 } else if (IS_ENABLED(CONFIG_OPENTHREAD_JOINER_AUTOSTART)) {
431 /* No dataset - initiate network join procedure. */
432 NET_DBG("Starting OpenThread join procedure.");
433
434 error = otJoinerStart(ot_instance, OT_JOINER_PSKD, NULL,
435 PACKAGE_NAME, OT_PLATFORM_INFO,
436 PACKAGE_VERSION, NULL,
437 &ot_joiner_start_handler, ot_context);
438
439 if (error != OT_ERROR_NONE) {
440 NET_ERR("Failed to start joiner [%d]", error);
441 }
442
443 goto exit;
444 } else {
445 /* No dataset - load the default configuration. */
446 NET_DBG("Loading OpenThread default configuration.");
447
448 otExtendedPanId xpanid;
449 otNetworkKey networkKey;
450
451 otThreadSetNetworkName(ot_instance, OT_NETWORK_NAME);
452 otLinkSetChannel(ot_instance, OT_CHANNEL);
453 otLinkSetPanId(ot_instance, OT_PANID);
454 net_bytes_from_str(xpanid.m8, 8, (char *)OT_XPANID);
455 otThreadSetExtendedPanId(ot_instance, &xpanid);
456
457 if (strlen(OT_NETWORKKEY)) {
458 net_bytes_from_str(networkKey.m8, OT_NETWORK_KEY_SIZE,
459 (char *)OT_NETWORKKEY);
460 otThreadSetNetworkKey(ot_instance, &networkKey);
461 }
462 }
463
464 NET_INFO("Network name: %s",
465 otThreadGetNetworkName(ot_instance));
466
467 /* Start the network. */
468 error = otThreadSetEnabled(ot_instance, true);
469 if (error != OT_ERROR_NONE) {
470 NET_ERR("Failed to start the OpenThread network [%d]", error);
471 }
472
473 exit:
474
475 openthread_api_mutex_unlock(ot_context);
476
477 return error == OT_ERROR_NONE ? 0 : -EIO;
478 }
479
openthread_stop(struct openthread_context * ot_context)480 int openthread_stop(struct openthread_context *ot_context)
481 {
482 otError error;
483
484 if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
485 return 0;
486 }
487
488 openthread_api_mutex_lock(ot_context);
489
490 error = otThreadSetEnabled(ot_context->instance, false);
491 if (error == OT_ERROR_INVALID_STATE) {
492 NET_DBG("Openthread interface was not up [%d]", error);
493 }
494
495 openthread_api_mutex_unlock(ot_context);
496
497 return 0;
498 }
499
openthread_init(struct net_if * iface)500 static int openthread_init(struct net_if *iface)
501 {
502 struct openthread_context *ot_context = net_if_l2_data(iface);
503 struct k_work_queue_config q_cfg = {
504 .name = "openthread",
505 .no_yield = true,
506 };
507 otError err;
508
509 NET_DBG("openthread_init");
510
511 k_mutex_init(&ot_context->api_lock);
512 k_work_init(&ot_context->api_work, openthread_process);
513
514 ll_addr = net_if_get_link_addr(iface);
515
516 openthread_api_mutex_lock(ot_context);
517
518 otSysInit(0, NULL);
519
520 ot_context->instance = otInstanceInitSingle();
521 ot_context->iface = iface;
522
523 __ASSERT(ot_context->instance, "OT instance is NULL");
524
525 if (IS_ENABLED(CONFIG_OPENTHREAD_SHELL)) {
526 platformShellInit(ot_context->instance);
527 }
528
529 if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
530 otPlatUartEnable();
531 otNcpHdlcInit(ot_context->instance, ncp_hdlc_send);
532 } else {
533 otIp6SetReceiveFilterEnabled(ot_context->instance, true);
534 otIp6SetReceiveCallback(ot_context->instance,
535 ot_receive_handler, ot_context);
536 sys_slist_init(&ot_context->state_change_cbs);
537 err = otSetStateChangedCallback(ot_context->instance,
538 &ot_state_changed_handler,
539 ot_context);
540 if (err != OT_ERROR_NONE) {
541 NET_ERR("Could not set state changed callback: %d", err);
542 }
543
544 net_mgmt_init_event_callback(
545 &ip6_addr_cb, ipv6_addr_event_handler,
546 NET_EVENT_IPV6_ADDR_ADD | NET_EVENT_IPV6_MADDR_ADD);
547 net_mgmt_add_event_callback(&ip6_addr_cb);
548
549 net_if_dormant_on(iface);
550 }
551
552 openthread_api_mutex_unlock(ot_context);
553
554 k_work_queue_start(&ot_context->work_q, ot_stack_area,
555 K_KERNEL_STACK_SIZEOF(ot_stack_area),
556 OT_PRIORITY, &q_cfg);
557
558 (void)k_work_submit_to_queue(&ot_context->work_q, &ot_context->api_work);
559
560 return 0;
561 }
562
ieee802154_init(struct net_if * iface)563 void ieee802154_init(struct net_if *iface)
564 {
565 if (IS_ENABLED(CONFIG_IEEE802154_NET_IF_NO_AUTO_START)) {
566 LOG_DBG("Interface auto start disabled.");
567 net_if_flag_set(iface, NET_IF_NO_AUTO_START);
568 }
569
570 net_if_flag_set(iface, NET_IF_IPV6_NO_ND);
571 net_if_flag_set(iface, NET_IF_IPV6_NO_MLD);
572
573 openthread_init(iface);
574 }
575
openthread_flags(struct net_if * iface)576 static enum net_l2_flags openthread_flags(struct net_if *iface)
577 {
578 /* TODO: Should report NET_L2_PROMISC_MODE if the radio driver
579 * reports the IEEE802154_HW_PROMISC capability.
580 */
581 return NET_L2_MULTICAST | NET_L2_MULTICAST_SKIP_JOIN_SOLICIT_NODE;
582 }
583
openthread_enable(struct net_if * iface,bool state)584 static int openthread_enable(struct net_if *iface, bool state)
585 {
586 struct openthread_context *ot_context = net_if_l2_data(iface);
587
588 NET_DBG("iface %p %s", iface, state ? "up" : "down");
589
590 if (state) {
591 if (IS_ENABLED(CONFIG_OPENTHREAD_MANUAL_START)) {
592 NET_DBG("OpenThread manual start.");
593 return 0;
594 }
595
596 return openthread_start(ot_context);
597 }
598
599 return openthread_stop(ot_context);
600 }
601
openthread_get_default_context(void)602 struct openthread_context *openthread_get_default_context(void)
603 {
604 struct net_if *iface;
605 struct openthread_context *ot_context = NULL;
606
607 iface = net_if_get_first_by_type(&NET_L2_GET_NAME(OPENTHREAD));
608 if (!iface) {
609 NET_ERR("There is no net interface for OpenThread");
610 goto exit;
611 }
612
613 ot_context = net_if_l2_data(iface);
614 if (!ot_context) {
615 NET_ERR("There is no Openthread context in net interface data");
616 goto exit;
617 }
618
619 exit:
620 return ot_context;
621 }
622
openthread_get_default_instance(void)623 struct otInstance *openthread_get_default_instance(void)
624 {
625 struct openthread_context *ot_context =
626 openthread_get_default_context();
627
628 return ot_context ? ot_context->instance : NULL;
629 }
630
openthread_state_changed_cb_register(struct openthread_context * ot_context,struct openthread_state_changed_cb * cb)631 int openthread_state_changed_cb_register(struct openthread_context *ot_context,
632 struct openthread_state_changed_cb *cb)
633 {
634 CHECKIF(cb == NULL || cb->state_changed_cb == NULL) {
635 return -EINVAL;
636 }
637
638 openthread_api_mutex_lock(ot_context);
639 sys_slist_append(&ot_context->state_change_cbs, &cb->node);
640 openthread_api_mutex_unlock(ot_context);
641
642 return 0;
643 }
644
openthread_state_changed_cb_unregister(struct openthread_context * ot_context,struct openthread_state_changed_cb * cb)645 int openthread_state_changed_cb_unregister(struct openthread_context *ot_context,
646 struct openthread_state_changed_cb *cb)
647 {
648 bool removed;
649
650 CHECKIF(cb == NULL) {
651 return -EINVAL;
652 }
653
654 openthread_api_mutex_lock(ot_context);
655 removed = sys_slist_find_and_remove(&ot_context->state_change_cbs, &cb->node);
656 openthread_api_mutex_unlock(ot_context);
657
658 if (!removed) {
659 return -EALREADY;
660 }
661
662 return 0;
663 }
664
openthread_set_state_changed_cb(otStateChangedCallback cb)665 void openthread_set_state_changed_cb(otStateChangedCallback cb)
666 {
667 state_changed_cb = cb;
668 }
669
openthread_api_mutex_lock(struct openthread_context * ot_context)670 void openthread_api_mutex_lock(struct openthread_context *ot_context)
671 {
672 (void)k_mutex_lock(&ot_context->api_lock, K_FOREVER);
673 }
674
openthread_api_mutex_try_lock(struct openthread_context * ot_context)675 int openthread_api_mutex_try_lock(struct openthread_context *ot_context)
676 {
677 return k_mutex_lock(&ot_context->api_lock, K_NO_WAIT);
678 }
679
openthread_api_mutex_unlock(struct openthread_context * ot_context)680 void openthread_api_mutex_unlock(struct openthread_context *ot_context)
681 {
682 (void)k_mutex_unlock(&ot_context->api_lock);
683 }
684
685 NET_L2_INIT(OPENTHREAD_L2, openthread_recv, openthread_send, openthread_enable,
686 openthread_flags);
687