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 subsytem.
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 uint16_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
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)106 static int wifi_utils_get_all_chans_in_range(uint8_t chan_start,
107 uint8_t chan_end,
108 struct wifi_band_channel *band_chan,
109 uint8_t band_idx,
110 uint8_t *chan_idx)
111 {
112 uint8_t i;
113 bool start = false;
114 bool end = false;
115 uint8_t idx;
116
117 if (!wifi_utils_validate_chan(band_idx, chan_start)) {
118 NET_ERR("Invalid channel value %d in band %d", chan_start, band_idx);
119 return -EINVAL;
120 }
121
122 if (!wifi_utils_validate_chan(band_idx, chan_end)) {
123 NET_ERR("Invalid channel value %d in band %d", chan_end, band_idx);
124 return -EINVAL;
125 }
126
127 if (chan_end < chan_start) {
128 NET_ERR("Channel range end (%d) cannot be less than start (%d)",
129 chan_end,
130 chan_start);
131 return -EINVAL;
132 }
133
134 switch (band_idx) {
135 case WIFI_FREQ_BAND_2_4_GHZ:
136 idx = *chan_idx;
137
138 for (i = chan_start; i <= chan_end; i++) {
139 band_chan[idx].band = band_idx;
140 band_chan[idx].channel = i;
141 idx++;
142 }
143
144 *chan_idx = idx;
145
146 break;
147 case WIFI_FREQ_BAND_5_GHZ:
148 idx = *chan_idx;
149
150 for (i = 0; i < ARRAY_SIZE(valid_5g_chans_20mhz); i++) {
151 if (valid_5g_chans_20mhz[i] == chan_start) {
152 start = true;
153 }
154
155 if (valid_5g_chans_20mhz[i] == chan_end) {
156 end = true;
157 }
158
159 if (start) {
160 band_chan[idx].band = band_idx;
161 band_chan[idx].channel = valid_5g_chans_20mhz[i];
162 idx++;
163 }
164
165 if (end) {
166 *chan_idx = idx;
167 break;
168 }
169 }
170
171 break;
172 case WIFI_FREQ_BAND_6_GHZ:
173 idx = *chan_idx;
174
175 i = chan_start;
176
177 while (i <= chan_end) {
178 band_chan[idx].band = band_idx;
179 band_chan[idx].channel = i;
180 idx++;
181
182 if (i == 1) {
183 i++;
184 } else if (i == 2) {
185 i += 3;
186 } else {
187 i += 4;
188 }
189 }
190
191 *chan_idx = idx;
192 break;
193 default:
194 NET_ERR("Unknown band value: %d", band_idx);
195 return -EINVAL;
196 }
197
198 return 0;
199 }
200
201
wifi_utils_validate_chan_str(char * chan_str)202 static int wifi_utils_validate_chan_str(char *chan_str)
203 {
204 uint8_t i;
205
206 if ((!chan_str) || (!strlen(chan_str))) {
207 NET_ERR("Null or empty channel string\n");
208 return -EINVAL;
209 }
210
211 for (i = 0; i < strlen(chan_str); i++) {
212 if (!isdigit((int)chan_str[i])) {
213 NET_ERR("Invalid character in channel string %c\n", chan_str[i]);
214 return -EINVAL;
215 }
216 }
217
218 return 0;
219 }
220
221
wifi_utils_parse_scan_bands(char * scan_bands_str,uint8_t * band_map)222 int wifi_utils_parse_scan_bands(char *scan_bands_str, uint8_t *band_map)
223 {
224 char parse_str[WIFI_MGMT_BAND_STR_SIZE_MAX + 1];
225 char *band_str = NULL;
226 char *ctx = NULL;
227 enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
228 int len;
229
230 if (!scan_bands_str) {
231 return -EINVAL;
232 }
233
234 len = strlen(scan_bands_str);
235
236 if (len > WIFI_MGMT_BAND_STR_SIZE_MAX) {
237 NET_ERR("Band string (%s) size (%d) exceeds maximum allowed value (%d)",
238 scan_bands_str,
239 len,
240 WIFI_MGMT_BAND_STR_SIZE_MAX);
241 return -EINVAL;
242 }
243
244 strncpy(parse_str, scan_bands_str, sizeof(parse_str) - 1);
245 parse_str[sizeof(parse_str) - 1] = '\0';
246
247 band_str = strtok_r(parse_str, ",", &ctx);
248
249 while (band_str) {
250 band = wifi_utils_map_band_str_to_idx(band_str);
251
252 if (band == WIFI_FREQ_BAND_UNKNOWN) {
253 NET_ERR("Invalid band value: %s", band_str);
254 return -EINVAL;
255 }
256
257 *band_map |= (1 << band);
258
259 band_str = strtok_r(NULL, ",", &ctx);
260 }
261
262 return 0;
263 }
264
wifi_utils_parse_scan_ssids(char * scan_ssids_str,const char * ssids[],uint8_t num_ssids)265 int wifi_utils_parse_scan_ssids(char *scan_ssids_str,
266 const char *ssids[],
267 uint8_t num_ssids)
268 {
269 int len;
270
271 if (!scan_ssids_str) {
272 return -EINVAL;
273 }
274
275 len = strlen(scan_ssids_str);
276 if (len > WIFI_SSID_MAX_LEN) {
277 NET_ERR("SSID string (%s) size (%d) exceeds maximum allowed value (%d)",
278 scan_ssids_str,
279 len,
280 WIFI_SSID_MAX_LEN);
281 return -EINVAL;
282 }
283
284 for (int i = 0; i < num_ssids; i++) {
285 if (ssids[i] != NULL) {
286 continue;
287 }
288 ssids[i] = scan_ssids_str;
289 return 0;
290 }
291
292 NET_WARN("Exceeded maximum allowed SSIDs (%d)", num_ssids);
293 return 0;
294 }
295
296
wifi_utils_parse_scan_chan(char * scan_chan_str,struct wifi_band_channel * band_chan,uint8_t max_channels)297 int wifi_utils_parse_scan_chan(char *scan_chan_str,
298 struct wifi_band_channel *band_chan,
299 uint8_t max_channels)
300 {
301 char band_str[WIFI_UTILS_MAX_BAND_STR_LEN] = {0};
302 char chan_str[WIFI_UTILS_MAX_CHAN_STR_LEN] = {0};
303 enum wifi_frequency_bands band = WIFI_FREQ_BAND_UNKNOWN;
304 uint16_t band_str_start_idx = 0;
305 uint16_t chan_str_start_idx = 0;
306 uint8_t chan_idx = 0;
307 uint8_t chan_start = 0;
308 uint8_t chan_val = 0;
309 uint16_t i = 0;
310 bool valid_band = false;
311 bool valid_chan = false;
312
313 while (scan_chan_str[i] != '\0') {
314 if (scan_chan_str[i] != ':') {
315 i++;
316 continue;
317 }
318
319 if (((i - band_str_start_idx) <= 0) ||
320 ((i - band_str_start_idx) > WIFI_UTILS_MAX_BAND_STR_LEN)) {
321 NET_ERR("Invalid band value %s",
322 &scan_chan_str[band_str_start_idx]);
323 return -EINVAL;
324 }
325
326 strncpy(band_str,
327 &scan_chan_str[band_str_start_idx],
328 (i - band_str_start_idx));
329
330 band = wifi_utils_map_band_str_to_idx(band_str);
331
332 if (band == WIFI_FREQ_BAND_UNKNOWN) {
333 NET_ERR("Unsupported band value: %s", band_str);
334 return -EINVAL;
335 }
336
337 i++;
338 chan_str_start_idx = i;
339 valid_band = true;
340
341 while (1) {
342 if ((scan_chan_str[i] != ',') &&
343 (scan_chan_str[i] != '_') &&
344 (scan_chan_str[i] != '-') &&
345 (scan_chan_str[i] != '\0')) {
346 i++;
347 continue;
348 }
349
350 if ((i - chan_str_start_idx) >
351 WIFI_UTILS_MAX_CHAN_STR_LEN) {
352 NET_ERR("Invalid chan value %s",
353 &scan_chan_str[chan_str_start_idx]);
354 return -EINVAL;
355 }
356
357 strncpy(chan_str,
358 &scan_chan_str[chan_str_start_idx],
359 (i - chan_str_start_idx));
360
361 if (wifi_utils_validate_chan_str(chan_str)) {
362 NET_ERR("Channel string validation failed");
363 return -EINVAL;
364 }
365
366 chan_val = atoi(chan_str);
367
368 memset(chan_str, 0, sizeof(chan_str));
369
370 if (chan_start) {
371 if ((chan_idx + (chan_val - chan_start)) > max_channels) {
372 NET_ERR("Too many channels specified (%d)", max_channels);
373 return -EINVAL;
374 }
375 if (wifi_utils_get_all_chans_in_range(chan_start,
376 chan_val,
377 band_chan,
378 band,
379 &chan_idx)) {
380 NET_ERR("Channel range invalid");
381 return -EINVAL;
382 }
383
384 chan_start = 0;
385 } else {
386 if (!wifi_utils_validate_chan(band,
387 chan_val)) {
388 NET_ERR("Invalid channel %d", chan_val);
389 return -EINVAL;
390 }
391 if (chan_idx == max_channels) {
392 NET_ERR("Too many channels specified (%d)", max_channels);
393 return -EINVAL;
394 }
395
396 band_chan[chan_idx].band = band;
397 band_chan[chan_idx].channel = chan_val;
398 chan_idx++;
399 }
400
401 valid_chan = true;
402
403 if (scan_chan_str[i] == '_') {
404 band_str_start_idx = ++i;
405 break;
406 } else if (scan_chan_str[i] == ',') {
407 chan_str_start_idx = ++i;
408 } else if (scan_chan_str[i] == '-') {
409 chan_start = chan_val;
410 chan_str_start_idx = ++i;
411 } else if (scan_chan_str[i] == '\0') {
412 break;
413 }
414 }
415 }
416
417 if (!valid_band) {
418 NET_ERR("No valid band found");
419 return -EINVAL;
420 }
421
422 if (!valid_chan) {
423 NET_ERR("No valid channel found");
424 return -EINVAL;
425 }
426
427 return 0;
428 }
429