1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file
8 * @brief IEEE 802.15.4 shell module
9 */
10
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(net_ieee802154_shell, CONFIG_NET_L2_IEEE802154_LOG_LEVEL);
13
14 #include <zephyr/kernel.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <zephyr/shell/shell.h>
18 #include <zephyr/sys/printk.h>
19
20 #include <zephyr/net/net_if.h>
21 #include <zephyr/net/ieee802154_mgmt.h>
22
23 #include "ieee802154_frame.h"
24
25 #define EXT_ADDR_STR_SIZE sizeof("xx:xx:xx:xx:xx:xx:xx:xx")
26 #define EXT_ADDR_STR_LEN (EXT_ADDR_STR_SIZE - 1U)
27
28 struct ieee802154_req_params params;
29 static struct net_mgmt_event_callback scan_cb;
30 static const struct shell *cb_shell;
31
cmd_ieee802154_ack(const struct shell * sh,size_t argc,char * argv[])32 static int cmd_ieee802154_ack(const struct shell *sh,
33 size_t argc, char *argv[])
34 {
35 struct net_if *iface = net_if_get_ieee802154();
36
37 ARG_UNUSED(argc);
38
39 if (!iface) {
40 shell_fprintf(sh, SHELL_INFO,
41 "No IEEE 802.15.4 interface found.\n");
42 return -ENOEXEC;
43 }
44
45 if (!strcmp(argv[1], "set") || !strcmp(argv[1], "1")) {
46 net_mgmt(NET_REQUEST_IEEE802154_SET_ACK, iface, NULL, 0);
47 shell_fprintf(sh, SHELL_NORMAL,
48 "ACK flag set on outgoing packets\n");
49
50 return 0;
51 }
52
53 if (!strcmp(argv[1], "unset") || !strcmp(argv[1], "0")) {
54 net_mgmt(NET_REQUEST_IEEE802154_UNSET_ACK, iface, NULL, 0);
55 shell_fprintf(sh, SHELL_NORMAL,
56 "ACK flag unset on outgoing packets\n");
57
58 return 0;
59 }
60
61 return 0;
62 }
63
64 /**
65 * Parse a string representing an extended address in ASCII HEX
66 * format into a big endian binary representation of the address.
67 *
68 * @param addr Extended address as a string.
69 * @param ext_addr Extended address in big endian byte ordering.
70 *
71 * @return 0 on success, negative error code otherwise
72 */
parse_extended_address(char * addr,uint8_t * ext_addr)73 static inline int parse_extended_address(char *addr, uint8_t *ext_addr)
74 {
75 return net_bytes_from_str(ext_addr, IEEE802154_EXT_ADDR_LENGTH, addr);
76 }
77
cmd_ieee802154_associate(const struct shell * sh,size_t argc,char * argv[])78 static int cmd_ieee802154_associate(const struct shell *sh,
79 size_t argc, char *argv[])
80 {
81 struct net_if *iface = net_if_get_ieee802154();
82 char ext_addr[EXT_ADDR_STR_SIZE] = {0};
83
84 if (argc < 3) {
85 shell_help(sh);
86 return -ENOEXEC;
87 }
88
89 if (!iface) {
90 shell_fprintf(sh, SHELL_INFO,
91 "No IEEE 802.15.4 interface found.\n");
92 return -ENOEXEC;
93 }
94
95 if (strlen(argv[2]) > EXT_ADDR_STR_LEN) {
96 shell_fprintf(sh, SHELL_INFO, "Address too long\n");
97 return -ENOEXEC;
98 }
99
100 params = (struct ieee802154_req_params){0};
101 params.pan_id = atoi(argv[1]);
102 strncpy(ext_addr, argv[2], EXT_ADDR_STR_LEN);
103
104 if (strlen(ext_addr) == EXT_ADDR_STR_LEN) {
105 if (parse_extended_address(ext_addr, params.addr) < 0) {
106 shell_fprintf(sh, SHELL_INFO,
107 "Failed to parse extended address\n");
108 return -ENOEXEC;
109 }
110 params.len = IEEE802154_EXT_ADDR_LENGTH;
111 } else {
112 params.short_addr = (uint16_t) atoi(ext_addr);
113 params.len = IEEE802154_SHORT_ADDR_LENGTH;
114 }
115
116 if (net_mgmt(NET_REQUEST_IEEE802154_ASSOCIATE, iface,
117 ¶ms, sizeof(struct ieee802154_req_params))) {
118 shell_fprintf(sh, SHELL_WARNING,
119 "Could not associate to %s on PAN ID %u\n",
120 argv[2], params.pan_id);
121
122 return -ENOEXEC;
123 } else {
124 shell_fprintf(sh, SHELL_NORMAL,
125 "Associated to PAN ID %u\n", params.pan_id);
126 }
127
128 return 0;
129 }
130
cmd_ieee802154_disassociate(const struct shell * sh,size_t argc,char * argv[])131 static int cmd_ieee802154_disassociate(const struct shell *sh,
132 size_t argc, char *argv[])
133 {
134 struct net_if *iface = net_if_get_ieee802154();
135 int ret;
136
137 ARG_UNUSED(argc);
138 ARG_UNUSED(argv);
139
140 if (!iface) {
141 shell_fprintf(sh, SHELL_INFO,
142 "No IEEE 802.15.4 interface found.\n");
143 return -ENOEXEC;
144 }
145
146 ret = net_mgmt(NET_REQUEST_IEEE802154_DISASSOCIATE, iface, NULL, 0);
147 if (ret == -EALREADY) {
148 shell_fprintf(sh, SHELL_INFO,
149 "Interface is not associated\n");
150
151 return -ENOEXEC;
152 } else if (ret) {
153 shell_fprintf(sh, SHELL_WARNING,
154 "Could not disassociate? (status: %i)\n", ret);
155
156 return -ENOEXEC;
157 } else {
158 shell_fprintf(sh, SHELL_NORMAL,
159 "Interface is now disassociated\n");
160 }
161
162 return 0;
163 }
164
parse_channel_set(char * str_set)165 static inline uint32_t parse_channel_set(char *str_set)
166 {
167 uint32_t channel_set = 0U;
168 char *p, *n;
169
170 p = str_set;
171
172 do {
173 uint32_t chan;
174
175 n = strchr(p, ':');
176 if (n) {
177 *n = '\0';
178 }
179
180 chan = atoi(p);
181 if (chan > 0 && chan < 32) {
182 channel_set |= BIT(chan - 1);
183 }
184
185 p = n ? n + 1 : n;
186 } while (n);
187
188 return channel_set;
189 }
190
print_coordinator_address(char * buf,int buf_len)191 static inline char *print_coordinator_address(char *buf, int buf_len)
192 {
193 if (params.len == IEEE802154_EXT_ADDR_LENGTH) {
194 int i, pos = 0;
195
196 pos += snprintk(buf + pos, buf_len - pos, "(extended) ");
197
198 for (i = 0; i < IEEE802154_EXT_ADDR_LENGTH; i++) {
199 pos += snprintk(buf + pos, buf_len - pos,
200 "%02X:", params.addr[i]);
201 }
202
203 buf[pos - 1] = '\0';
204 } else {
205 snprintk(buf, buf_len, "(short) %u", params.short_addr);
206 }
207
208 return buf;
209 }
210
scan_result_cb(struct net_mgmt_event_callback * cb,uint32_t mgmt_event,struct net_if * iface)211 static void scan_result_cb(struct net_mgmt_event_callback *cb,
212 uint32_t mgmt_event, struct net_if *iface)
213 {
214 char buf[64];
215
216 shell_fprintf(cb_shell, SHELL_NORMAL,
217 "Channel: %u\tPAN ID: %u\tCoordinator Address: %s\t "
218 "LQI: %u Associable: %s\n", params.channel, params.pan_id,
219 print_coordinator_address(buf, sizeof(buf)), params.lqi,
220 params.association_permitted ? "yes" : "no");
221 }
222
cmd_ieee802154_scan(const struct shell * sh,size_t argc,char * argv[])223 static int cmd_ieee802154_scan(const struct shell *sh,
224 size_t argc, char *argv[])
225 {
226 struct net_if *iface = net_if_get_ieee802154();
227 uint32_t scan_type;
228 int ret = 0;
229
230 if (argc < 3) {
231 shell_help(sh);
232 return -ENOEXEC;
233 }
234
235 if (!iface) {
236 shell_fprintf(sh, SHELL_INFO,
237 "No IEEE 802.15.4 interface found.\n");
238 return -ENOEXEC;
239 }
240
241 params = (struct ieee802154_req_params){0};
242
243 net_mgmt_init_event_callback(&scan_cb, scan_result_cb,
244 NET_EVENT_IEEE802154_SCAN_RESULT);
245 net_mgmt_add_event_callback(&scan_cb);
246
247 if (!strcmp(argv[1], "active")) {
248 scan_type = NET_REQUEST_IEEE802154_ACTIVE_SCAN;
249 } else if (!strcmp(argv[1], "passive")) {
250 scan_type = NET_REQUEST_IEEE802154_PASSIVE_SCAN;
251 } else {
252 ret = -ENOEXEC;
253 goto release_event_cb;
254 }
255
256 if (!strcmp(argv[2], "all")) {
257 params.channel_set = IEEE802154_ALL_CHANNELS;
258 } else {
259 params.channel_set = parse_channel_set(argv[2]);
260 }
261
262 if (!params.channel_set) {
263 ret = -ENOEXEC;
264 goto release_event_cb;
265 }
266
267 params.duration = atoi(argv[3]);
268
269 shell_fprintf(sh, SHELL_NORMAL,
270 "%s Scanning (channel set: 0x%08x, duration %u ms)...\n",
271 scan_type == NET_REQUEST_IEEE802154_ACTIVE_SCAN ?
272 "Active" : "Passive", params.channel_set,
273 params.duration);
274
275 cb_shell = sh;
276
277 if (scan_type == NET_REQUEST_IEEE802154_ACTIVE_SCAN) {
278 ret = net_mgmt(NET_REQUEST_IEEE802154_ACTIVE_SCAN, iface,
279 ¶ms, sizeof(struct ieee802154_req_params));
280 } else {
281 ret = net_mgmt(NET_REQUEST_IEEE802154_PASSIVE_SCAN, iface,
282 ¶ms, sizeof(struct ieee802154_req_params));
283 }
284
285 if (ret) {
286 shell_fprintf(sh, SHELL_WARNING,
287 "Could not raise a scan (status: %i)\n", ret);
288
289 ret = -ENOEXEC;
290 goto release_event_cb;
291 } else {
292 shell_fprintf(sh, SHELL_NORMAL,
293 "Done\n");
294 }
295
296 release_event_cb:
297 net_mgmt_del_event_callback(&scan_cb);
298 return ret;
299 }
300
cmd_ieee802154_set_chan(const struct shell * sh,size_t argc,char * argv[])301 static int cmd_ieee802154_set_chan(const struct shell *sh,
302 size_t argc, char *argv[])
303 {
304 struct net_if *iface = net_if_get_ieee802154();
305 uint16_t channel;
306
307 if (argc < 2) {
308 shell_help(sh);
309 return -ENOEXEC;
310 }
311
312 if (!iface) {
313 shell_fprintf(sh, SHELL_INFO,
314 "No IEEE 802.15.4 interface found.\n");
315 return -ENOEXEC;
316 }
317
318 channel = (uint16_t)atoi(argv[1]);
319
320 if (net_mgmt(NET_REQUEST_IEEE802154_SET_CHANNEL, iface,
321 &channel, sizeof(uint16_t))) {
322 shell_fprintf(sh, SHELL_WARNING,
323 "Could not set channel %u\n", channel);
324
325 return -ENOEXEC;
326 } else {
327 shell_fprintf(sh, SHELL_NORMAL,
328 "Channel %u set\n", channel);
329 }
330
331 return 0;
332 }
333
cmd_ieee802154_get_chan(const struct shell * sh,size_t argc,char * argv[])334 static int cmd_ieee802154_get_chan(const struct shell *sh,
335 size_t argc, char *argv[])
336 {
337 struct net_if *iface = net_if_get_ieee802154();
338 uint16_t channel;
339
340 ARG_UNUSED(argc);
341 ARG_UNUSED(argv);
342
343 if (!iface) {
344 shell_fprintf(sh, SHELL_INFO,
345 "No IEEE 802.15.4 interface found.\n");
346 return -ENOEXEC;
347 }
348
349 if (net_mgmt(NET_REQUEST_IEEE802154_GET_CHANNEL, iface,
350 &channel, sizeof(uint16_t))) {
351 shell_fprintf(sh, SHELL_WARNING,
352 "Could not get channel\n");
353
354 return -ENOEXEC;
355 } else {
356 shell_fprintf(sh, SHELL_NORMAL,
357 "Channel %u\n", channel);
358 }
359
360 return 0;
361 }
362
cmd_ieee802154_set_pan_id(const struct shell * sh,size_t argc,char * argv[])363 static int cmd_ieee802154_set_pan_id(const struct shell *sh,
364 size_t argc, char *argv[])
365 {
366 struct net_if *iface = net_if_get_ieee802154();
367 uint16_t pan_id;
368
369 ARG_UNUSED(argc);
370
371 if (argc < 2) {
372 shell_help(sh);
373 return -ENOEXEC;
374 }
375
376 if (!iface) {
377 shell_fprintf(sh, SHELL_INFO,
378 "No IEEE 802.15.4 interface found.\n");
379 return -ENOEXEC;
380 }
381
382 pan_id = (uint16_t)atoi(argv[1]);
383
384 if (net_mgmt(NET_REQUEST_IEEE802154_SET_PAN_ID, iface,
385 &pan_id, sizeof(uint16_t))) {
386 shell_fprintf(sh, SHELL_WARNING,
387 "Could not set PAN ID %u\n", pan_id);
388
389 return -ENOEXEC;
390 } else {
391 shell_fprintf(sh, SHELL_NORMAL,
392 "PAN ID %u set\n", pan_id);
393 }
394
395 return 0;
396 }
397
cmd_ieee802154_get_pan_id(const struct shell * sh,size_t argc,char * argv[])398 static int cmd_ieee802154_get_pan_id(const struct shell *sh,
399 size_t argc, char *argv[])
400 {
401 struct net_if *iface = net_if_get_ieee802154();
402 uint16_t pan_id;
403
404 ARG_UNUSED(argc);
405 ARG_UNUSED(argv);
406
407 if (!iface) {
408 shell_fprintf(sh, SHELL_INFO,
409 "No IEEE 802.15.4 interface found.\n");
410 return -ENOEXEC;
411 }
412
413 if (net_mgmt(NET_REQUEST_IEEE802154_GET_PAN_ID, iface,
414 &pan_id, sizeof(uint16_t))) {
415 shell_fprintf(sh, SHELL_WARNING,
416 "Could not get PAN ID\n");
417
418 return -ENOEXEC;
419 } else {
420 shell_fprintf(sh, SHELL_NORMAL,
421 "PAN ID %u (0x%x)\n", pan_id, pan_id);
422 }
423
424 return 0;
425 }
426
cmd_ieee802154_set_ext_addr(const struct shell * sh,size_t argc,char * argv[])427 static int cmd_ieee802154_set_ext_addr(const struct shell *sh,
428 size_t argc, char *argv[])
429 {
430 struct net_if *iface = net_if_get_ieee802154();
431 uint8_t addr[IEEE802154_EXT_ADDR_LENGTH]; /* in big endian */
432
433 if (argc < 2) {
434 shell_help(sh);
435 return -ENOEXEC;
436 }
437
438 if (!iface) {
439 shell_fprintf(sh, SHELL_INFO,
440 "No IEEE 802.15.4 interface found.\n");
441 return -ENOEXEC;
442 }
443
444 if (strlen(argv[1]) != EXT_ADDR_STR_LEN) {
445 shell_fprintf(sh, SHELL_INFO,
446 "%zd characters needed\n", EXT_ADDR_STR_LEN);
447 return -ENOEXEC;
448 }
449
450 if (parse_extended_address(argv[1], addr) < 0) {
451 shell_fprintf(sh, SHELL_INFO,
452 "Failed to parse extended address\n");
453 return -ENOEXEC;
454 }
455
456 if (net_mgmt(NET_REQUEST_IEEE802154_SET_EXT_ADDR, iface,
457 addr, IEEE802154_EXT_ADDR_LENGTH)) {
458 shell_fprintf(sh, SHELL_WARNING,
459 "Could not set extended address\n");
460
461 return -ENOEXEC;
462 } else {
463 shell_fprintf(sh, SHELL_NORMAL,
464 "Extended address set\n");
465 }
466
467 return 0;
468 }
469
cmd_ieee802154_get_ext_addr(const struct shell * sh,size_t argc,char * argv[])470 static int cmd_ieee802154_get_ext_addr(const struct shell *sh,
471 size_t argc, char *argv[])
472 {
473 struct net_if *iface = net_if_get_ieee802154();
474 uint8_t addr[IEEE802154_EXT_ADDR_LENGTH]; /* in big endian */
475
476 if (!iface) {
477 shell_fprintf(sh, SHELL_INFO,
478 "No IEEE 802.15.4 interface found.\n");
479 return -ENOEXEC;
480 }
481
482 if (net_mgmt(NET_REQUEST_IEEE802154_GET_EXT_ADDR, iface,
483 addr, IEEE802154_EXT_ADDR_LENGTH)) {
484 shell_fprintf(sh, SHELL_WARNING,
485 "Could not get extended address\n");
486 return -ENOEXEC;
487 } else {
488 static char ext_addr[EXT_ADDR_STR_SIZE];
489 int i, pos = 0;
490
491 for (i = 0; i < IEEE802154_EXT_ADDR_LENGTH; i++) {
492 pos += snprintk(ext_addr + pos,
493 EXT_ADDR_STR_SIZE - pos,
494 "%02X:", addr[i]);
495 }
496
497 ext_addr[EXT_ADDR_STR_SIZE - 1] = '\0';
498
499 shell_fprintf(sh, SHELL_NORMAL,
500 "Extended address: %s\n", ext_addr);
501 }
502
503 return 0;
504 }
505
cmd_ieee802154_set_short_addr(const struct shell * sh,size_t argc,char * argv[])506 static int cmd_ieee802154_set_short_addr(const struct shell *sh,
507 size_t argc, char *argv[])
508 {
509 struct net_if *iface = net_if_get_ieee802154();
510 uint16_t short_addr; /* in CPU byte order */
511
512 if (argc < 2) {
513 shell_help(sh);
514 return -ENOEXEC;
515 }
516
517 if (!iface) {
518 shell_fprintf(sh, SHELL_INFO,
519 "No IEEE 802.15.4 interface found.\n");
520 return -ENOEXEC;
521 }
522
523 short_addr = (uint16_t)atoi(argv[1]);
524
525 if (net_mgmt(NET_REQUEST_IEEE802154_SET_SHORT_ADDR, iface,
526 &short_addr, sizeof(uint16_t))) {
527 shell_fprintf(sh, SHELL_WARNING,
528 "Could not set short address %u\n", short_addr);
529
530 return -ENOEXEC;
531 } else {
532 shell_fprintf(sh, SHELL_NORMAL,
533 "Short address %u set\n", short_addr);
534 }
535
536 return 0;
537 }
538
cmd_ieee802154_get_short_addr(const struct shell * sh,size_t argc,char * argv[])539 static int cmd_ieee802154_get_short_addr(const struct shell *sh,
540 size_t argc, char *argv[])
541 {
542 struct net_if *iface = net_if_get_ieee802154();
543 uint16_t short_addr; /* in CPU byte order */
544
545 if (!iface) {
546 shell_fprintf(sh, SHELL_INFO,
547 "No IEEE 802.15.4 interface found.\n");
548 return -ENOEXEC;
549 }
550
551 if (net_mgmt(NET_REQUEST_IEEE802154_GET_SHORT_ADDR, iface,
552 &short_addr, sizeof(uint16_t))) {
553 shell_fprintf(sh, SHELL_WARNING,
554 "Could not get short address\n");
555
556 return -ENOEXEC;
557 } else {
558 shell_fprintf(sh, SHELL_NORMAL,
559 "Short address %u\n", short_addr);
560 }
561
562 return 0;
563 }
564
cmd_ieee802154_set_tx_power(const struct shell * sh,size_t argc,char * argv[])565 static int cmd_ieee802154_set_tx_power(const struct shell *sh,
566 size_t argc, char *argv[])
567 {
568 struct net_if *iface = net_if_get_ieee802154();
569 int16_t tx_power;
570
571 if (argc < 2) {
572 shell_help(sh);
573 return -ENOEXEC;
574 }
575
576 if (!iface) {
577 shell_fprintf(sh, SHELL_INFO,
578 "No IEEE 802.15.4 interface found.\n");
579 return -ENOEXEC;
580 }
581
582 tx_power = (int16_t)atoi(argv[1]);
583
584 if (net_mgmt(NET_REQUEST_IEEE802154_SET_TX_POWER, iface,
585 &tx_power, sizeof(int16_t))) {
586 shell_fprintf(sh, SHELL_WARNING,
587 "Could not set TX power %d\n", tx_power);
588
589 return -ENOEXEC;
590 } else {
591 shell_fprintf(sh, SHELL_NORMAL,
592 "TX power %d set\n", tx_power);
593 }
594
595 return 0;
596 }
597
cmd_ieee802154_get_tx_power(const struct shell * sh,size_t argc,char * argv[])598 static int cmd_ieee802154_get_tx_power(const struct shell *sh,
599 size_t argc, char *argv[])
600 {
601 struct net_if *iface = net_if_get_ieee802154();
602 int16_t tx_power;
603
604 if (!iface) {
605 shell_fprintf(sh, SHELL_INFO,
606 "No IEEE 802.15.4 interface found.\n");
607 return -ENOEXEC;
608 }
609
610 if (net_mgmt(NET_REQUEST_IEEE802154_GET_TX_POWER, iface,
611 &tx_power, sizeof(int16_t))) {
612 shell_fprintf(sh, SHELL_WARNING,
613 "Could not get TX power\n");
614
615 return -ENOEXEC;
616 } else {
617 shell_fprintf(sh, SHELL_NORMAL,
618 "TX power (in dbm) %d\n", tx_power);
619 }
620
621 return 0;
622 }
623
624 SHELL_STATIC_SUBCMD_SET_CREATE(ieee802154_commands,
625 SHELL_CMD(ack, NULL,
626 "<set/1 | unset/0> Set auto-ack flag",
627 cmd_ieee802154_ack),
628 SHELL_CMD(associate, NULL,
629 "<pan_id> <PAN coordinator short or long address (EUI-64)>",
630 cmd_ieee802154_associate),
631 SHELL_CMD(disassociate, NULL,
632 "Disassociate from network",
633 cmd_ieee802154_disassociate),
634 SHELL_CMD(get_chan, NULL,
635 "Get currently used channel",
636 cmd_ieee802154_get_chan),
637 SHELL_CMD(get_ext_addr, NULL,
638 "Get currently used extended address",
639 cmd_ieee802154_get_ext_addr),
640 SHELL_CMD(get_pan_id, NULL,
641 "Get currently used PAN id",
642 cmd_ieee802154_get_pan_id),
643 SHELL_CMD(get_short_addr, NULL,
644 "Get currently used short address",
645 cmd_ieee802154_get_short_addr),
646 SHELL_CMD(get_tx_power, NULL,
647 "Get currently used TX power",
648 cmd_ieee802154_get_tx_power),
649 SHELL_CMD(scan, NULL,
650 "<passive|active> <channels set n[:m:...]:x|all>"
651 " <per-channel duration in ms>",
652 cmd_ieee802154_scan),
653 SHELL_CMD(set_chan, NULL,
654 "<channel> Set used channel",
655 cmd_ieee802154_set_chan),
656 SHELL_CMD(set_ext_addr, NULL,
657 "<long/extended address (EUI-64)> Set extended address",
658 cmd_ieee802154_set_ext_addr),
659 SHELL_CMD(set_pan_id, NULL,
660 "<pan_id> Set used PAN id",
661 cmd_ieee802154_set_pan_id),
662 SHELL_CMD(set_short_addr, NULL,
663 "<short address> Set short address",
664 cmd_ieee802154_set_short_addr),
665 SHELL_CMD(set_tx_power, NULL,
666 "<-18/-7/-4/-2/0/1/2/3/5> Set TX power",
667 cmd_ieee802154_set_tx_power),
668 SHELL_SUBCMD_SET_END
669 );
670
671 SHELL_CMD_REGISTER(ieee802154, &ieee802154_commands, "IEEE 802.15.4 commands",
672 NULL);
673