xref: /aosp_15_r20/external/iw/bitrate.c (revision 92022041c981f431db0b590d0c3272306d0ea2a2)
1 #include <errno.h>
2 
3 #include "nl80211.h"
4 #include "iw.h"
5 
6 
parse_rate_chunk(const char * arg,__u8 * nss,__u16 * mcs,unsigned int mode)7 static int parse_rate_chunk(const char *arg, __u8 *nss, __u16 *mcs, unsigned int mode)
8 {
9 	unsigned int count, i;
10 	unsigned int inss, mcs_start, mcs_end, tab[12];
11 	unsigned int max_mcs = 0, max_nss = 0;
12 
13 	*nss = 0; *mcs = 0;
14 
15 	if (mode == NL80211_TXRATE_HE) {
16 		max_mcs = 11;
17 		max_nss = NL80211_HE_NSS_MAX;
18 	} else {
19 		max_mcs = 9;
20 		max_nss = NL80211_VHT_NSS_MAX;
21 	}
22 
23 	if (strchr(arg, '-')) {
24 		/* Format: NSS:MCS_START-MCS_END */
25 		count = sscanf(arg, "%u:%u-%u", &inss, &mcs_start, &mcs_end);
26 
27 		if (count != 3)
28 			return 0;
29 
30 		if (inss < 1 || inss > max_nss)
31 			return 0;
32 
33 		if (mcs_start > mcs_end)
34 			return 0;
35 
36 		if (mcs_start > max_mcs || mcs_end > max_mcs)
37 			return 0;
38 
39 		*nss = inss;
40 		for (i = mcs_start; i <= mcs_end; i++)
41 			*mcs |= 1 << i;
42 
43 	} else {
44 		/* Format: NSS:MCSx,MCSy,... */
45 		if (mode == NL80211_TXRATE_HE) {
46 			count = sscanf(arg, "%u:%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
47 				       &inss, &tab[0], &tab[1], &tab[2], &tab[3],
48 				       &tab[4], &tab[5], &tab[6], &tab[7], &tab[8],
49 				       &tab[9], &tab[10], &tab[11]);
50 		} else {
51 			count = sscanf(arg, "%u:%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", &inss,
52 				       &tab[0], &tab[1], &tab[2], &tab[3], &tab[4],
53 				       &tab[5], &tab[6], &tab[7], &tab[8], &tab[9]);
54 		}
55 
56 		if (count < 2)
57 			return 0;
58 
59 		if (inss < 1 || inss > max_nss)
60 			return 0;
61 
62 		*nss = inss;
63 		for (i = 0; i < count - 1; i++) {
64 			if (tab[i] > max_mcs)
65 				return 0;
66 			*mcs |= 1 << tab[i];
67 		}
68 	}
69 
70 	return 1;
71 }
72 
parse_vht_chunk(const char * arg,__u8 * nss,__u16 * mcs)73 static int parse_vht_chunk(const char *arg, __u8 *nss, __u16 *mcs)
74 {
75 	return parse_rate_chunk(arg, nss, mcs, NL80211_TXRATE_VHT);
76 }
77 
parse_he_chunk(const char * arg,__u8 * nss,__u16 * mcs)78 static int parse_he_chunk(const char *arg, __u8 *nss, __u16 *mcs)
79 {
80 	return parse_rate_chunk(arg, nss, mcs, NL80211_TXRATE_HE);
81 }
82 
setup_vht(struct nl80211_txrate_vht * txrate_vht,int argc,char ** argv)83 static int setup_vht(struct nl80211_txrate_vht *txrate_vht,
84 		     int argc, char **argv)
85 {
86 	__u8 nss;
87 	__u16 mcs;
88 	int i;
89 
90 	memset(txrate_vht, 0, sizeof(*txrate_vht));
91 
92 	for (i = 0; i < argc; i++) {
93 		if (!parse_vht_chunk(argv[i], &nss, &mcs))
94 			return 0;
95 
96 		nss--;
97 		txrate_vht->mcs[nss] |= mcs;
98 	}
99 
100 	return 1;
101 }
102 
setup_he(struct nl80211_txrate_he * txrate_he,int argc,char ** argv)103 static int setup_he(struct nl80211_txrate_he *txrate_he,
104 		    int argc, char **argv)
105 {
106 	__u8 nss;
107 	__u16 mcs;
108 	int i;
109 
110 	memset(txrate_he, 0, sizeof(*txrate_he));
111 
112 	for (i = 0; i < argc; i++) {
113 		if (!parse_he_chunk(argv[i], &nss, &mcs))
114 			return 0;
115 
116 		nss--;
117 		txrate_he->mcs[nss] |= mcs;
118 	}
119 
120 	return 1;
121 }
122 
123 #define HE_GI_STR_MAX	16
124 #define HE_GI_08_STR "0.8"
125 #define HE_GI_16_STR "1.6"
126 #define HE_GI_32_STR "3.2"
parse_he_gi(char * he_gi)127 static int parse_he_gi(char *he_gi)
128 {
129 	if (he_gi == NULL)
130 		return 0;
131 
132 	if (!strncmp(he_gi, HE_GI_08_STR, sizeof(HE_GI_08_STR)))
133 		return NL80211_RATE_INFO_HE_GI_0_8;
134 	if (!strncmp(he_gi, HE_GI_16_STR, sizeof(HE_GI_16_STR)))
135 		return NL80211_RATE_INFO_HE_GI_1_6;
136 	if (!strncmp(he_gi, HE_GI_32_STR, sizeof(HE_GI_32_STR)))
137 		return NL80211_RATE_INFO_HE_GI_3_2;
138 
139 	return -1;
140 }
141 
142 #define VHT_ARGC_MAX	100
143 
set_bitrates(struct nl_msg * msg,int argc,char ** argv,enum nl80211_attrs attr)144 int set_bitrates(struct nl_msg *msg,
145 		 int argc, char **argv,
146 		 enum nl80211_attrs attr)
147 {
148 	struct nlattr *nl_rates, *nl_band;
149 	int i, ret = 0;
150 	bool have_legacy_24 = false, have_legacy_5 = false;
151 	uint8_t legacy_24[32], legacy_5[32];
152 	int n_legacy_24 = 0, n_legacy_5 = 0;
153 	uint8_t *legacy = NULL;
154 	int *n_legacy = NULL;
155 	bool have_ht_mcs_24 = false, have_ht_mcs_5 = false;
156 	bool have_vht_mcs_24 = false, have_vht_mcs_5 = false;
157 	bool have_he_mcs_24 = false, have_he_mcs_5 = false;
158 	bool have_he_mcs_6 = false;
159 	uint8_t ht_mcs_24[77], ht_mcs_5[77];
160 	int n_ht_mcs_24 = 0, n_ht_mcs_5 = 0;
161 	struct nl80211_txrate_vht txrate_vht_24 = {};
162 	struct nl80211_txrate_vht txrate_vht_5 = {};
163 	struct nl80211_txrate_he txrate_he_24 = {};
164 	struct nl80211_txrate_he txrate_he_5 = {};
165 	struct nl80211_txrate_he txrate_he_6 = {};
166 	uint8_t *mcs = NULL;
167 	int *n_mcs = NULL;
168 	char *vht_argv_5[VHT_ARGC_MAX] = {}; char *vht_argv_24[VHT_ARGC_MAX] = {};
169 	char *he_argv_5[VHT_ARGC_MAX] = {}; char *he_argv_24[VHT_ARGC_MAX] = {};
170 	char *he_argv_6[VHT_ARGC_MAX] = {};
171 	char **vht_argv = NULL, **he_argv = NULL;
172 	int vht_argc_5 = 0; int vht_argc_24 = 0;
173 	int he_argc_5 = 0; int he_argc_24 = 0;
174 	int he_argc_6 = 0;
175 	int *vht_argc = NULL, *he_argc = NULL;
176 	int sgi_24 = 0, sgi_5 = 0, lgi_24 = 0, lgi_5 = 0;
177 	int has_he_gi_24 = 0, has_he_gi_5 = 0, has_he_ltf_24 = 0, has_he_ltf_5 = 0;
178 	int has_he_gi_6 = 0, has_he_ltf_6 = 0;
179 	int he_gi = 0, he_ltf = 0;
180 	char *he_gi_argv = NULL;
181 
182 	enum {
183 		S_NONE,
184 		S_LEGACY,
185 		S_HT,
186 		S_VHT,
187 		S_HE,
188 		S_GI,
189 		S_HE_GI,
190 		S_HE_LTF,
191 	} parser_state = S_NONE;
192 
193 	for (i = 0; i < argc; i++) {
194 		char *end;
195 		double tmpd;
196 		long tmpl;
197 
198 		if (strcmp(argv[i], "legacy-2.4") == 0) {
199 			if (have_legacy_24)
200 				return 1;
201 			parser_state = S_LEGACY;
202 			legacy = legacy_24;
203 			n_legacy = &n_legacy_24;
204 			have_legacy_24 = true;
205 		} else if (strcmp(argv[i], "legacy-5") == 0) {
206 			if (have_legacy_5)
207 				return 1;
208 			parser_state = S_LEGACY;
209 			legacy = legacy_5;
210 			n_legacy = &n_legacy_5;
211 			have_legacy_5 = true;
212 		}
213 		else if (strcmp(argv[i], "ht-mcs-2.4") == 0) {
214 			if (have_ht_mcs_24)
215 				return 1;
216 			parser_state = S_HT;
217 			mcs = ht_mcs_24;
218 			n_mcs = &n_ht_mcs_24;
219 			have_ht_mcs_24 = true;
220 		} else if (strcmp(argv[i], "ht-mcs-5") == 0) {
221 			if (have_ht_mcs_5)
222 				return 1;
223 			parser_state = S_HT;
224 			mcs = ht_mcs_5;
225 			n_mcs = &n_ht_mcs_5;
226 			have_ht_mcs_5 = true;
227 		} else if (strcmp(argv[i], "vht-mcs-2.4") == 0) {
228 			if (have_vht_mcs_24)
229 				return 1;
230 			parser_state = S_VHT;
231 			vht_argv = vht_argv_24;
232 			vht_argc = &vht_argc_24;
233 			have_vht_mcs_24 = true;
234 		} else if (strcmp(argv[i], "vht-mcs-5") == 0) {
235 			if (have_vht_mcs_5)
236 				return 1;
237 			parser_state = S_VHT;
238 			vht_argv = vht_argv_5;
239 			vht_argc = &vht_argc_5;
240 			have_vht_mcs_5 = true;
241 		} else if (strcmp(argv[i], "he-mcs-2.4") == 0) {
242 			if (have_he_mcs_24)
243 				return 1;
244 			parser_state = S_HE;
245 			he_argv = he_argv_24;
246 			he_argc = &he_argc_24;
247 			have_he_mcs_24 = true;
248 		} else if (strcmp(argv[i], "he-mcs-5") == 0) {
249 			if (have_he_mcs_5)
250 				return 1;
251 			parser_state = S_HE;
252 			he_argv = he_argv_5;
253 			he_argc = &he_argc_5;
254 			have_he_mcs_5 = true;
255 		} else if (strcmp(argv[i], "he-mcs-6") == 0) {
256 			if (have_he_mcs_6)
257 				return 1;
258 			parser_state = S_HE;
259 			he_argv = he_argv_6;
260 			he_argc = &he_argc_6;
261 			have_he_mcs_6 = true;
262 		} else if (strcmp(argv[i], "sgi-2.4") == 0) {
263 			sgi_24 = 1;
264 			parser_state = S_GI;
265 		} else if (strcmp(argv[i], "sgi-5") == 0) {
266 			sgi_5 = 1;
267 			parser_state = S_GI;
268 		} else if (strcmp(argv[i], "lgi-2.4") == 0) {
269 			lgi_24 = 1;
270 			parser_state = S_GI;
271 		} else if (strcmp(argv[i], "lgi-5") == 0) {
272 			lgi_5 = 1;
273 			parser_state = S_GI;
274 		} else if (strcmp(argv[i], "he-gi-2.4") == 0) {
275 			has_he_gi_24 = 1;
276 			parser_state = S_HE_GI;
277 		} else if (strcmp(argv[i], "he-gi-5") == 0) {
278 			has_he_gi_5 = 1;
279 			parser_state = S_HE_GI;
280 		} else if (strcmp(argv[i], "he-gi-6") == 0) {
281 			has_he_gi_6 = 1;
282 			parser_state = S_HE_GI;
283 		} else if (strcmp(argv[i], "he-ltf-2.4") == 0) {
284 			has_he_ltf_24 = 1;
285 			parser_state = S_HE_LTF;
286 		} else if (strcmp(argv[i], "he-ltf-5") == 0) {
287 			has_he_ltf_5 = 1;
288 			parser_state = S_HE_LTF;
289 		} else if (strcmp(argv[i], "he-ltf-6") == 0) {
290 			has_he_ltf_6 = 1;
291 			parser_state = S_HE_LTF;
292 		} else switch (parser_state) {
293 		case S_LEGACY:
294 			tmpd = strtod(argv[i], &end);
295 			if (*end != '\0')
296 				return 1;
297 			if (tmpd < 1 || tmpd > 255 * 2)
298 				return 1;
299 			legacy[(*n_legacy)++] = tmpd * 2;
300 			break;
301 		case S_HT:
302 			tmpl = strtol(argv[i], &end, 0);
303 			if (*end != '\0')
304 				return 1;
305 			if (tmpl < 0 || tmpl > 255)
306 				return 1;
307 			mcs[(*n_mcs)++] = tmpl;
308 			break;
309 		case S_VHT:
310 			if (*vht_argc >= VHT_ARGC_MAX)
311 				return 1;
312 			vht_argv[(*vht_argc)++] = argv[i];
313 			break;
314 		case S_HE:
315 			if (*he_argc >= VHT_ARGC_MAX)
316 				return 1;
317 			he_argv[(*he_argc)++] = argv[i];
318 			break;
319 		case S_GI:
320 			break;
321 		case S_HE_GI:
322 			he_gi_argv = argv[i];
323 			break;
324 		case S_HE_LTF:
325 			he_ltf = strtol(argv[i], &end, 0);
326 			if (*end != '\0')
327 				return 1;
328 			if (he_ltf < 0 || he_ltf > 4)
329 				return 1;
330 			he_ltf = he_ltf >> 1;
331 			break;
332 		default:
333 			if (attr != NL80211_ATTR_TX_RATES)
334 				goto next;
335 			return 1;
336 		}
337 	}
338 
339 next:
340 	if (attr != NL80211_ATTR_TX_RATES)
341 		ret = i;
342 
343 	if (have_vht_mcs_24)
344 		if (!setup_vht(&txrate_vht_24, vht_argc_24, vht_argv_24))
345 			return -EINVAL;
346 
347 	if (have_vht_mcs_5)
348 		if (!setup_vht(&txrate_vht_5, vht_argc_5, vht_argv_5))
349 			return -EINVAL;
350 
351 	if (have_he_mcs_24)
352 		if (!setup_he(&txrate_he_24, he_argc_24, he_argv_24))
353 			return -EINVAL;
354 
355 	if (have_he_mcs_5)
356 		if (!setup_he(&txrate_he_5, he_argc_5, he_argv_5))
357 			return -EINVAL;
358 
359 	if (have_he_mcs_6)
360 		if (!setup_he(&txrate_he_6, he_argc_6, he_argv_6))
361 			return -EINVAL;
362 
363 	if (sgi_5 && lgi_5)
364 		return 1;
365 
366 	if (sgi_24 && lgi_24)
367 		return 1;
368 
369 	if (he_gi_argv) {
370 		he_gi = parse_he_gi(he_gi_argv);
371 		if (he_gi < 0)
372 			return 1;
373 	}
374 
375 	nl_rates = nla_nest_start(msg, attr);
376 	if (!nl_rates)
377 		goto nla_put_failure;
378 
379 	if (have_legacy_24 || have_ht_mcs_24 || have_vht_mcs_24 || have_he_mcs_24 ||
380 	    sgi_24 || lgi_24 || has_he_gi_24 || has_he_ltf_24) {
381 		nl_band = nla_nest_start(msg, NL80211_BAND_2GHZ);
382 		if (!nl_band)
383 			goto nla_put_failure;
384 		if (have_legacy_24)
385 			nla_put(msg, NL80211_TXRATE_LEGACY, n_legacy_24, legacy_24);
386 		if (have_ht_mcs_24)
387 			nla_put(msg, NL80211_TXRATE_HT, n_ht_mcs_24, ht_mcs_24);
388 		if (have_vht_mcs_24)
389 			nla_put(msg, NL80211_TXRATE_VHT, sizeof(txrate_vht_24), &txrate_vht_24);
390 		if (have_he_mcs_24)
391 			nla_put(msg, NL80211_TXRATE_HE, sizeof(txrate_he_24),
392 				&txrate_he_24);
393 		if (sgi_24)
394 			nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_SGI);
395 		if (lgi_24)
396 			nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_LGI);
397 		if (has_he_gi_24)
398 			nla_put_u8(msg, NL80211_TXRATE_HE_GI, he_gi);
399 		if (has_he_ltf_24)
400 			nla_put_u8(msg, NL80211_TXRATE_HE_LTF, he_ltf);
401 		nla_nest_end(msg, nl_band);
402 	}
403 
404 	if (have_legacy_5 || have_ht_mcs_5 || have_vht_mcs_5 || have_he_mcs_5 ||
405 	    sgi_5 || lgi_5 || has_he_gi_5 || has_he_ltf_5) {
406 		nl_band = nla_nest_start(msg, NL80211_BAND_5GHZ);
407 		if (!nl_band)
408 			goto nla_put_failure;
409 		if (have_legacy_5)
410 			nla_put(msg, NL80211_TXRATE_LEGACY, n_legacy_5, legacy_5);
411 		if (have_ht_mcs_5)
412 			nla_put(msg, NL80211_TXRATE_HT, n_ht_mcs_5, ht_mcs_5);
413 		if (have_vht_mcs_5)
414 			nla_put(msg, NL80211_TXRATE_VHT, sizeof(txrate_vht_5), &txrate_vht_5);
415 		if (have_he_mcs_5)
416 			nla_put(msg, NL80211_TXRATE_HE, sizeof(txrate_he_5),
417 				&txrate_he_5);
418 		if (sgi_5)
419 			nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_SGI);
420 		if (lgi_5)
421 			nla_put_u8(msg, NL80211_TXRATE_GI, NL80211_TXRATE_FORCE_LGI);
422 		if (has_he_gi_5)
423 			nla_put_u8(msg, NL80211_TXRATE_HE_GI, he_gi);
424 		if (has_he_ltf_5)
425 			nla_put_u8(msg, NL80211_TXRATE_HE_LTF, he_ltf);
426 		nla_nest_end(msg, nl_band);
427 	}
428 
429 	if (have_he_mcs_6 || has_he_gi_6 || has_he_ltf_6) {
430 		nl_band = nla_nest_start(msg, NL80211_BAND_6GHZ);
431 		if (!nl_band)
432 			goto nla_put_failure;
433 		if (have_he_mcs_6)
434 			nla_put(msg, NL80211_TXRATE_HE, sizeof(txrate_he_6),
435 				&txrate_he_6);
436 		if (has_he_gi_6)
437 			nla_put_u8(msg, NL80211_TXRATE_HE_GI, he_gi);
438 		if (has_he_ltf_6)
439 			nla_put_u8(msg, NL80211_TXRATE_HE_LTF, he_ltf);
440 		nla_nest_end(msg, nl_band);
441 	}
442 
443 	nla_nest_end(msg, nl_rates);
444 
445 	return ret;
446  nla_put_failure:
447 	return -ENOBUFS;
448 }
449 
handle_bitrates(struct nl80211_state * state,struct nl_msg * msg,int argc,char ** argv,enum id_input id)450 static int handle_bitrates(struct nl80211_state *state,
451 			   struct nl_msg *msg,
452 			   int argc, char **argv,
453 			   enum id_input id)
454 {
455 	return set_bitrates(msg, argc, argv, NL80211_ATTR_TX_RATES);
456 }
457 
458 #define DESCR_LEGACY "[legacy-<2.4|5> <legacy rate in Mbps>*]"
459 #define DESCR DESCR_LEGACY " [ht-mcs-<2.4|5> <MCS index>*] [vht-mcs-<2.4|5>  [he-mcs-<2.4|5|6> <NSS:MCSx,MCSy... | NSS:MCSx-MCSy>*] [sgi-2.4|lgi-2.4] [sgi-5|lgi-5]"
460 
461 COMMAND(set, bitrates, "[legacy-<2.4|5> <legacy rate in Mbps>*] [ht-mcs-<2.4|5> <MCS index>*] [vht-mcs-<2.4|5> [he-mcs-<2.4|5|6> <NSS:MCSx,MCSy... | NSS:MCSx-MCSy>*] [sgi-2.4|lgi-2.4] [sgi-5|lgi-5] [he-gi-<2.4|5|6> <0.8|1.6|3.2>] [he-ltf-<2.4|5|6> <1|2|4>]",
462 	NL80211_CMD_SET_TX_BITRATE_MASK, 0, CIB_NETDEV, handle_bitrates,
463 	"Sets up the specified rate masks.\n"
464 	"Not passing any arguments would clear the existing mask (if any).");
465