]>
Commit | Line | Data |
---|---|---|
379f8397 | 1 | #include <stdbool.h> |
0f55e0b8 | 2 | #include <errno.h> |
d0260390 | 3 | #include <strings.h> |
91454b65 VK |
4 | #include <sys/param.h> |
5 | #include <sys/stat.h> | |
6 | #include <fcntl.h> | |
0f55e0b8 JB |
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 | ||
f408e01b | 14 | #include "nl80211.h" |
0f55e0b8 JB |
15 | #include "iw.h" |
16 | ||
db9d4050 RM |
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 | ||
7c37a24d | 169 | static int handle_name(struct nl80211_state *state, |
0f55e0b8 | 170 | struct nl_msg *msg, |
05514f95 JB |
171 | int argc, char **argv, |
172 | enum id_input id) | |
0f55e0b8 | 173 | { |
0f55e0b8 | 174 | if (argc != 1) |
5e75fd04 | 175 | return 1; |
0f55e0b8 JB |
176 | |
177 | NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, *argv); | |
178 | ||
70391ccf | 179 | return 0; |
0f55e0b8 | 180 | nla_put_failure: |
70391ccf | 181 | return -ENOBUFS; |
0f55e0b8 | 182 | } |
cea8fa1c JB |
183 | COMMAND(set, name, "<new name>", NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_name, |
184 | "Rename this wireless device."); | |
b822cda9 | 185 | |
7c60bb76 JB |
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; | |
0ee571d5 | 199 | unsigned int i, bwval = NL80211_CHAN_WIDTH_20_NOHT; |
7c60bb76 JB |
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 | ||
379f8397 JB |
244 | static int handle_freqchan(struct nl_msg *msg, bool chan, |
245 | int argc, char **argv) | |
b822cda9 | 246 | { |
e86b7e02 | 247 | char *end; |
b822cda9 JB |
248 | static const struct { |
249 | const char *name; | |
250 | unsigned int val; | |
251 | } htmap[] = { | |
68632dc7 JB |
252 | { .name = "HT20", .val = NL80211_CHAN_HT20, }, |
253 | { .name = "HT40+", .val = NL80211_CHAN_HT40PLUS, }, | |
254 | { .name = "HT40-", .val = NL80211_CHAN_HT40MINUS, }, | |
b822cda9 | 255 | }; |
68632dc7 | 256 | unsigned int htval = NL80211_CHAN_NO_HT; |
b822cda9 | 257 | unsigned int freq; |
0ee571d5 | 258 | unsigned int i; |
b822cda9 | 259 | |
7c60bb76 | 260 | if (!argc || argc > 4) |
b822cda9 JB |
261 | return 1; |
262 | ||
e86b7e02 JB |
263 | if (!*argv[0]) |
264 | return 1; | |
265 | freq = strtoul(argv[0], &end, 10); | |
266 | if (*end) | |
267 | return 1; | |
268 | ||
58b46da2 BR |
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 | } | |
b822cda9 JB |
274 | |
275 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); | |
7c60bb76 JB |
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 | ||
68632dc7 | 290 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, htval); |
b822cda9 JB |
291 | |
292 | return 0; | |
293 | nla_put_failure: | |
294 | return -ENOBUFS; | |
295 | } | |
379f8397 | 296 | |
34b23014 | 297 | static int handle_freq(struct nl80211_state *state, struct nl_msg *msg, |
05514f95 JB |
298 | int argc, char **argv, |
299 | enum id_input id) | |
379f8397 JB |
300 | { |
301 | return handle_freqchan(msg, false, argc, argv); | |
302 | } | |
b822cda9 | 303 | COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]", |
00c448b2 JB |
304 | NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_freq, |
305 | "Set frequency/channel the hardware is using, including HT\n" | |
306 | "configuration."); | |
7c60bb76 JB |
307 | COMMAND(set, freq, "<freq> [HT20|HT40+|HT40-]\n" |
308 | "<control freq> [20|40|80|80+80|160] [<center freq 1>] [<center freq 2>]", | |
01ae06f9 | 309 | NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_freq, NULL); |
379f8397 | 310 | |
34b23014 | 311 | static int handle_chan(struct nl80211_state *state, struct nl_msg *msg, |
05514f95 JB |
312 | int argc, char **argv, |
313 | enum id_input id) | |
379f8397 JB |
314 | { |
315 | return handle_freqchan(msg, true, argc, argv); | |
316 | } | |
317 | COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]", | |
01ae06f9 | 318 | NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_chan, NULL); |
379f8397 | 319 | COMMAND(set, channel, "<channel> [HT20|HT40+|HT40-]", |
01ae06f9 | 320 | NL80211_CMD_SET_WIPHY, 0, CIB_NETDEV, handle_chan, NULL); |
625aa4ae JB |
321 | |
322 | static int handle_fragmentation(struct nl80211_state *state, | |
34b23014 | 323 | struct nl_msg *msg, |
05514f95 JB |
324 | int argc, char **argv, |
325 | enum id_input id) | |
625aa4ae JB |
326 | { |
327 | unsigned int frag; | |
328 | ||
329 | if (argc != 1) | |
330 | return 1; | |
331 | ||
332 | if (strcmp("off", argv[0]) == 0) | |
333 | frag = -1; | |
e86b7e02 JB |
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 | } | |
625aa4ae JB |
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, | |
34b23014 | 355 | struct nl_msg *msg, |
05514f95 JB |
356 | int argc, char **argv, |
357 | enum id_input id) | |
625aa4ae JB |
358 | { |
359 | unsigned int rts; | |
360 | ||
361 | if (argc != 1) | |
362 | return 1; | |
363 | ||
364 | if (strcmp("off", argv[0]) == 0) | |
365 | rts = -1; | |
e86b7e02 JB |
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 | } | |
625aa4ae JB |
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."); | |
e960e066 | 385 | |
c993e6e7 | 386 | static int handle_retry(struct nl80211_state *state, |
34b23014 | 387 | struct nl_msg *msg, |
c993e6e7 UR |
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 | ||
91454b65 VK |
450 | #ifndef NETNS_RUN_DIR |
451 | #define NETNS_RUN_DIR "/var/run/netns" | |
452 | #endif | |
c0441e65 | 453 | static int netns_get_fd(const char *name) |
91454b65 VK |
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 | ||
e960e066 | 468 | static int handle_netns(struct nl80211_state *state, |
e960e066 | 469 | struct nl_msg *msg, |
05514f95 JB |
470 | int argc, char **argv, |
471 | enum id_input id) | |
e960e066 JB |
472 | { |
473 | char *end; | |
91454b65 | 474 | int fd; |
e960e066 | 475 | |
91454b65 | 476 | if (argc < 1 || !*argv[0]) |
e960e066 JB |
477 | return 1; |
478 | ||
91454b65 VK |
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")) | |
e86b7e02 JB |
490 | return 1; |
491 | ||
91454b65 VK |
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 | } | |
e960e066 | 498 | |
91454b65 | 499 | return 1; |
e960e066 | 500 | |
e960e066 JB |
501 | nla_put_failure: |
502 | return -ENOBUFS; | |
503 | } | |
91454b65 | 504 | COMMAND(set, netns, "{ <pid> | name <nsname> }", |
e960e066 | 505 | NL80211_CMD_SET_WIPHY_NETNS, 0, CIB_PHY, handle_netns, |
91454b65 VK |
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"); | |
b2f92dd0 LT |
510 | |
511 | static int handle_coverage(struct nl80211_state *state, | |
b2f92dd0 | 512 | struct nl_msg *msg, |
05514f95 JB |
513 | int argc, char **argv, |
514 | enum id_input id) | |
b2f92dd0 | 515 | { |
e86b7e02 | 516 | char *end; |
b2f92dd0 LT |
517 | unsigned int coverage; |
518 | ||
519 | if (argc != 1) | |
520 | return 1; | |
521 | ||
e86b7e02 JB |
522 | if (!*argv[0]) |
523 | return 1; | |
524 | coverage = strtoul(argv[0], &end, 10); | |
b2f92dd0 LT |
525 | if (coverage > 255) |
526 | return 1; | |
527 | ||
e86b7e02 JB |
528 | if (*end) |
529 | return 1; | |
530 | ||
b2f92dd0 LT |
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, | |
b2f92dd0 | 543 | struct nl_msg *msg, |
05514f95 JB |
544 | int argc, char **argv, |
545 | enum id_input id) | |
b2f92dd0 | 546 | { |
b2f92dd0 LT |
547 | if (argc != 1) |
548 | return 1; | |
549 | ||
e86b7e02 JB |
550 | if (!*argv[0]) |
551 | return 1; | |
552 | ||
e642142d LB |
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; | |
e86b7e02 | 558 | |
e642142d | 559 | distance = strtoul(argv[0], &end, 10); |
b2f92dd0 | 560 | |
e642142d LB |
561 | if (*end) |
562 | return 1; | |
b2f92dd0 | 563 | |
e642142d LB |
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 | } | |
b2f92dd0 LT |
577 | |
578 | return 0; | |
579 | nla_put_failure: | |
580 | return -ENOBUFS; | |
581 | } | |
e642142d | 582 | COMMAND(set, distance, "<auto|distance>", |
b2f92dd0 | 583 | NL80211_CMD_SET_WIPHY, 0, CIB_PHY, handle_distance, |
e642142d LB |
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" | |
b2f92dd0 | 587 | "Valid values: 0 - 114750"); |
a0b1f574 JO |
588 | |
589 | static int handle_txpower(struct nl80211_state *state, | |
a0b1f574 | 590 | struct nl_msg *msg, |
05514f95 JB |
591 | int argc, char **argv, |
592 | enum id_input id) | |
a0b1f574 JO |
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) { | |
18e05613 | 615 | char *endptr; |
a0b1f574 JO |
616 | if (argc != 2) { |
617 | printf("Missing TX power level argument.\n"); | |
618 | return 2; | |
619 | } | |
620 | ||
18e05613 | 621 | mbm = strtol(argv[1], &endptr, 10); |
08ec4c6b | 622 | if (*endptr) |
18e05613 | 623 | return 2; |
a0b1f574 JO |
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."); | |
afce7986 BR |
639 | |
640 | static int handle_antenna(struct nl80211_state *state, | |
afce7986 | 641 | struct nl_msg *msg, |
05514f95 JB |
642 | int argc, char **argv, |
643 | enum id_input id) | |
afce7986 BR |
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."); |