1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file
8 * @brief Utility functions to be used by the Wi-Fi subsystem.
9 */
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(net_wifi_utils, CONFIG_NET_L2_WIFI_MGMT_LOG_LEVEL);
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17
18 #include <zephyr/kernel.h>
19 #include <zephyr/net/net_core.h>
20 #include <zephyr/net/wifi.h>
21 #include <zephyr/net/wifi_mgmt.h>
22 #include <zephyr/net/wifi_utils.h>
23
24 /* Ensure 'strtok_r' is available even with -std=c99. */
25 char *strtok_r(char *str, const char *delim, char **saveptr);
26
27 static const uint8_t valid_5g_chans_20mhz[] = {32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 96, 100,
28 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 159, 161,
29 163, 165, 167, 169, 171, 173, 175, 177};
30
wifi_utils_map_band_str_to_idx(char * band_str)31 static enum wifi_frequency_bands wifi_utils_map_band_str_to_idx(char *band_str)
32 {
33 enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
34
35 if (!strcmp(band_str, "2")) {
36 band = WIFI_FREQ_BAND_2_4_GHZ;
37 } else if (!strcmp(band_str, "5")) {
38 band = WIFI_FREQ_BAND_5_GHZ;
39 } else if (!strcmp(band_str, "6")) {
40 band = WIFI_FREQ_BAND_6_GHZ;
41 }
42
43 return band;
44 }
45
46
wifi_utils_validate_chan_2g(uint16_t chan)47 bool wifi_utils_validate_chan_2g(uint16_t chan)
48 {
49 if ((chan >= 1) && (chan <= 14)) {
50 return true;
51 }
52
53 return false;
54 }
55
56
wifi_utils_validate_chan_5g(uint16_t chan)57 bool wifi_utils_validate_chan_5g(uint16_t chan)
58 {
59 uint16_t i;
60
61 for (i = 0; i < ARRAY_SIZE(valid_5g_chans_20mhz); i++) {
62 if (chan == valid_5g_chans_20mhz[i]) {
63 return true;
64 }
65 }
66
67 return false;
68 }
69
70
wifi_utils_validate_chan_6g(uint16_t chan)71 bool wifi_utils_validate_chan_6g(uint16_t chan)
72 {
73 if (((chan >= 1) && (chan <= 233) && (!((chan - 1) % 4))) ||
74 (chan == 2)) {
75 return true;
76 }
77
78 return false;
79 }
80
81
wifi_utils_validate_chan(uint8_t band,uint16_t chan)82 bool wifi_utils_validate_chan(uint8_t band,
83 uint16_t chan)
84 {
85 bool result = false;
86
87 switch (band) {
88 case WIFI_FREQ_BAND_2_4_GHZ:
89 result = wifi_utils_validate_chan_2g(chan);
90 break;
91 case WIFI_FREQ_BAND_5_GHZ:
92 result = wifi_utils_validate_chan_5g(chan);
93 break;
94 case WIFI_FREQ_BAND_6_GHZ:
95 result = wifi_utils_validate_chan_6g(chan);
96 break;
97 default:
98 NET_ERR("Unknown band: %d", band);
99 break;
100 }
101
102 return result;
103 }
104
105 /**
106 * @brief Get the next Wi-Fi 6GHz channel based on the given (valid) channel.
107 * The function handles the initial edge cases (1 -> 2, 2 -> 5) and then increments by 4.
108 *
109 * @param chan Current channel number.
110 * @return Next valid channel number.
111 */
wifi_utils_get_next_chan_6g(uint16_t chan)112 uint16_t wifi_utils_get_next_chan_6g(uint16_t chan)
113 {
114 if (chan == 1) {
115 return 2;
116 } else if (chan == 2) {
117 return 5;
118 } else {
119 return chan + 4;
120 }
121 }
122
wifi_utils_get_all_chans_in_range(uint8_t chan_start,uint8_t chan_end,struct wifi_band_channel * band_chan,uint8_t band_idx,uint8_t * chan_idx)123 static int wifi_utils_get_all_chans_in_range(uint8_t chan_start,
124 uint8_t chan_end,
125 struct wifi_band_channel *band_chan,
126 uint8_t band_idx,
127 uint8_t *chan_idx)
128 {
129 uint8_t i;
130 bool start = false;
131 bool end = false;
132 uint8_t idx;
133
134 if (!wifi_utils_validate_chan(band_idx, chan_start)) {
135 NET_ERR("Invalid channel value %d in band %d", chan_start, band_idx);
136 return -EINVAL;
137 }
138
139 if (!wifi_utils_validate_chan(band_idx, chan_end)) {
140 NET_ERR("Invalid channel value %d in band %d", chan_end, band_idx);
141 return -EINVAL;
142 }
143
144 if (chan_end < chan_start) {
145 NET_ERR("Channel range end (%d) cannot be less than start (%d)",
146 chan_end,
147 chan_start);
148 return -EINVAL;
149 }
150
151 switch (band_idx) {
152 case WIFI_FREQ_BAND_2_4_GHZ:
153 idx = *chan_idx;
154
155 for (i = chan_start+1; i <= chan_end; i++) {
156 band_chan[idx].band = band_idx;
157 band_chan[idx].channel = i;
158 idx++;
159 }
160
161 *chan_idx = idx;
162
163 break;
164 case WIFI_FREQ_BAND_5_GHZ:
165 idx = *chan_idx;
166
167 for (i = 0; i < ARRAY_SIZE(valid_5g_chans_20mhz); i++) {
168 if (valid_5g_chans_20mhz[i] == chan_start) {
169 start = true;
170 continue;
171 }
172
173 if (valid_5g_chans_20mhz[i] == chan_end) {
174 end = true;
175 }
176
177 if (start) {
178 if (idx > WIFI_MGMT_SCAN_CHAN_MAX_MANUAL) {
179 *chan_idx = idx;
180 break;
181 }
182 band_chan[idx].band = band_idx;
183 band_chan[idx].channel = valid_5g_chans_20mhz[i];
184 idx++;
185 }
186
187 if (end) {
188 *chan_idx = idx;
189 break;
190 }
191 }
192
193 break;
194 case WIFI_FREQ_BAND_6_GHZ:
195 idx = *chan_idx;
196
197 i = wifi_utils_get_next_chan_6g(chan_start);
198
199 while (i <= chan_end) {
200 band_chan[idx].band = band_idx;
201 band_chan[idx].channel = i;
202 idx++;
203
204 i = wifi_utils_get_next_chan_6g(i);
205 }
206
207 *chan_idx = idx;
208 break;
209 default:
210 NET_ERR("Unknown band value: %d", band_idx);
211 return -EINVAL;
212 }
213
214 return 0;
215 }
216
217
wifi_utils_validate_chan_str(char * chan_str)218 static int wifi_utils_validate_chan_str(char *chan_str)
219 {
220 uint8_t i;
221
222 if ((!chan_str) || (!strlen(chan_str))) {
223 NET_ERR("Null or empty channel string\n");
224 return -EINVAL;
225 }
226
227 for (i = 0; i < strlen(chan_str); i++) {
228 if (!isdigit((int)chan_str[i])) {
229 NET_ERR("Invalid character in channel string %c\n", chan_str[i]);
230 return -EINVAL;
231 }
232 }
233
234 return 0;
235 }
236
237
wifi_utils_parse_scan_bands(char * scan_bands_str,uint8_t * band_map)238 int wifi_utils_parse_scan_bands(char *scan_bands_str, uint8_t *band_map)
239 {
240 char parse_str[WIFI_MGMT_BAND_STR_SIZE_MAX + 1];
241 char *band_str = NULL;
242 char *ctx = NULL;
243 enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
244 int len;
245
246 if (!scan_bands_str) {
247 return -EINVAL;
248 }
249
250 len = strlen(scan_bands_str);
251
252 if (len > WIFI_MGMT_BAND_STR_SIZE_MAX) {
253 NET_ERR("Band string (%s) size (%d) exceeds maximum allowed value (%d)",
254 scan_bands_str,
255 len,
256 WIFI_MGMT_BAND_STR_SIZE_MAX);
257 return -EINVAL;
258 }
259
260 strncpy(parse_str, scan_bands_str, sizeof(parse_str) - 1);
261 parse_str[sizeof(parse_str) - 1] = '\0';
262
263 band_str = strtok_r(parse_str, ",", &ctx);
264
265 while (band_str) {
266 band = wifi_utils_map_band_str_to_idx(band_str);
267
268 if (band == WIFI_FREQ_BAND_UNKNOWN) {
269 NET_ERR("Invalid band value: %s", band_str);
270 return -EINVAL;
271 }
272
273 *band_map |= (1 << band);
274
275 band_str = strtok_r(NULL, ",", &ctx);
276 }
277
278 return 0;
279 }
280
wifi_utils_parse_scan_ssids(char * scan_ssids_str,const char * ssids[],uint8_t num_ssids)281 int wifi_utils_parse_scan_ssids(char *scan_ssids_str,
282 const char *ssids[],
283 uint8_t num_ssids)
284 {
285 int len;
286
287 if (!scan_ssids_str) {
288 return -EINVAL;
289 }
290
291 len = strlen(scan_ssids_str);
292 if (len > WIFI_SSID_MAX_LEN) {
293 NET_ERR("SSID string (%s) size (%d) exceeds maximum allowed value (%d)",
294 scan_ssids_str,
295 len,
296 WIFI_SSID_MAX_LEN);
297 return -EINVAL;
298 }
299
300 for (int i = 0; i < num_ssids; i++) {
301 if (ssids[i] != NULL) {
302 continue;
303 }
304 ssids[i] = scan_ssids_str;
305 return 0;
306 }
307
308 NET_WARN("Exceeded maximum allowed SSIDs (%d)", num_ssids);
309 return 0;
310 }
311
312
wifi_utils_parse_scan_chan(char * scan_chan_str,struct wifi_band_channel * band_chan,uint8_t max_channels)313 int wifi_utils_parse_scan_chan(char *scan_chan_str,
314 struct wifi_band_channel *band_chan,
315 uint8_t max_channels)
316 {
317 char band_str[WIFI_UTILS_MAX_BAND_STR_LEN] = {0};
318 char chan_str[WIFI_UTILS_MAX_CHAN_STR_LEN] = {0};
319 enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
320 uint16_t band_str_start_idx = 0;
321 uint16_t chan_str_start_idx = 0;
322 uint8_t chan_idx = 0;
323 uint8_t chan_start = 0;
324 uint8_t chan_val = 0;
325 uint16_t i = 0;
326 bool valid_band = false;
327 bool valid_chan = false;
328
329 while (scan_chan_str[i] != '\0') {
330 if (scan_chan_str[i] != ':') {
331 i++;
332 continue;
333 }
334
335 if (((i - band_str_start_idx) <= 0) ||
336 ((i - band_str_start_idx) > WIFI_UTILS_MAX_BAND_STR_LEN)) {
337 NET_ERR("Invalid band value %s",
338 &scan_chan_str[band_str_start_idx]);
339 return -EINVAL;
340 }
341
342 strncpy(band_str,
343 &scan_chan_str[band_str_start_idx],
344 (i - band_str_start_idx));
345
346 band = wifi_utils_map_band_str_to_idx(band_str);
347
348 if (band == WIFI_FREQ_BAND_UNKNOWN) {
349 NET_ERR("Unsupported band value: %s", band_str);
350 return -EINVAL;
351 }
352
353 i++;
354 chan_str_start_idx = i;
355 valid_band = true;
356
357 while (1) {
358 if ((scan_chan_str[i] != ',') &&
359 (scan_chan_str[i] != '_') &&
360 (scan_chan_str[i] != '-') &&
361 (scan_chan_str[i] != '\0')) {
362 i++;
363 continue;
364 }
365
366 if ((i - chan_str_start_idx) >
367 WIFI_UTILS_MAX_CHAN_STR_LEN) {
368 NET_ERR("Invalid chan value %s",
369 &scan_chan_str[chan_str_start_idx]);
370 return -EINVAL;
371 }
372
373 strncpy(chan_str,
374 &scan_chan_str[chan_str_start_idx],
375 (i - chan_str_start_idx));
376
377 if (wifi_utils_validate_chan_str(chan_str)) {
378 NET_ERR("Channel string validation failed");
379 return -EINVAL;
380 }
381
382 chan_val = atoi(chan_str);
383
384 memset(chan_str, 0, sizeof(chan_str));
385
386 if (chan_start) {
387 if (wifi_utils_get_all_chans_in_range(chan_start,
388 chan_val,
389 band_chan,
390 band,
391 &chan_idx)) {
392 NET_ERR("Channel range invalid");
393 return -EINVAL;
394 }
395
396 if (chan_idx > max_channels) {
397 NET_ERR("Too many channels specified (%d)", max_channels);
398 return -EINVAL;
399 }
400 chan_start = 0;
401 } else {
402 if (!wifi_utils_validate_chan(band,
403 chan_val)) {
404 NET_ERR("Invalid channel %d", chan_val);
405 return -EINVAL;
406 }
407 if (chan_idx == max_channels) {
408 NET_ERR("Too many channels specified (%d)", max_channels);
409 return -EINVAL;
410 }
411
412 band_chan[chan_idx].band = band;
413 band_chan[chan_idx].channel = chan_val;
414 chan_idx++;
415 }
416
417 valid_chan = true;
418
419 if (scan_chan_str[i] == '_') {
420 band_str_start_idx = ++i;
421 break;
422 } else if (scan_chan_str[i] == ',') {
423 chan_str_start_idx = ++i;
424 } else if (scan_chan_str[i] == '-') {
425 chan_start = chan_val;
426 chan_str_start_idx = ++i;
427 } else if (scan_chan_str[i] == '\0') {
428 break;
429 }
430 }
431 }
432
433 if (!valid_band) {
434 NET_ERR("No valid band found");
435 return -EINVAL;
436 }
437
438 if (!valid_chan) {
439 NET_ERR("No valid channel found");
440 return -EINVAL;
441 }
442
443 return 0;
444 }
445