]> git.ipfire.org Git - thirdparty/iw.git/blob - phy.c
add "channels" PHY command listing frequencies with more details
[thirdparty/iw.git] / phy.c
1 #include <stdbool.h>
2 #include <errno.h>
3 #include <strings.h>
4 #include <sys/param.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7
8 #include <netlink/genl/genl.h>
9 #include <netlink/genl/family.h>
10 #include <netlink/genl/ctrl.h>
11 #include <netlink/msg.h>
12 #include <netlink/attr.h>
13
14 #include "nl80211.h"
15 #include "iw.h"
16
17 struct channels_ctx {
18 int last_band;
19 bool width_40;
20 bool width_80;
21 bool width_160;
22 };
23
24 static char *dfs_state_name(enum nl80211_dfs_state state)
25 {
26 switch (state) {
27 case NL80211_DFS_USABLE:
28 return "usable";
29 case NL80211_DFS_AVAILABLE:
30 return "available";
31 case NL80211_DFS_UNAVAILABLE:
32 return "unavailable";
33 default:
34 return "unknown";
35 }
36 }
37
38 static int print_channels_handler(struct nl_msg *msg, void *arg)
39 {
40 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
41 struct channels_ctx *ctx = arg;
42 struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
43 struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
44 struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
45 struct nlattr *nl_band;
46 struct nlattr *nl_freq;
47 int rem_band, rem_freq;
48
49 nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
50
51 if (tb_msg[NL80211_ATTR_WIPHY_BANDS]) {
52 nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
53 if (ctx->last_band != nl_band->nla_type) {
54 printf("Band %d:\n", nl_band->nla_type + 1);
55 ctx->width_40 = false;
56 ctx->width_80 = false;
57 ctx->width_160 = false;
58 ctx->last_band = nl_band->nla_type;
59 }
60
61 nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), nla_len(nl_band), NULL);
62
63 if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
64 __u16 cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
65
66 if (cap & BIT(1))
67 ctx->width_40 = true;
68 }
69
70 if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) {
71 __u32 capa;
72
73 ctx->width_80 = true;
74
75 capa = nla_get_u32(tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
76 switch ((capa >> 2) & 3) {
77 case 2:
78 /* width_80p80 = true; */
79 /* fall through */
80 case 1:
81 ctx->width_160 = true;
82 break;
83 }
84 }
85
86 if (tb_band[NL80211_BAND_ATTR_FREQS]) {
87 nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
88 uint32_t freq;
89
90 nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), nla_len(nl_freq), NULL);
91
92 if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
93 continue;
94 freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
95 printf("\t* %d MHz [%d] ", freq, ieee80211_frequency_to_channel(freq));
96
97 if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) {
98 printf("(disabled)\n");
99 continue;
100 }
101 printf("\n");
102
103 if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
104 printf("\t Maximum TX power: %.1f dBm\n", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]));
105
106 /* If both flags are set assume an new kernel */
107 if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR] && tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]) {
108 printf("\t No IR\n");
109 } else if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) {
110 printf("\t Passive scan\n");
111 } else if (tb_freq[__NL80211_FREQUENCY_ATTR_NO_IBSS]){
112 printf("\t No IBSS\n");
113 }
114
115 if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
116 printf("\t Radar detection\n");
117
118 printf("\t Channel widths:");
119 if (!tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
120 printf(" 20MHz");
121 if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
122 printf(" HT40-");
123 if (ctx->width_40 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
124 printf(" HT40+");
125 if (ctx->width_80 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
126 printf(" VHT80");
127 if (ctx->width_160 && !tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
128 printf(" VHT160");
129 printf("\n");
130
131 if (!tb_freq[NL80211_FREQUENCY_ATTR_DISABLED] && tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
132 enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
133 unsigned long time;
134
135 printf("\t DFS state: %s", dfs_state_name(state));
136 if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) {
137 time = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]);
138 printf(" (for %lu sec)", time / 1000);
139 }
140 printf("\n");
141 if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME])
142 printf("\t DFS CAC time: %u ms\n",
143 nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]));
144 }
145 }
146 }
147 }
148 }
149
150 return NL_SKIP;
151 }
152
153 static int handle_channels(struct nl80211_state *state, struct nl_msg *msg,
154 int argc, char **argv, enum id_input id)
155 {
156 static struct channels_ctx ctx = {
157 .last_band = -1,
158 };
159
160 nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
161 nlmsg_hdr(msg)->nlmsg_flags |= NLM_F_DUMP;
162
163 register_handler(print_channels_handler, &ctx);
164
165 return 0;
166 }
167 TOPLEVEL(channels, NULL, NL80211_CMD_GET_WIPHY, 0, CIB_PHY, handle_channels, "Show available channels.");
168
169 static int handle_name(struct nl80211_state *state,
170 struct nl_msg *msg,
171 int argc, char **argv,
172 enum id_input id)
173 {
174 if (argc != 1)
175 return 1;
176
177 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv);
178
179 return 0;
180 nla_put_failure:
181 return -ENOBUFS;
182 }
183 COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name,
184 "Rename this wireless device.");
185
186 static int handle_freqs(struct nl_msg *msg, int argc, char **argv)
187 {
188 static const struct {
189 const char *name;
190 unsigned int val;
191 } bwmap[] = {
192 { .name = "20", .val = NL80211_CHAN_WIDTH_20, },
193 { .name = "40", .val = NL80211_CHAN_WIDTH_40, },
194 { .name = "80", .val = NL80211_CHAN_WIDTH_80, },
195 { .name = "80+80", .val = NL80211_CHAN_WIDTH_80P80, },
196 { .name = "160", .val = NL80211_CHAN_WIDTH_160, },
197 };
198 uint32_t freq;
199 unsigned int i, bwval = NL80211_CHAN_WIDTH_20_NOHT;
200 char *end;
201
202 if (argc < 1)
203 return 1;
204
205 for (i = 0; i < ARRAY_SIZE(bwmap); i++) {
206 if (strcasecmp(bwmap[i].name, argv[0]) == 0) {
207 bwval = bwmap[i].val;
208 break;
209 }
210 }
211
212 if (bwval == NL80211_CHAN_WIDTH_20_NOHT)
213 return 1;
214
215 NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, bwval);
216
217 if (argc == 1)
218 return 0;
219
220 /* center freq 1 */
221 if (!*argv[1])
222 return 1;
223 freq = strtoul(argv[1], &end, 10);
224 if (*end)
225 return 1;
226 NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq);
227
228 if (argc == 2)
229 return 0;
230
231 /* center freq 2 */
232 if (!*argv[2])
233 return 1;
234 freq = strtoul(argv[2], &end, 10);
235 if (*end)
236 return 1;
237 NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, freq);
238
239 return 0;
240 nla_put_failure:
241 return -ENOBUFS;
242 }
243
244 static int handle_freqchan(struct nl_msg *msg, bool chan,
245 int argc, char **argv)
246 {
247 char *end;
248 static const struct {
249 const char *name;
250 unsigned int val;
251 } htmap[] = {
252 { .name = "HT20", .val = NL80211_CHAN_HT20, },
253 { .name = "HT40+", .val = NL80211_CHAN_HT40PLUS, },
254 { .name = "HT40-", .val = NL80211_CHAN_HT40MINUS, },
255 };
256 unsigned int htval = NL80211_CHAN_NO_HT;
257 unsigned int freq;
258 unsigned int i;
259
260 if (!argc || argc > 4)
261 return 1;
262
263 if (!*argv[0])
264 return 1;
265 freq = strtoul(argv[0], &end, 10);
266 if (*end)
267 return 1;
268
269 if (chan) {
270 enum nl80211_band band;
271 band = freq <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
272 freq = ieee80211_channel_to_frequency(freq, band);
273 }
274
275 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
276
277 if (argc > 2) {
278 return handle_freqs(msg, argc - 1, argv + 1);
279 } else if (argc == 2) {
280 for (i = 0; i < ARRAY_SIZE(htmap); i++) {
281 if (strcasecmp(htmap[i].name, argv[1]) == 0) {
282 htval = htmap[i].val;
283 break;
284 }
285 }
286 if (htval == NL80211_CHAN_NO_HT)
287 return handle_freqs(msg, argc - 1, argv + 1);
288 }
289
290 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, htval);
291
292 return 0;
293 nla_put_failure:
294 return -ENOBUFS;
295 }
296
297 static int handle_freq(struct nl80211_state *state, struct nl_msg *msg,
298 int argc, char **argv,
299 enum id_input id)
300 {
301 return handle_freqchan(msg, false, argc, argv);
302 }
303 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]",
304 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq,
305 "Set frequency/channel the hardware is using, including HT\n"
306 "configuration.");
307 COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]\n"
308 "<control freq> [20|40|80|80+80|160] [<center freq 1>] [<center freq 2>]",
309 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL);
310
311 static int handle_chan(struct nl80211_state *state, struct nl_msg *msg,
312 int argc, char **argv,
313 enum id_input id)
314 {
315 return handle_freqchan(msg, true, argc, argv);
316 }
317 COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
318 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL);
319 COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]",
320 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL);
321
322 static int handle_fragmentation(struct nl80211_state *state,
323 struct nl_msg *msg,
324 int argc, char **argv,
325 enum id_input id)
326 {
327 unsigned int frag;
328
329 if (argc != 1)
330 return 1;
331
332 if (strcmp("off", argv[0]) == 0)
333 frag = -1;
334 else {
335 char *end;
336
337 if (!*argv[0])
338 return 1;
339 frag = strtoul(argv[0], &end, 10);
340 if (*end != '\0')
341 return 1;
342 }
343
344 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, frag);
345
346 return 0;
347 nla_put_failure:
348 return -ENOBUFS;
349 }
350 COMMAND(set, frag, "<fragmentation threshold|off>",
351 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_fragmentation,
352 "Set fragmentation threshold.");
353
354 static int handle_rts(struct nl80211_state *state,
355 struct nl_msg *msg,
356 int argc, char **argv,
357 enum id_input id)
358 {
359 unsigned int rts;
360
361 if (argc != 1)
362 return 1;
363
364 if (strcmp("off", argv[0]) == 0)
365 rts = -1;
366 else {
367 char *end;
368
369 if (!*argv[0])
370 return 1;
371 rts = strtoul(argv[0], &end, 10);
372 if (*end != '\0')
373 return 1;
374 }
375
376 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, rts);
377
378 return 0;
379 nla_put_failure:
380 return -ENOBUFS;
381 }
382 COMMAND(set, rts, "<rts threshold|off>",
383 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_rts,
384 "Set rts threshold.");
385
386 static int handle_retry(struct nl80211_state *state,
387 struct nl_msg *msg,
388 int argc, char **argv, enum id_input id)
389 {
390 unsigned int retry_short = 0, retry_long = 0;
391 bool have_retry_s = false, have_retry_l = false;
392 int i;
393 enum {
394 S_NONE,
395 S_SHORT,
396 S_LONG,
397 } parser_state = S_NONE;
398
399 if (!argc || (argc != 2 && argc != 4))
400 return 1;
401
402 for (i = 0; i < argc; i++) {
403 char *end;
404 unsigned int tmpul;
405
406 if (strcmp(argv[i], "short") == 0) {
407 if (have_retry_s)
408 return 1;
409 parser_state = S_SHORT;
410 have_retry_s = true;
411 } else if (strcmp(argv[i], "long") == 0) {
412 if (have_retry_l)
413 return 1;
414 parser_state = S_LONG;
415 have_retry_l = true;
416 } else {
417 tmpul = strtoul(argv[i], &end, 10);
418 if (*end != '\0')
419 return 1;
420 if (!tmpul || tmpul > 255)
421 return -EINVAL;
422 switch (parser_state) {
423 case S_SHORT:
424 retry_short = tmpul;
425 break;
426 case S_LONG:
427 retry_long = tmpul;
428 break;
429 default:
430 return 1;
431 }
432 }
433 }
434
435 if (!have_retry_s && !have_retry_l)
436 return 1;
437 if (have_retry_s)
438 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, retry_short);
439 if (have_retry_l)
440 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, retry_long);
441
442 return 0;
443 nla_put_failure:
444 return -ENOBUFS;
445 }
446 COMMAND(set, retry, "[short <limit>] [long <limit>]",
447 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_retry,
448 "Set retry limit.");
449
450 #ifndef NETNS_RUN_DIR
451 #define NETNS_RUN_DIR "/var/run/netns"
452 #endif
453 static int netns_get_fd(const char *name)
454 {
455 char pathbuf[MAXPATHLEN];
456 const char *path, *ptr;
457
458 path = name;
459 ptr = strchr(name, '/');
460 if (!ptr) {
461 snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
462 NETNS_RUN_DIR, name );
463 path = pathbuf;
464 }
465 return open(path, O_RDONLY);
466 }
467
468 static int handle_netns(struct nl80211_state *state,
469 struct nl_msg *msg,
470 int argc, char **argv,
471 enum id_input id)
472 {
473 char *end;
474 int fd;
475
476 if (argc < 1 || !*argv[0])
477 return 1;
478
479 if (argc == 1) {
480 NLA_PUT_U32(msg, NL80211_ATTR_PID,
481 strtoul(argv[0], &end, 10));
482 if (*end != '\0') {
483 printf("Invalid parameter: pid(%s)\n", argv[0]);
484 return 1;
485 }
486 return 0;
487 }
488
489 if (argc != 2 || strcmp(argv[0], "name"))
490 return 1;
491
492 if ((fd = netns_get_fd(argv[1])) >= 0) {
493 NLA_PUT_U32(msg, NL80211_ATTR_NETNS_FD, fd);
494 return 0;
495 } else {
496 printf("Invalid parameter: nsname(%s)\n", argv[0]);
497 }
498
499 return 1;
500
501 nla_put_failure:
502 return -ENOBUFS;
503 }
504 COMMAND(set, netns, "{ <pid> | name <nsname> }",
505 NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns,
506 "Put this wireless device into a different network namespace:\n"
507 " <pid> - change network namespace by process id\n"
508 " <nsname> - change network namespace by name from "NETNS_RUN_DIR"\n"
509 " or by absolute path (man ip-netns)\n");
510
511 static int handle_coverage(struct nl80211_state *state,
512 struct nl_msg *msg,
513 int argc, char **argv,
514 enum id_input id)
515 {
516 char *end;
517 unsigned int coverage;
518
519 if (argc != 1)
520 return 1;
521
522 if (!*argv[0])
523 return 1;
524 coverage = strtoul(argv[0], &end, 10);
525 if (coverage > 255)
526 return 1;
527
528 if (*end)
529 return 1;
530
531 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
532
533 return 0;
534 nla_put_failure:
535 return -ENOBUFS;
536 }
537 COMMAND(set, coverage, "<coverage class>",
538 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_coverage,
539 "Set coverage class (1 for every 3 usec of air propagation time).\n"
540 "Valid values: 0 - 255.");
541
542 static int handle_distance(struct nl80211_state *state,
543 struct nl_msg *msg,
544 int argc, char **argv,
545 enum id_input id)
546 {
547 if (argc != 1)
548 return 1;
549
550 if (!*argv[0])
551 return 1;
552
553 if (strcmp("auto", argv[0]) == 0) {
554 NLA_PUT_FLAG(msg, NL80211_ATTR_WIPHY_DYN_ACK);
555 } else {
556 char *end;
557 unsigned int distance, coverage;
558
559 distance = strtoul(argv[0], &end, 10);
560
561 if (*end)
562 return 1;
563
564 /*
565 * Divide double the distance by the speed of light
566 * in m/usec (300) to get round-trip time in microseconds
567 * and then divide the result by three to get coverage class
568 * as specified in IEEE 802.11-2007 table 7-27.
569 * Values are rounded upwards.
570 */
571 coverage = (distance + 449) / 450;
572 if (coverage > 255)
573 return 1;
574
575 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, coverage);
576 }
577
578 return 0;
579 nla_put_failure:
580 return -ENOBUFS;
581 }
582 COMMAND(set, distance, "<auto|distance>",
583 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance,
584 "Enable ACK timeout estimation algorithm (dynack) or set appropriate\n"
585 "coverage class for given link distance in meters.\n"
586 "To disable dynack set valid value for coverage class.\n"
587 "Valid values: 0 - 114750");
588
589 static int handle_txpower(struct nl80211_state *state,
590 struct nl_msg *msg,
591 int argc, char **argv,
592 enum id_input id)
593 {
594 enum nl80211_tx_power_setting type;
595 int mbm;
596
597 /* get the required args */
598 if (argc != 1 && argc != 2)
599 return 1;
600
601 if (!strcmp(argv[0], "auto"))
602 type = NL80211_TX_POWER_AUTOMATIC;
603 else if (!strcmp(argv[0], "fixed"))
604 type = NL80211_TX_POWER_FIXED;
605 else if (!strcmp(argv[0], "limit"))
606 type = NL80211_TX_POWER_LIMITED;
607 else {
608 printf("Invalid parameter: %s\n", argv[0]);
609 return 2;
610 }
611
612 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
613
614 if (type != NL80211_TX_POWER_AUTOMATIC) {
615 char *endptr;
616 if (argc != 2) {
617 printf("Missing TX power level argument.\n");
618 return 2;
619 }
620
621 mbm = strtol(argv[1], &endptr, 10);
622 if (*endptr)
623 return 2;
624 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
625 } else if (argc != 1)
626 return 1;
627
628 return 0;
629
630 nla_put_failure:
631 return -ENOBUFS;
632 }
633 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
634 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_txpower,
635 "Specify transmit power level and setting type.");
636 COMMAND(set, txpower, "<auto|fixed|limit> [<tx power in mBm>]",
637 NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_txpower,
638 "Specify transmit power level and setting type.");
639
640 static int handle_antenna(struct nl80211_state *state,
641 struct nl_msg *msg,
642 int argc, char **argv,
643 enum id_input id)
644 {
645 char *end;
646 uint32_t tx_ant = 0, rx_ant = 0;
647
648 if (argc == 1 && strcmp(argv[0], "all") == 0) {
649 tx_ant = 0xffffffff;
650 rx_ant = 0xffffffff;
651 } else if (argc == 1) {
652 tx_ant = rx_ant = strtoul(argv[0], &end, 0);
653 if (*end)
654 return 1;
655 }
656 else if (argc == 2) {
657 tx_ant = strtoul(argv[0], &end, 0);
658 if (*end)
659 return 1;
660 rx_ant = strtoul(argv[1], &end, 0);
661 if (*end)
662 return 1;
663 } else
664 return 1;
665
666 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
667 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
668
669 return 0;
670
671 nla_put_failure:
672 return -ENOBUFS;
673 }
674 COMMAND(set, antenna, "<bitmap> | all | <tx bitmap> <rx bitmap>",
675 NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_antenna,
676 "Set a bitmap of allowed antennas to use for TX and RX.\n"
677 "The driver may reject antenna configurations it cannot support.");