1 /** @file mlan_11d.c
2 *
3 * @brief This file provides functions for 802.11D
4 *
5 * Copyright 2008-2024 NXP
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 */
10
11 /********************************************************
12 Change log:
13 10/21/2008: initial version
14 ********************************************************/
15
16 #include <mlan_api.h>
17
18 /* Additional WMSDK header files */
19 #include <wmerrno.h>
20 #include <osa.h>
21
22 /* Always keep this include at the end of all include files */
23 #include <mlan_remap_mem_operations.h>
24 /********************************************************
25 Local Variables
26 ********************************************************/
27
28 /** Region code mapping */
29 typedef struct _region_code_mapping
30 {
31 /** Region */
32 t_u8 region[COUNTRY_CODE_LEN];
33 /** Code */
34 t_u8 code;
35 } region_code_mapping_t;
36
37 /** Region code mapping table */
38 static const region_code_mapping_t region_code_mapping[] = {
39 {"WW ", 0x00}, /* World Wide Safe */
40 {"US ", 0x10}, /* US FCC */
41 {"CA ", 0x20}, /* IC Canada */
42 {"SG ", 0x10}, /* Singapore */
43 {"EU ", 0x30}, /* ETSI */
44 {"AU ", 0x30}, /* Australia */
45 {"KR ", 0x30}, /* Republic Of Korea */
46 {"FR ", 0x32}, /* France */
47 {"JP ", 0xFF}, /* Japan */
48 {"CN ", 0x50}, /* China */
49 };
50
51 #ifdef STA_SUPPORT
52 /** Default Tx power */
53 #define TX_PWR_DEFAULT 10
54
55 /** Universal region code */
56 #define UNIVERSAL_REGION_CODE 0xff
57
58 #define EU_REGION_CODE 0x30
59
60 /* Following two structures define the supported channels */
61 /** Channels for 802.11b/g */
62 static const chan_freq_power_t channel_freq_power_UN_BG[] = {
63 {1, 2412, TX_PWR_DEFAULT}, {2, 2417, TX_PWR_DEFAULT}, {3, 2422, TX_PWR_DEFAULT}, {4, 2427, TX_PWR_DEFAULT},
64 {5, 2432, TX_PWR_DEFAULT}, {6, 2437, TX_PWR_DEFAULT}, {7, 2442, TX_PWR_DEFAULT}, {8, 2447, TX_PWR_DEFAULT},
65 {9, 2452, TX_PWR_DEFAULT}, {10, 2457, TX_PWR_DEFAULT}, {11, 2462, TX_PWR_DEFAULT}, {12, 2467, TX_PWR_DEFAULT},
66 {13, 2472, TX_PWR_DEFAULT}, {14, 2484, TX_PWR_DEFAULT}};
67
68 #if CONFIG_5GHz_SUPPORT
69 /** Channels for 802.11a/j */
70 static chan_freq_power_t channel_freq_power_UN_AJ[] = {
71 {8, 5040, TX_PWR_DEFAULT}, {12, 5060, TX_PWR_DEFAULT}, {16, 5080, TX_PWR_DEFAULT}, {34, 5170, TX_PWR_DEFAULT},
72 {38, 5190, TX_PWR_DEFAULT}, {42, 5210, TX_PWR_DEFAULT}, {46, 5230, TX_PWR_DEFAULT}, {36, 5180, TX_PWR_DEFAULT},
73 {40, 5200, TX_PWR_DEFAULT}, {44, 5220, TX_PWR_DEFAULT}, {48, 5240, TX_PWR_DEFAULT}, {52, 5260, TX_PWR_DEFAULT},
74 {56, 5280, TX_PWR_DEFAULT}, {60, 5300, TX_PWR_DEFAULT}, {64, 5320, TX_PWR_DEFAULT}, {100, 5500, TX_PWR_DEFAULT},
75 {104, 5520, TX_PWR_DEFAULT}, {108, 5540, TX_PWR_DEFAULT}, {112, 5560, TX_PWR_DEFAULT}, {116, 5580, TX_PWR_DEFAULT},
76 {120, 5600, TX_PWR_DEFAULT}, {124, 5620, TX_PWR_DEFAULT}, {128, 5640, TX_PWR_DEFAULT}, {132, 5660, TX_PWR_DEFAULT},
77 {136, 5680, TX_PWR_DEFAULT}, {140, 5700, TX_PWR_DEFAULT}, {144, 5720, TX_PWR_DEFAULT}, {149, 5745, TX_PWR_DEFAULT},
78 {153, 5765, TX_PWR_DEFAULT}, {157, 5785, TX_PWR_DEFAULT}, {161, 5805, TX_PWR_DEFAULT},
79 #if CONFIG_UNII4_BAND_SUPPORT
80 {165, 5825, TX_PWR_DEFAULT}, {169, 5845, TX_PWR_DEFAULT}, {173, 5865, TX_PWR_DEFAULT}, {177, 5885, TX_PWR_DEFAULT},
81 #else
82 {165, 5825, TX_PWR_DEFAULT}
83 #endif
84 /* {240, 4920, TX_PWR_DEFAULT},
85 {244, 4940, TX_PWR_DEFAULT},
86 {248, 4960, TX_PWR_DEFAULT},
87 {252, 4980, TX_PWR_DEFAULT},
88 channels for 11J JP 10M channel gap */
89 };
90 #endif /* CONFIG_5GHz_SUPPORT */
91 #endif /* STA_SUPPORT */
92 /********************************************************
93 Global Variables
94 ********************************************************/
95
96 /********************************************************
97 Local Functions
98 ********************************************************/
99
100 /**
101 * @brief This function converts region string to region code
102 *
103 * @param region_string Region string
104 *
105 * @return Region code
106 */
region_string_2_region_code(t_u8 * region_string)107 t_u8 region_string_2_region_code(t_u8 *region_string)
108 {
109 t_u8 i;
110
111 ENTER();
112
113 for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++)
114 {
115 if (memcmp(region_string, region_code_mapping[i].region, COUNTRY_CODE_LEN - 1) == 0)
116 {
117 LEAVE();
118 return region_code_mapping[i].code;
119 }
120 }
121
122 if (wlan_is_etsi_country(NULL, region_string))
123 {
124 LEAVE();
125 return EU_REGION_CODE;
126 }
127
128 /* Default is WW */
129 LEAVE();
130 return region_code_mapping[0].code;
131 }
132
133 /**
134 * @brief This function converts region string to integer code
135 *
136 * @param pmadapter A pointer to mlan_adapter structure
137 * @param region Region string
138 * @param code [output] Region code
139 *
140 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
141 */
wlan_11d_region_2_code(pmlan_adapter pmadapter,t_u8 * region,OUT t_u8 * code)142 mlan_status wlan_11d_region_2_code(pmlan_adapter pmadapter, t_u8 *region, OUT t_u8 *code)
143 {
144 t_u8 i;
145 t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t);
146
147 ENTER();
148
149 if (wlan_is_etsi_country(pmadapter, region))
150 {
151 *code = EU_REGION_CODE;
152 LEAVE();
153 return MLAN_STATUS_SUCCESS;
154 }
155
156 /* Look for code in mapping table */
157 for (i = 0; i < size; i++)
158 {
159 if (!__memcmp(pmadapter, region_code_mapping[i].region, region, COUNTRY_CODE_LEN))
160 {
161 *code = region_code_mapping[i].code;
162 LEAVE();
163 return MLAN_STATUS_SUCCESS;
164 }
165 }
166
167 LEAVE();
168 return MLAN_STATUS_FAILURE;
169 }
170
171 /**
172 * @brief This function converts country index to country string
173 *
174 * @param code Country index
175 *
176 * @return Country string
177 */
wlan_11d_country_index_2_string(int country)178 const uint8_t *wlan_11d_country_index_2_string(int country)
179 {
180 if (country < 1 || country > 9)
181 {
182 return ((const uint8_t *)region_code_mapping[0].region);
183 }
184 return ((const uint8_t *)region_code_mapping[country - 1].region);
185 }
186
187 /**
188 * @brief This function converts integer code to region string
189 *
190 * @param pmadapter A pointer to mlan_adapter structure
191 * @param code Region code
192 *
193 * @return Region string
194 */
wlan_11d_code_2_region(pmlan_adapter pmadapter,t_u8 code)195 const t_u8 *wlan_11d_code_2_region(pmlan_adapter pmadapter, t_u8 code)
196 {
197 t_u8 i;
198 t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t);
199
200 ENTER();
201
202 /* Look for code in mapping table */
203 for (i = 0; i < size; i++)
204 {
205 if (region_code_mapping[i].code == code)
206 {
207 LEAVE();
208 return ((const t_u8 *)region_code_mapping[i].region);
209 }
210 }
211
212 LEAVE();
213 /* Default is WW */
214 return ((const t_u8 *)region_code_mapping[0].region);
215 }
216
217 #ifdef STA_SUPPORT
218 /**
219 * @brief This function Checks if channel txpwr is learned from AP/IBSS
220 *
221 * @param pmadapter A pointer to mlan_adapter structure
222 * @param band Band number
223 * @param chan Channel number
224 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
225 *
226 * @return MTRUE or MFALSE
227 */
wlan_11d_channel_known(pmlan_adapter pmadapter,t_u16 band,t_u8 chan,parsed_region_chan_11d_t * parsed_region_chan)228 static t_u8 wlan_11d_channel_known(pmlan_adapter pmadapter,
229 t_u16 band,
230 t_u8 chan,
231 parsed_region_chan_11d_t *parsed_region_chan)
232 {
233 chan_power_11d_t *pchan_pwr = parsed_region_chan->chan_pwr;
234 t_u8 no_of_chan = parsed_region_chan->no_of_chan;
235 t_u8 i = 0;
236 t_u8 ret = MFALSE;
237 mlan_private *pmpriv;
238
239 ENTER();
240
241 HEXDUMP("11D: parsed_region_chan", (t_u8 *)pchan_pwr, sizeof(chan_power_11d_t) * no_of_chan);
242
243 /* Search channel */
244 for (i = 0; i < no_of_chan; i++)
245 {
246 if (chan == pchan_pwr[i].chan && band == pchan_pwr[i].band)
247 {
248 PRINTM(MINFO, "11D: Found channel:%d (band:%d)\n", chan, band);
249 ret = MTRUE;
250
251 if ((band & BAND_A) != 0U)
252 {
253 /* If chan is a DFS channel, we need to see an AP on it */
254 pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA);
255 if (pmpriv != MNULL && wlan_11h_radar_detect_required(pmpriv, chan))
256 {
257 PRINTM(MINFO, "11H: DFS channel %d, and ap_seen=%d\n", chan, pchan_pwr[i].ap_seen);
258 ret = pchan_pwr[i].ap_seen;
259 }
260 }
261
262 LEAVE();
263 return ret;
264 }
265 }
266
267 PRINTM(MINFO, "11D: Could not find channel:%d (band:%d)\n", chan, band);
268 LEAVE();
269 return ret;
270 }
271
272 /**
273 * @brief This function generates parsed_region_chan from Domain Info
274 * learned from AP/IBSS
275 *
276 * @param pmadapter Pointer to mlan_adapter structure
277 * @param region_chan Pointer to region_chan_t
278 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
279 *
280 * @return N/A
281 */
wlan_11d_generate_parsed_region_chan(pmlan_adapter pmadapter,region_chan_t * region_chan,parsed_region_chan_11d_t * parsed_region_chan)282 static t_void wlan_11d_generate_parsed_region_chan(pmlan_adapter pmadapter,
283 region_chan_t *region_chan,
284 parsed_region_chan_11d_t *parsed_region_chan)
285 {
286 const chan_freq_power_t *cfp;
287 t_u8 i;
288
289 ENTER();
290
291 /* Region channel must be provided */
292 if (region_chan == MNULL)
293 {
294 PRINTM(MWARN, "11D: region_chan is MNULL\n");
295 LEAVE();
296 return;
297 }
298
299 /* Get channel-frequency-power trio */
300 cfp = region_chan->pcfp;
301 if (cfp == MNULL)
302 {
303 PRINTM(MWARN, "11D: cfp equal MNULL\n");
304 LEAVE();
305 return;
306 }
307
308 /* Set channel, band and power */
309 for (i = 0; i < region_chan->num_cfp; i++)
310 {
311 parsed_region_chan->chan_pwr[i].chan = (t_u8)cfp->channel;
312 parsed_region_chan->chan_pwr[i].band = (t_u8)region_chan->band;
313 parsed_region_chan->chan_pwr[i].pwr = (t_u8)cfp->max_tx_power;
314 PRINTM(MINFO, "11D: Chan[%d] Band[%d] Pwr[%d]\n", parsed_region_chan->chan_pwr[i].chan,
315 parsed_region_chan->chan_pwr[i].band, parsed_region_chan->chan_pwr[i].pwr);
316 cfp++;
317 }
318 parsed_region_chan->no_of_chan = region_chan->num_cfp;
319
320 PRINTM(MINFO, "11D: no_of_chan[%d]\n", parsed_region_chan->no_of_chan);
321
322 LEAVE();
323 return;
324 }
325
326 /**
327 * @brief This function generates domain_info from parsed_region_chan
328 *
329 * @param pmadapter Pointer to mlan_adapter structure
330 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
331 *
332 * @return MLAN_STATUS_SUCCESS
333 */
wlan_11d_generate_domain_info(pmlan_adapter pmadapter,parsed_region_chan_11d_t * parsed_region_chan)334 static mlan_status wlan_11d_generate_domain_info(pmlan_adapter pmadapter, parsed_region_chan_11d_t *parsed_region_chan)
335 {
336 t_u8 no_of_sub_band = 0;
337 t_u8 no_of_chan = parsed_region_chan->no_of_chan;
338 t_u8 no_of_parsed_chan = 0;
339 t_u8 first_chan = 0, next_chan = 0, max_pwr = 0;
340 t_u8 i, flag = MFALSE;
341 wlan_802_11d_domain_reg_t *domain_info = &pmadapter->domain_reg;
342
343 ENTER();
344
345 /* Should be only place that clear domain_reg (besides init) */
346 (void)__memset(pmadapter, domain_info, 0, sizeof(wlan_802_11d_domain_reg_t));
347
348 /* Set country code */
349 (void)__memcpy(pmadapter, domain_info->country_code,
350 wlan_11d_code_2_region(pmadapter, (t_u8)pmadapter->region_code), COUNTRY_CODE_LEN);
351
352 PRINTM(MINFO, "11D: Number of channel = %d\n", no_of_chan);
353 HEXDUMP("11D: parsed_region_chan", (t_u8 *)parsed_region_chan, sizeof(parsed_region_chan_11d_t));
354
355 /* Set channel and power */
356 for (i = 0; i < no_of_chan; i++)
357 {
358 if (!flag)
359 {
360 flag = MTRUE;
361 next_chan = first_chan = parsed_region_chan->chan_pwr[i].chan;
362 max_pwr = parsed_region_chan->chan_pwr[i].pwr;
363 no_of_parsed_chan = 1;
364 continue;
365 }
366
367 if (parsed_region_chan->chan_pwr[i].chan == next_chan + 1U && parsed_region_chan->chan_pwr[i].pwr == max_pwr)
368 {
369 next_chan++;
370 no_of_parsed_chan++;
371 }
372 else
373 {
374 domain_info->sub_band[no_of_sub_band].first_chan = first_chan;
375 domain_info->sub_band[no_of_sub_band].no_of_chan = no_of_parsed_chan;
376 domain_info->sub_band[no_of_sub_band].max_tx_pwr = max_pwr;
377 no_of_sub_band++;
378 no_of_parsed_chan = 1;
379 next_chan = first_chan = parsed_region_chan->chan_pwr[i].chan;
380 max_pwr = parsed_region_chan->chan_pwr[i].pwr;
381 }
382 }
383
384 if (flag != 0U)
385 {
386 domain_info->sub_band[no_of_sub_band].first_chan = first_chan;
387 domain_info->sub_band[no_of_sub_band].no_of_chan = no_of_parsed_chan;
388 domain_info->sub_band[no_of_sub_band].max_tx_pwr = max_pwr;
389 no_of_sub_band++;
390 }
391 domain_info->no_of_sub_band = no_of_sub_band;
392
393 PRINTM(MINFO, "11D: Number of sub-band =0x%x\n", domain_info->no_of_sub_band);
394 HEXDUMP("11D: domain_info", (t_u8 *)domain_info,
395 COUNTRY_CODE_LEN + 1 + sizeof(IEEEtypes_SubbandSet_t) * no_of_sub_band);
396 LEAVE();
397 return MLAN_STATUS_SUCCESS;
398 }
399
400 /**
401 * @brief This function updates the channel power table with the channel
402 * present in BSSDescriptor.
403 *
404 * @param pmpriv A pointer to mlan_private structure
405 * @param pbss_desc A pointer to BSSDescriptor_t
406 *
407 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
408 */
wlan_11d_update_chan_pwr_table(mlan_private * pmpriv,BSSDescriptor_t * pbss_desc)409 static mlan_status wlan_11d_update_chan_pwr_table(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc)
410 {
411 mlan_adapter *pmadapter = pmpriv->adapter;
412 parsed_region_chan_11d_t *parsed_region_chan = &pmadapter->parsed_region_chan;
413 t_u16 i;
414 t_u8 tx_power = 0;
415 t_u8 chan;
416
417 ENTER();
418
419 chan = pbss_desc->phy_param_set.ds_param_set.current_chan;
420
421 tx_power = wlan_get_txpwr_of_chan_from_cfp(pmpriv, chan);
422
423 if (!tx_power)
424 {
425 PRINTM(MMSG, "11D: Invalid channel\n");
426 LEAVE();
427 return MLAN_STATUS_FAILURE;
428 }
429
430 /* Check whether the channel already exists in channel power table of
431 parsed region */
432 for (i = 0; ((i < parsed_region_chan->no_of_chan) && (i < MAX_NO_OF_CHAN)); i++)
433 {
434 if (parsed_region_chan->chan_pwr[i].chan == chan && parsed_region_chan->chan_pwr[i].band == pbss_desc->bss_band)
435 {
436 /* Channel already exists, use minimum of existing and tx_power */
437 parsed_region_chan->chan_pwr[i].pwr = MIN(parsed_region_chan->chan_pwr[i].pwr, tx_power);
438 parsed_region_chan->chan_pwr[i].ap_seen = MTRUE;
439 break;
440 }
441 }
442
443 if (i == parsed_region_chan->no_of_chan && i < MAX_NO_OF_CHAN)
444 {
445 /* Channel not found. Update the channel in the channel-power table */
446 parsed_region_chan->chan_pwr[i].chan = chan;
447 parsed_region_chan->chan_pwr[i].band = (t_u8)pbss_desc->bss_band;
448 parsed_region_chan->chan_pwr[i].pwr = tx_power;
449 parsed_region_chan->chan_pwr[i].ap_seen = MTRUE;
450 parsed_region_chan->no_of_chan++;
451 }
452
453 LEAVE();
454 return MLAN_STATUS_SUCCESS;
455 }
456
457 /**
458 * @brief This function finds the no_of_chan-th chan after the first_chan
459 *
460 * @param pmadapter A pointer to mlan_adapter structure
461 * @param band Band
462 * @param first_chan First channel number
463 * @param no_of_chan Number of channels
464 * @param chan Pointer to the returned no_of_chan-th chan number
465 *
466 * @return MTRUE or MFALSE
467 */
wlan_11d_get_chan(pmlan_adapter pmadapter,t_u16 band,t_u8 first_chan,t_u8 no_of_chan,t_u8 * chan)468 static t_u8 wlan_11d_get_chan(pmlan_adapter pmadapter, t_u16 band, t_u8 first_chan, t_u8 no_of_chan, t_u8 *chan)
469 {
470 const chan_freq_power_t *cfp = MNULL;
471 t_u8 i;
472 t_u8 cfp_no = 0;
473
474 ENTER();
475 if ((band & (BAND_B | BAND_G | BAND_GN)) != 0U)
476 {
477 cfp = (const chan_freq_power_t *)channel_freq_power_UN_BG;
478 cfp_no = sizeof(channel_freq_power_UN_BG) / sizeof(chan_freq_power_t);
479 }
480 #if CONFIG_5GHz_SUPPORT
481 else if ((band & (BAND_A | BAND_AN)) != 0U)
482 {
483 cfp = channel_freq_power_UN_AJ;
484 cfp_no = sizeof(channel_freq_power_UN_AJ) / sizeof(chan_freq_power_t);
485 }
486 #endif /* CONFIG_5GHz_SUPPORT */
487 else
488 {
489 PRINTM(MERROR, "11D: Wrong Band[%d]\n", band);
490 LEAVE();
491 return MFALSE;
492 }
493 /* Locate the first_chan */
494 for (i = 0; i < cfp_no; i++)
495 {
496 if ((cfp + i)->channel == first_chan)
497 {
498 PRINTM(MINFO, "11D: first_chan found\n");
499 break;
500 }
501 }
502
503 if (i < cfp_no)
504 {
505 /* Check if beyond the boundary */
506 if (i + no_of_chan < cfp_no)
507 {
508 /* Get first_chan + no_of_chan */
509 *chan = (t_u8)(cfp + i + no_of_chan)->channel;
510 LEAVE();
511 return MTRUE;
512 }
513 }
514
515 LEAVE();
516 return MFALSE;
517 }
518
519 /**
520 * @brief This function processes the country info present in BSSDescriptor.
521 *
522 * @param pmpriv A pointer to mlan_private structure
523 * @param pbss_desc A pointer to BSSDescriptor_t
524 *
525 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
526 */
wlan_11d_process_country_info(mlan_private * pmpriv,BSSDescriptor_t * pbss_desc)527 static mlan_status wlan_11d_process_country_info(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc)
528 {
529 mlan_adapter *pmadapter = pmpriv->adapter;
530 parsed_region_chan_11d_t region_chan;
531 parsed_region_chan_11d_t *parsed_region_chan = &pmadapter->parsed_region_chan;
532 t_u16 i, j, num_chan_added = 0;
533
534 ENTER();
535
536 (void)__memset(pmadapter, ®ion_chan, 0, sizeof(parsed_region_chan_11d_t));
537
538 /* Parse 11D country info */
539 if (wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info, pbss_desc->bss_band, ®ion_chan) !=
540 MLAN_STATUS_SUCCESS)
541 {
542 LEAVE();
543 return MLAN_STATUS_FAILURE;
544 }
545
546 if (parsed_region_chan->no_of_chan != 0U)
547 {
548 /*
549 * Check if the channel number already exists in the
550 * chan-power table of parsed_region_chan
551 */
552 for (i = 0; (i < region_chan.no_of_chan && i < MAX_NO_OF_CHAN); i++)
553 {
554 for (j = 0; (j < parsed_region_chan->no_of_chan && j < MAX_NO_OF_CHAN); j++)
555 {
556 /*
557 * Channel already exists, update the tx power with new tx
558 * power, since country IE is valid here.
559 */
560 if (region_chan.chan_pwr[i].chan == parsed_region_chan->chan_pwr[j].chan &&
561 region_chan.chan_pwr[i].band == parsed_region_chan->chan_pwr[j].band)
562 {
563 parsed_region_chan->chan_pwr[j].pwr = region_chan.chan_pwr[i].pwr;
564 break;
565 }
566 }
567
568 if (j == parsed_region_chan->no_of_chan && j < MAX_NO_OF_CHAN)
569 {
570 if (parsed_region_chan->no_of_chan + num_chan_added >= MAX_NO_OF_CHAN)
571 {
572 break;
573 }
574 /*
575 * Channel does not exist in the channel power table,
576 * update this new chan and tx_power to the channel power table
577 */
578 parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan + num_chan_added].chan =
579 region_chan.chan_pwr[i].chan;
580 parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan + num_chan_added].band =
581 region_chan.chan_pwr[i].band;
582 parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan + num_chan_added].pwr =
583 region_chan.chan_pwr[i].pwr;
584 parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan + num_chan_added].ap_seen = MFALSE;
585 num_chan_added++;
586 }
587 }
588 parsed_region_chan->no_of_chan += (t_u8)num_chan_added;
589 if (parsed_region_chan->no_of_chan > MAX_NO_OF_CHAN)
590 {
591 parsed_region_chan->no_of_chan = MAX_NO_OF_CHAN;
592 }
593 }
594 else
595 {
596 /* Parsed region is empty, copy the first one */
597 (void)__memcpy(pmadapter, parsed_region_chan, ®ion_chan, sizeof(parsed_region_chan_11d_t));
598 }
599
600 LEAVE();
601 return MLAN_STATUS_SUCCESS;
602 }
603
604 /**
605 * @brief This helper function copies chan_power_11d_t element
606 *
607 * @param chan_dst Pointer to destination of chan_power
608 * @param chan_src Pointer to source of chan_power
609 *
610 * @return N/A
611 */
wlan_11d_copy_chan_power(chan_power_11d_t * chan_dst,chan_power_11d_t * chan_src)612 static t_void wlan_11d_copy_chan_power(chan_power_11d_t *chan_dst, chan_power_11d_t *chan_src)
613 {
614 ENTER();
615
616 chan_dst->chan = chan_src->chan;
617 chan_dst->band = chan_src->band;
618 chan_dst->pwr = chan_src->pwr;
619 chan_dst->ap_seen = chan_src->ap_seen;
620
621 LEAVE();
622 return;
623 }
624
625 /**
626 * @brief This function sorts parsed_region_chan in ascending
627 * channel number.
628 *
629 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
630 *
631 * @return N/A
632 */
wlan_11d_sort_parsed_region_chan(parsed_region_chan_11d_t * parsed_region_chan)633 static t_void wlan_11d_sort_parsed_region_chan(parsed_region_chan_11d_t *parsed_region_chan)
634 {
635 t_u8 i, j;
636 chan_power_11d_t temp;
637 chan_power_11d_t *pchan_power = parsed_region_chan->chan_pwr;
638
639 ENTER();
640
641 PRINTM(MINFO, "11D: Number of channel = %d\n", parsed_region_chan->no_of_chan);
642
643 // Use insertion sort method
644 for (i = 1; i < parsed_region_chan->no_of_chan; i++)
645 {
646 wlan_11d_copy_chan_power(&temp, pchan_power + i);
647 for (j = i; j > 0 && (pchan_power + j - 1)->chan > temp.chan; j--)
648 {
649 wlan_11d_copy_chan_power(pchan_power + j, pchan_power + j - 1);
650 }
651 wlan_11d_copy_chan_power(pchan_power + j, &temp);
652 }
653
654 HEXDUMP("11D: parsed_region_chan", (t_u8 *)parsed_region_chan, sizeof(parsed_region_chan_11d_t));
655
656 LEAVE();
657 return;
658 }
659 #endif /* STA_SUPPORT */
660
661 /**
662 * @brief This function sends domain info to FW
663 *
664 * @param pmpriv A pointer to mlan_private structure
665 * @param pioctl_buf A pointer to MLAN IOCTL Request buffer
666 *
667 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
668 */
wlan_11d_send_domain_info(mlan_private * pmpriv,t_void * pioctl_buf,t_bool is_op_special_set)669 static mlan_status wlan_11d_send_domain_info(mlan_private *pmpriv, t_void *pioctl_buf, t_bool is_op_special_set)
670 {
671 mlan_status ret = MLAN_STATUS_SUCCESS;
672
673 ENTER();
674
675 /* Send cmd to FW to set domain info */
676 if (is_op_special_set)
677 {
678 ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_SPC_SET, 0, (t_void *)pioctl_buf,
679 MNULL);
680 }
681 else
682 {
683 ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_buf,
684 MNULL);
685 }
686 if (ret != MLAN_STATUS_SUCCESS)
687 {
688 PRINTM(MERROR, "11D: Failed to download domain Info\n");
689 }
690
691 LEAVE();
692 return ret;
693 }
694
695 /**
696 * @brief This function overwrites domain_info
697 *
698 * @param pmadapter Pointer to mlan_adapter structure
699 * @param band Intended operating band
700 * @param country_code Intended country code
701 * @param num_sub_band Count of tuples in list below
702 * @param sub_band_list List of sub_band tuples
703 *
704 * @return MLAN_STATUS_SUCCESS
705 */
wlan_11d_set_domain_info(mlan_private * pmpriv,t_u16 band,const t_u8 country_code[COUNTRY_CODE_LEN],t_u8 num_sub_band,IEEEtypes_SubbandSet_t * sub_band_list)706 mlan_status wlan_11d_set_domain_info(mlan_private *pmpriv,
707 t_u16 band,
708 const t_u8 country_code[COUNTRY_CODE_LEN],
709 t_u8 num_sub_band,
710 IEEEtypes_SubbandSet_t *sub_band_list)
711 {
712 mlan_adapter *pmadapter = pmpriv->adapter;
713 wlan_802_11d_domain_reg_t *pdomain = &pmadapter->domain_reg;
714 mlan_status ret = MLAN_STATUS_SUCCESS;
715
716 ENTER();
717
718 (void)__memset(pmadapter, pdomain, 0, sizeof(wlan_802_11d_domain_reg_t));
719 (void)__memcpy(pmadapter, pdomain->country_code, country_code, COUNTRY_CODE_LEN);
720 pdomain->band = band;
721 pdomain->no_of_sub_band = num_sub_band;
722 (void)__memcpy(pmadapter, pdomain->sub_band, sub_band_list,
723 MIN(MRVDRV_MAX_SUBBAND_802_11D, num_sub_band) * sizeof(IEEEtypes_SubbandSet_t));
724
725 LEAVE();
726 return ret;
727 }
728
729 /********************************************************
730 Global functions
731 ********************************************************/
732
733 /**
734 * @brief This function gets if priv is a station (STA)
735 *
736 * @param pmpriv Pointer to mlan_private structure
737 *
738 * @return MTRUE or MFALSE
739 */
wlan_is_station(mlan_private * pmpriv)740 t_bool wlan_is_station(mlan_private *pmpriv)
741 {
742 ENTER();
743 LEAVE();
744 return (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) ? MTRUE : MFALSE;
745 }
746
747 /**
748 * @brief This function gets if 11D is enabled
749 *
750 * @param pmpriv Pointer to mlan_private structure
751 *
752 * @return MTRUE or MFALSE
753 */
wlan_11d_support_is_enabled(mlan_private * pmpriv)754 t_bool wlan_11d_support_is_enabled(mlan_private *pmpriv)
755 {
756 ENTER();
757 LEAVE();
758 return (pmpriv->state_11d.user_enable_11d_support == ENABLE_11D) ? MTRUE : MFALSE;
759 }
760
761 /**
762 * @brief This function gets if 11D is enabled
763 *
764 * @param pmpriv Pointer to mlan_private structure
765 *
766 * @return MTRUE or MFALSE
767 */
wlan_11d_is_enabled(mlan_private * pmpriv)768 t_bool wlan_11d_is_enabled(mlan_private *pmpriv)
769 {
770 ENTER();
771 LEAVE();
772 return (pmpriv->state_11d.enable_11d == ENABLE_11D) ? MTRUE : MFALSE;
773 }
774
775 static wlan_11d_fn_t wlan_11d_fn = {
776 #ifdef STA_SUPPORT
777 .wlan_11d_prepare_dnld_domain_info_cmd_p = wlan_11d_prepare_dnld_domain_info_cmd,
778 .wlan_11d_create_dnld_countryinfo_p = wlan_11d_create_dnld_countryinfo,
779 .wlan_11d_parse_dnld_countryinfo_p = wlan_11d_parse_dnld_countryinfo,
780 #endif
781 };
782
783 static void *wlan_11d_enable_support = (wlan_11d_fn_t *)&wlan_11d_fn;
784
785 /**
786 * @brief This function sets 11D support
787 *
788 * @param pmpriv Pointer to mlan_private structure
789 *
790 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
791 */
wlan_enable_11d_support(mlan_private * pmpriv)792 t_u16 wlan_enable_11d_support(mlan_private *pmpriv)
793 {
794 ENTER();
795 LEAVE();
796
797 pmpriv->state_11d.user_enable_11d_support = ENABLE_11D;
798
799 pmpriv->support_11d = wlan_11d_enable_support;
800
801 return (t_u16)MLAN_STATUS_SUCCESS;
802 }
803
804 static wlan_11d_apis_t wlan_11d_apis = {
805 .wlan_11d_cfg_ioctl_p = wlan_11d_cfg_ioctl,
806 .wlan_11d_cfg_domain_info_p = wlan_11d_cfg_domain_info,
807 .wlan_cmd_802_11d_domain_info_p = wlan_cmd_802_11d_domain_info,
808 #if UAP_SUPPORT
809 .wlan_11d_handle_uap_domain_info_p = wlan_11d_handle_uap_domain_info,
810 #endif
811 };
812
813 static void *wlan_11d_support_apis = (wlan_11d_apis_t *)&wlan_11d_apis;
814
815 /**
816 * @brief This function enables 11D driver APIs
817 *
818 * @param pmpriv Pointer to mlan_private structure
819 *
820 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
821 */
wlan_11d_support_APIs(mlan_private * pmpriv)822 t_u16 wlan_11d_support_APIs(mlan_private *pmpriv)
823 {
824 ENTER();
825 LEAVE();
826
827 pmpriv->support_11d_APIs = wlan_11d_support_apis;
828
829 return (t_u16)MLAN_STATUS_SUCCESS;
830 }
831
832 /**
833 * @brief Initialize interface variable for 11D
834 *
835 * @param pmpriv Pointer to mlan_private structure
836 *
837 * @return N/A
838 */
wlan_11d_priv_init(mlan_private * pmpriv)839 t_void wlan_11d_priv_init(mlan_private *pmpriv)
840 {
841 wlan_802_11d_state_t *state = &pmpriv->state_11d;
842
843 ENTER();
844
845 /* Start in disabled mode */
846 state->enable_11d = DISABLE_11D;
847 if (!pmpriv->adapter->init_para.cfg_11d)
848 {
849 state->user_enable_11d = DEFAULT_11D_STATE;
850 }
851 else
852 {
853 state->user_enable_11d =
854 (pmpriv->adapter->init_para.cfg_11d == MLAN_INIT_PARA_DISABLED) ? DISABLE_11D : ENABLE_11D;
855 }
856
857 LEAVE();
858 return;
859 }
860
861 /**
862 * @brief Initialize device variable for 11D
863 *
864 * @param pmadapter Pointer to mlan_adapter structure
865 *
866 * @return N/A
867 */
wlan_11d_init(mlan_adapter * pmadapter)868 t_void wlan_11d_init(mlan_adapter *pmadapter)
869 {
870 ENTER();
871
872 #ifdef STA_SUPPORT
873 (void)__memset(pmadapter, &(pmadapter->parsed_region_chan), 0, sizeof(parsed_region_chan_11d_t));
874 (void)__memset(pmadapter, &(pmadapter->universal_channel), 0, sizeof(region_chan_t));
875 #endif
876 (void)__memset(pmadapter, &(pmadapter->domain_reg), 0, sizeof(wlan_802_11d_domain_reg_t));
877
878 LEAVE();
879 return;
880 }
881
882 /**
883 * @brief This function implements command CMD_802_11D_DOMAIN_INFO
884 *
885 * @param pmpriv A pointer to mlan_private structure
886 * @param pcmd A pointer to HostCmd_DS_COMMAND structure of
887 * command buffer
888 * @param cmd_action Command action
889 *
890 * @return MLAN_STATUS_SUCCESS
891 */
wlan_cmd_802_11d_domain_info(mlan_private * pmpriv,HostCmd_DS_COMMAND * pcmd,t_u16 cmd_action)892 mlan_status wlan_cmd_802_11d_domain_info(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd, t_u16 cmd_action)
893 {
894 mlan_adapter *pmadapter = pmpriv->adapter;
895 HostCmd_DS_802_11D_DOMAIN_INFO *pdomain_info = &pcmd->params.domain_info;
896 MrvlIEtypes_DomainParamSet_t *domain = &pdomain_info->domain;
897 t_u8 no_of_sub_band = pmadapter->domain_reg.no_of_sub_band;
898
899 ENTER();
900
901 PRINTM(MINFO, "11D: number of sub-band=0x%x\n", no_of_sub_band);
902
903 pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO);
904 pdomain_info->action = wlan_cpu_to_le16(cmd_action);
905 if (cmd_action == HostCmd_ACT_GEN_GET)
906 {
907 /* Dump domain info */
908 pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + S_DS_GEN);
909 HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *)pcmd, wlan_le16_to_cpu(pcmd->size));
910 LEAVE();
911 return MLAN_STATUS_SUCCESS;
912 }
913
914 /* Set domain info fields */
915 domain->header.type = wlan_cpu_to_le16(TLV_TYPE_DOMAIN);
916 (void)__memcpy(pmadapter, domain->country_code, pmadapter->domain_reg.country_code, sizeof(domain->country_code));
917
918 domain->header.len = (t_u16)(((no_of_sub_band * sizeof(IEEEtypes_SubbandSet_t)) + sizeof(domain->country_code)));
919
920 if (no_of_sub_band != 0U)
921 {
922 (void)__memcpy(pmadapter, domain->sub_band, pmadapter->domain_reg.sub_band,
923 MIN(MRVDRV_MAX_SUBBAND_802_11D, no_of_sub_band) * sizeof(IEEEtypes_SubbandSet_t));
924
925 pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + domain->header.len + sizeof(MrvlIEtypesHeader_t) +
926 S_DS_GEN);
927 }
928 else
929 {
930 pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + S_DS_GEN);
931 }
932 domain->header.len = wlan_cpu_to_le16(domain->header.len);
933
934 HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *)pcmd, wlan_le16_to_cpu(pcmd->size));
935
936 LEAVE();
937 return MLAN_STATUS_SUCCESS;
938 }
939
940 #ifdef STA_SUPPORT
941
942 /** Region code mapping */
943 typedef struct _region_code_t
944 {
945 /** Region */
946 t_u8 region[COUNTRY_CODE_LEN];
947 } region_code_t;
948
949 /**
950 * @brief This function check cfg80211 special region code.
951 *
952 * @param region_string Region string
953 *
954 * @return MTRUE/MFALSE
955 */
is_special_region_code(t_u8 * region_string)956 t_u8 is_special_region_code(t_u8 *region_string)
957 {
958 t_u8 i;
959 region_code_t special_region_code[] = {{"00 "}, {"99 "}, {"98 "}, {"97 "}};
960
961 for (i = 0; i < COUNTRY_CODE_LEN && region_string[i]; i++)
962 region_string[i] = toupper(region_string[i]);
963
964 for (i = 0; i < ARRAY_SIZE(special_region_code); i++)
965 {
966 if (!memcmp(region_string, special_region_code[i].region, COUNTRY_CODE_LEN))
967 {
968 PRINTM(MINFO, "special region code=%s\n", region_string);
969 return MTRUE;
970 }
971 }
972 return MFALSE;
973 }
974
975 /**
976 * @brief This function parses country information for region channel
977 *
978 * @param pmadapter Pointer to mlan_adapter structure
979 * @param country_info Country information
980 * @param band Chan band
981 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
982 *
983 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
984 */
wlan_11d_parse_domain_info(pmlan_adapter pmadapter,IEEEtypes_CountryInfoFullSet_t * country_info,t_u16 band,parsed_region_chan_11d_t * parsed_region_chan)985 mlan_status wlan_11d_parse_domain_info(pmlan_adapter pmadapter,
986 IEEEtypes_CountryInfoFullSet_t *country_info,
987 t_u16 band,
988 parsed_region_chan_11d_t *parsed_region_chan)
989 {
990 t_u8 no_of_sub_band, no_of_chan;
991 t_u8 last_chan, first_chan, cur_chan = 0;
992 t_u8 idx = 0;
993 t_u8 j, i;
994
995 ENTER();
996
997 /*
998 * Validation Rules:
999 * 1. Valid Region Code
1000 * 2. First Chan increment
1001 * 3. Channel range no overlap
1002 * 4. Channel is valid?
1003 * 5. Channel is supported by Region?
1004 * 6. Others
1005 */
1006
1007 HEXDUMP("country_info", (t_u8 *)country_info, 30);
1008 /* Step 1: Check region_code */
1009 if (!(*(country_info->country_code)) || (country_info->len <= COUNTRY_CODE_LEN))
1010 {
1011 /* No region info or wrong region info: treat as no 11D info */
1012 LEAVE();
1013 return MLAN_STATUS_FAILURE;
1014 }
1015
1016 no_of_sub_band = (t_u8)((country_info->len - COUNTRY_CODE_LEN) / sizeof(IEEEtypes_SubbandSet_t));
1017
1018 last_chan = 0;
1019 for (j = 0; j < no_of_sub_band; j++)
1020 {
1021 if (country_info->sub_band[j].first_chan <= last_chan)
1022 {
1023 /* Step2&3: Check First Chan Num increment and no overlap */
1024 PRINTM(MINFO, "11D: Chan[%d>%d] Overlap\n", country_info->sub_band[j].first_chan, last_chan);
1025 continue;
1026 }
1027
1028 first_chan = country_info->sub_band[j].first_chan;
1029 no_of_chan = country_info->sub_band[j].no_of_chan;
1030
1031 i = 0;
1032 while (idx < MAX_NO_OF_CHAN && i < no_of_chan)
1033 {
1034 /* Step 4 : Channel is supported? */
1035 if (wlan_11d_get_chan(pmadapter, band, first_chan, i, &cur_chan) == MFALSE)
1036 {
1037 /* Chan is not found in UN table */
1038 PRINTM(MWARN, "11D: channel is not supported: %d\n", i);
1039 break;
1040 }
1041
1042 last_chan = cur_chan;
1043
1044 /* Step 5: We don't need to check if cur_chan is supported by mrvl
1045 in region */
1046 parsed_region_chan->chan_pwr[idx].chan = cur_chan;
1047 parsed_region_chan->chan_pwr[idx].band = (t_u8)band;
1048 parsed_region_chan->chan_pwr[idx].pwr = country_info->sub_band[j].max_tx_pwr;
1049 idx++;
1050 i++;
1051 }
1052
1053 /* Step 6: Add other checking if any */
1054 }
1055
1056 parsed_region_chan->no_of_chan = idx;
1057
1058 PRINTM(MINFO, "11D: number of channel=0x%x\n", parsed_region_chan->no_of_chan);
1059 HEXDUMP("11D: parsed_region_chan", (t_u8 *)parsed_region_chan->chan_pwr, sizeof(chan_power_11d_t) * idx);
1060
1061 LEAVE();
1062 return MLAN_STATUS_SUCCESS;
1063 }
1064
1065 /**
1066 * @brief This function converts channel to frequency
1067 *
1068 * @param pmadapter A pointer to mlan_adapter structure
1069 * @param chan Channel number
1070 * @param band Band
1071 *
1072 * @return Channel frequency
1073 */
wlan_11d_chan_2_freq(pmlan_adapter pmadapter,t_u8 chan,t_u16 band)1074 t_u32 wlan_11d_chan_2_freq(pmlan_adapter pmadapter, t_u8 chan, t_u16 band)
1075 {
1076 const chan_freq_power_t *cf;
1077 t_u16 cnt;
1078 t_u16 i;
1079 t_u32 freq = 0;
1080
1081 ENTER();
1082
1083 #if CONFIG_5GHz_SUPPORT
1084 /* Get channel-frequency-power trios */
1085 if ((band & (BAND_A | BAND_AN)) != 0)
1086 {
1087 cf = channel_freq_power_UN_AJ;
1088 cnt = sizeof(channel_freq_power_UN_AJ) / sizeof(chan_freq_power_t);
1089 }
1090 else
1091 {
1092 #endif /* CONFIG_5GHz_SUPPORT */
1093 cf = (const chan_freq_power_t *)channel_freq_power_UN_BG;
1094 cnt = sizeof(channel_freq_power_UN_BG) / sizeof(chan_freq_power_t);
1095 #if CONFIG_5GHz_SUPPORT
1096 }
1097 #endif /* CONFIG_5GHz_SUPPORT */
1098
1099 /* Locate channel and return corresponding frequency */
1100 for (i = 0; i < cnt; i++)
1101 {
1102 if (chan == cf[i].channel)
1103 {
1104 freq = cf[i].freq;
1105 }
1106 }
1107
1108 LEAVE();
1109 return freq;
1110 }
1111
1112 /**
1113 * @brief This function setups scan channels
1114 *
1115 * @param pmpriv Pointer to mlan_private structure
1116 * @param band Band
1117 *
1118 * @return MLAN_STATUS_SUCCESS
1119 */
wlan_11d_set_universaltable(mlan_private * pmpriv,t_u16 band)1120 mlan_status wlan_11d_set_universaltable(mlan_private *pmpriv, t_u16 band)
1121 {
1122 mlan_adapter *pmadapter = pmpriv->adapter;
1123 t_u16 size = (t_u16)sizeof(chan_freq_power_t);
1124 t_u16 i = 0;
1125
1126 ENTER();
1127
1128 (void)__memset(pmadapter, pmadapter->universal_channel, 0, sizeof(pmadapter->universal_channel));
1129
1130 if ((band & (BAND_B | BAND_G | BAND_GN)) != 0U)
1131 /* If band B, G or N */
1132 {
1133 /* Set channel-frequency-power */
1134 pmadapter->universal_channel[i].num_cfp = (t_u8)(sizeof(channel_freq_power_UN_BG) / size);
1135 PRINTM(MINFO, "11D: BG-band num_cfp=%d\n", pmadapter->universal_channel[i].num_cfp);
1136
1137 pmadapter->universal_channel[i].pcfp = (const chan_freq_power_t *)channel_freq_power_UN_BG;
1138 pmadapter->universal_channel[i].valid = MTRUE;
1139
1140 /* Set region code */
1141 pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
1142
1143 /* Set band */
1144 if ((band & BAND_GN) != 0U)
1145 {
1146 pmadapter->universal_channel[i].band = BAND_G;
1147 }
1148 else
1149 {
1150 pmadapter->universal_channel[i].band = (band & BAND_G) ? BAND_G : BAND_B;
1151 }
1152 i++;
1153 }
1154
1155 #if CONFIG_5GHz_SUPPORT
1156 #if CONFIG_11AC
1157 if ((band & (BAND_A | BAND_AN | BAND_AAC)) != 0U)
1158 {
1159 #else
1160 if ((band & (BAND_A | BAND_AN)) != 0U)
1161 {
1162 #endif
1163 /* If band A */
1164
1165 /* Set channel-frequency-power */
1166 pmadapter->universal_channel[i].num_cfp = (t_u8)(sizeof(channel_freq_power_UN_AJ) / size);
1167 PRINTM(MINFO, "11D: AJ-band num_cfp=%d\n", pmadapter->universal_channel[i].num_cfp);
1168
1169 pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_AJ;
1170
1171 pmadapter->universal_channel[i].valid = MTRUE;
1172
1173 /* Set region code */
1174 pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
1175
1176 /* Set band */
1177 pmadapter->universal_channel[i].band = BAND_A;
1178 }
1179 #endif /* CONFIG_5GHz_SUPPORT */
1180
1181 LEAVE();
1182 return MLAN_STATUS_SUCCESS;
1183 }
1184
1185 /**
1186 * @brief This function calculates the scan type for channels
1187 *
1188 * @param pmadapter A pointer to mlan_adapter structure
1189 * @param band Band number
1190 * @param chan Chan number
1191 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
1192 *
1193 * @return PASSIVE if chan is unknown; ACTIVE if chan is known
1194 */
1195 mlan_scan_type wlan_11d_get_scan_type(mlan_private *pmpriv,
1196 t_u16 band,
1197 t_u8 chan,
1198 parsed_region_chan_11d_t *parsed_region_chan)
1199 {
1200 mlan_adapter *pmadapter = pmpriv->adapter;
1201
1202 mlan_scan_type scan_type = MLAN_SCAN_TYPE_PASSIVE;
1203
1204 ENTER();
1205
1206 if (wlan_11d_channel_known(pmadapter, band, chan, parsed_region_chan) != 0U)
1207 {
1208 /* Channel found */
1209 PRINTM(MINFO, "11D: Channel found and doing Active Scan\n");
1210 scan_type = MLAN_SCAN_TYPE_ACTIVE;
1211 }
1212 else
1213 {
1214 PRINTM(MINFO, "11D: Channel not found and doing Passive Scan\n");
1215 }
1216
1217 LEAVE();
1218 return scan_type;
1219 }
1220
1221 /**
1222 * @brief This function clears the parsed region table, if 11D is enabled
1223 *
1224 * @param pmpriv A pointer to mlan_private structure
1225 *
1226 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1227 */
1228 mlan_status wlan_11d_clear_parsedtable(mlan_private *pmpriv)
1229 {
1230 mlan_adapter *pmadapter = pmpriv->adapter;
1231 mlan_status ret = MLAN_STATUS_SUCCESS;
1232
1233 ENTER();
1234
1235 if (wlan_11d_is_enabled(pmpriv))
1236 {
1237 (void)__memset(pmadapter, &(pmadapter->parsed_region_chan), 0, sizeof(parsed_region_chan_11d_t));
1238 }
1239 else
1240 {
1241 ret = MLAN_STATUS_FAILURE;
1242 }
1243
1244 LEAVE();
1245 return ret;
1246 }
1247
1248 /**
1249 * @brief This function generates 11D info from user specified regioncode
1250 * and download to FW
1251 *
1252 * @param pmpriv A pointer to mlan_private structure
1253 * @param band Band to create
1254 *
1255 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1256 */
1257 mlan_status wlan_11d_create_dnld_countryinfo(mlan_private *pmpriv, t_u16 band)
1258 {
1259 mlan_status ret = MLAN_STATUS_SUCCESS;
1260 mlan_adapter *pmadapter = pmpriv->adapter;
1261 region_chan_t *region_chan;
1262 parsed_region_chan_11d_t parsed_region_chan;
1263 t_u8 j;
1264
1265 ENTER();
1266
1267 /* Only valid if 11D is enabled */
1268 if (wlan_11d_is_enabled(pmpriv))
1269 {
1270 PRINTM(MINFO, "11D: Band[%d]\n", band);
1271
1272 /* Update parsed_region_chan; download domain info to FW */
1273
1274 /* Find region channel */
1275 for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++)
1276 {
1277 bool continue_loop = MFALSE;
1278 region_chan = &pmadapter->region_channel[j];
1279
1280 PRINTM(MINFO, "11D: [%d] region_chan->Band[%d]\n", j, region_chan->band);
1281
1282 if (region_chan == MNULL || !region_chan->valid || region_chan->pcfp == MNULL)
1283 {
1284 continue;
1285 }
1286 switch (region_chan->band)
1287 {
1288 #if CONFIG_5GHz_SUPPORT
1289 case BAND_A:
1290 switch (band)
1291 {
1292 case BAND_A:
1293 case BAND_AN:
1294 case BAND_A | BAND_AN:
1295 #if CONFIG_11AC
1296 case BAND_A | BAND_AN | BAND_AAC:
1297 #endif
1298 #if CONFIG_11AX
1299 case BAND_A | BAND_AN | BAND_AAC | BAND_AAX:
1300 #endif
1301 break;
1302 default:
1303 continue_loop = MTRUE;
1304 break;
1305 }
1306 break;
1307 #endif /* CONFIG_5GHz_SUPPORT */
1308 case BAND_B:
1309 case BAND_G:
1310 switch (band)
1311 {
1312 case BAND_B:
1313 case BAND_G:
1314 case BAND_G | BAND_B:
1315 case BAND_GN:
1316 case BAND_G | BAND_GN:
1317 case BAND_B | BAND_G | BAND_GN:
1318 #if CONFIG_11AC
1319 case BAND_B | BAND_G | BAND_GN | BAND_GAC:
1320 #endif
1321 #if CONFIG_11AX
1322 case BAND_B | BAND_G | BAND_GN | BAND_GAC | BAND_GAX:
1323 #endif
1324 break;
1325 default:
1326 continue_loop = MTRUE;
1327 break;
1328 }
1329 break;
1330 default:
1331 continue_loop = MTRUE;
1332 break;
1333 }
1334
1335 if (continue_loop == MTRUE)
1336 {
1337 continue;
1338 }
1339 else
1340 {
1341 break;
1342 }
1343 }
1344
1345 /* Check if region channel found */
1346 if (j >= MAX_REGION_CHANNEL_NUM)
1347 {
1348 PRINTM(MERROR, "11D: region_chan not found. Band[%d]\n", band);
1349 LEAVE();
1350 return MLAN_STATUS_FAILURE;
1351 }
1352
1353 /* Generate parsed region channel info from region channel */
1354 (void)__memset(pmadapter, &parsed_region_chan, 0, sizeof(parsed_region_chan_11d_t));
1355 wlan_11d_generate_parsed_region_chan(pmadapter, region_chan, &parsed_region_chan);
1356
1357 /* Generate domain info from parsed region channel info */
1358 (void)wlan_11d_generate_domain_info(pmadapter, &parsed_region_chan);
1359
1360 /* Set domain info */
1361 ret = wlan_11d_send_domain_info(pmpriv, MNULL, MFALSE);
1362 if (ret != MLAN_STATUS_SUCCESS)
1363 {
1364 PRINTM(MERROR, "11D: Error sending domain info to FW\n");
1365 }
1366 }
1367
1368 LEAVE();
1369 return ret;
1370 }
1371
1372 void wlan_filter_domain_channel(mlan_private *pmpriv,
1373 parsed_region_chan_11d_t *origin_region_chan,
1374 parsed_region_chan_11d_t *filtered_region_chan)
1375 {
1376 t_u32 i;
1377
1378 for (i = 0; (i < origin_region_chan->no_of_chan) && (i < MAX_NO_OF_CHAN); i++)
1379 {
1380 if(MTRUE == wlan_check_channel_by_region_table(pmpriv, origin_region_chan->chan_pwr[i].chan))
1381 {
1382 (void)__memcpy(pmpriv->adapter, &filtered_region_chan->chan_pwr[filtered_region_chan->no_of_chan],
1383 &origin_region_chan->chan_pwr[i], sizeof(chan_power_11d_t));
1384 filtered_region_chan->no_of_chan++;
1385 }
1386 }
1387 }
1388 /**
1389 * @brief This function parses country info from AP and
1390 * download country info to FW
1391 *
1392 * @param pmpriv A pointer to mlan_private structure
1393 * @param pbss_desc A pointer to BSS descriptor
1394 *
1395 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1396 */
1397 mlan_status wlan_11d_parse_dnld_countryinfo(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc)
1398 {
1399 mlan_status ret = MLAN_STATUS_SUCCESS;
1400 mlan_adapter *pmadapter = pmpriv->adapter;
1401 parsed_region_chan_11d_t region_chan;
1402 parsed_region_chan_11d_t bssdesc_region_chan;
1403 parsed_region_chan_11d_t filtered_region_chan;
1404 t_u32 i, j;
1405
1406 ENTER();
1407
1408 /* Only valid if 11D is enabled */
1409 if (wlan_11d_is_enabled(pmpriv))
1410 {
1411 (void)__memset(pmadapter, ®ion_chan, 0, sizeof(parsed_region_chan_11d_t));
1412 (void)__memset(pmadapter, &bssdesc_region_chan, 0, sizeof(parsed_region_chan_11d_t));
1413 (void)__memset(pmadapter, &filtered_region_chan, 0, sizeof(parsed_region_chan_11d_t));
1414
1415 (void)__memcpy(pmadapter, ®ion_chan, &pmadapter->parsed_region_chan, sizeof(parsed_region_chan_11d_t));
1416
1417 if (pbss_desc != MNULL)
1418 {
1419 /** Country code */
1420 t_u8 country_code[COUNTRY_CODE_LEN];
1421 country_code[0] = pbss_desc->country_info.country_code[0];
1422 country_code[1] = pbss_desc->country_info.country_code[1];
1423 country_code[2] = ' ';
1424
1425 if (is_special_region_code(country_code))
1426 {
1427 PRINTM(MINFO, "Skip special region code in CountryIE");
1428 LEAVE();
1429 return MLAN_STATUS_SUCCESS;
1430 }
1431 /* Parse domain info if available */
1432 ret = wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info, pbss_desc->bss_band,
1433 &bssdesc_region_chan);
1434 if (ret == MLAN_STATUS_SUCCESS)
1435 {
1436 /* Update the channel-power table */
1437 for (i = 0; ((i < bssdesc_region_chan.no_of_chan) && (i < MAX_NO_OF_CHAN)); i++)
1438 {
1439 for (j = 0; ((j < region_chan.no_of_chan) && (j < MAX_NO_OF_CHAN)); j++)
1440 {
1441 /*
1442 * Channel already exists, use minimum of existing
1443 * tx power and tx_power received from
1444 * country info of the current AP
1445 */
1446 if (region_chan.chan_pwr[i].chan == bssdesc_region_chan.chan_pwr[j].chan &&
1447 region_chan.chan_pwr[i].band == bssdesc_region_chan.chan_pwr[j].band)
1448 {
1449 region_chan.chan_pwr[j].pwr =
1450 MIN(region_chan.chan_pwr[j].pwr, bssdesc_region_chan.chan_pwr[i].pwr);
1451 break;
1452 }
1453 }
1454 }
1455 }
1456 }
1457
1458 /* Filter out channel list of current region code, then generate domain info */
1459 (void)wlan_filter_domain_channel(pmpriv, ®ion_chan, &filtered_region_chan);
1460
1461 /* Generate domain info */
1462 (void)wlan_11d_generate_domain_info(pmadapter, &filtered_region_chan);
1463
1464 /* Set domain info */
1465 ret = wlan_11d_send_domain_info(pmpriv, MNULL, MFALSE);
1466 if (ret != MLAN_STATUS_SUCCESS)
1467 {
1468 PRINTM(MERROR, "11D: Error sending domain info to FW\n");
1469 }
1470 }
1471
1472 LEAVE();
1473 return ret;
1474 }
1475
1476 /**
1477 * @brief This function prepares domain info from scan table and
1478 * downloads the domain info command to the FW.
1479 *
1480 * @param pmpriv A pointer to mlan_private structure
1481 *
1482 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1483 */
1484 mlan_status wlan_11d_prepare_dnld_domain_info_cmd(mlan_private *pmpriv)
1485 {
1486 mlan_status ret = MLAN_STATUS_SUCCESS;
1487 mlan_adapter *pmadapter = pmpriv->adapter;
1488 IEEEtypes_CountryInfoFullSet_t *pcountry_full = MNULL;
1489 t_u32 idx;
1490
1491 ENTER();
1492
1493 /* Only valid if 11D is enabled */
1494 if (wlan_11d_is_enabled(pmpriv) && pmadapter->num_in_scan_table != 0U)
1495 {
1496 for (idx = 0; idx < pmadapter->num_in_scan_table; idx++)
1497 {
1498 pcountry_full = &pmadapter->pscan_table[idx].country_info;
1499
1500 ret = wlan_11d_update_chan_pwr_table(pmpriv, &pmadapter->pscan_table[idx]);
1501
1502 if (*(pcountry_full->country_code) != 0U && (pcountry_full->len > COUNTRY_CODE_LEN))
1503 {
1504 /* Country info found in the BSS Descriptor */
1505 ret = wlan_11d_process_country_info(pmpriv, &pmadapter->pscan_table[idx]);
1506 }
1507 }
1508
1509 /* Sort parsed_region_chan in ascending channel number */
1510 wlan_11d_sort_parsed_region_chan(&pmadapter->parsed_region_chan);
1511
1512 #if 0
1513 /* Check if connected */
1514 if (pmpriv->media_connected == MTRUE)
1515 {
1516 ret = wlan_11d_parse_dnld_countryinfo(pmpriv, &pmpriv->curr_bss_params.bss_descriptor);
1517 }
1518 else
1519 {
1520 ret = wlan_11d_parse_dnld_countryinfo(pmpriv, MNULL);
1521 }
1522 #endif
1523 }
1524
1525 LEAVE();
1526 return ret;
1527 }
1528
1529 /**
1530 * @brief This function sets up domain_reg and downloads CMD to FW
1531 *
1532 * @param pmadapter A pointer to mlan_adapter structure
1533 * @param pioctl_req Pointer to the IOCTL request buffer
1534 *
1535 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1536 */
1537 mlan_status wlan_11d_cfg_domain_info(IN pmlan_adapter pmadapter, IN mlan_ioctl_req *pioctl_req)
1538 {
1539 mlan_status ret = MLAN_STATUS_SUCCESS;
1540 mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
1541 mlan_ds_11d_domain_info *domain_info = MNULL;
1542 mlan_ds_11d_cfg *cfg_11d = MNULL;
1543 t_u8 region_code = 0;
1544
1545 ENTER();
1546
1547 cfg_11d = (mlan_ds_11d_cfg *)(void *)pioctl_req->pbuf;
1548 domain_info = &cfg_11d->param.domain_info;
1549
1550 #ifdef OTP_CHANINFO
1551 if ((pmadapter->otp_region != MNULL) && (pmadapter->otp_region->force_reg != 0U))
1552 {
1553 (void)PRINTF("ForceRegionRule is set in the on-chip OTP memory\r\n");
1554 ret = MLAN_STATUS_FAILURE;
1555 goto done;
1556 }
1557 #endif
1558
1559 /* Update region code and table based on country code */
1560 if (wlan_11d_region_2_code(pmadapter, domain_info->country_code, ®ion_code) == MLAN_STATUS_SUCCESS)
1561 {
1562 pmadapter->region_code = region_code;
1563 ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands);
1564 if (ret != MLAN_STATUS_SUCCESS)
1565 {
1566 goto done;
1567 }
1568 }
1569
1570 (void)wlan_11d_set_domain_info(pmpriv, domain_info->band, domain_info->country_code, domain_info->no_of_sub_band,
1571 (IEEEtypes_SubbandSet_t *)(void *)domain_info->sub_band);
1572 ret = wlan_11d_send_domain_info(pmpriv, pioctl_req, MFALSE);
1573
1574 if (ret == MLAN_STATUS_SUCCESS)
1575 {
1576 ret = MLAN_STATUS_PENDING;
1577 }
1578
1579 done:
1580 LEAVE();
1581 return ret;
1582 }
1583 #endif /* STA_SUPPORT */
1584
1585 #if UAP_SUPPORT
1586 /**
1587 * @brief This function handles domain info data from UAP interface.
1588 * Checks conditions, sets up domain_reg, then downloads CMD.
1589 *
1590 * @param pmpriv A pointer to mlan_private structure
1591 * @param band Band interface is operating on
1592 * @param domain_tlv Pointer to domain_info tlv
1593 * @param pioctl_buf Pointer to the IOCTL buffer
1594 *
1595 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1596 */
1597 mlan_status wlan_11d_handle_uap_domain_info(mlan_private *pmpriv, t_u16 band, t_u8 *domain_tlv, t_void *pioctl_buf)
1598 {
1599 mlan_status ret = MLAN_STATUS_SUCCESS;
1600 mlan_adapter *pmadapter = pmpriv->adapter;
1601 MrvlIEtypes_DomainParamSet_t *pdomain_tlv;
1602 t_u8 num_sub_band = 0;
1603 t_u8 region_code = 0;
1604
1605 ENTER();
1606
1607 pdomain_tlv = (MrvlIEtypes_DomainParamSet_t *)(void *)domain_tlv;
1608
1609 // update region code & table based on country string
1610 if (wlan_11d_region_2_code(pmadapter, pdomain_tlv->country_code, ®ion_code) == MLAN_STATUS_SUCCESS)
1611 {
1612 pmadapter->region_code = region_code;
1613 ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands);
1614 }
1615
1616 num_sub_band = (t_u8)((pdomain_tlv->header.len - COUNTRY_CODE_LEN) / sizeof(IEEEtypes_SubbandSet_t));
1617
1618 // TODO: don't just clobber pmadapter->domain_reg.
1619 // Add some checking or merging between STA & UAP domain_info
1620
1621 (void)wlan_11d_set_domain_info(pmpriv, band, pdomain_tlv->country_code, num_sub_band, pdomain_tlv->sub_band);
1622
1623 /* wmsdk: We do not yet have mechanism in wlan_prepare_cmd() to
1624 separate uao and sta commands. Hence we have to call uap cmd send
1625 function here manually */
1626 /* ret = wlan_11d_send_domain_info(pmpriv, pioctl_buf); */
1627 int rv = wifi_uap_prepare_and_send_cmd(pmpriv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0,
1628 (t_void *)pioctl_buf, MNULL, MLAN_BSS_TYPE_UAP, NULL);
1629 if (rv != 0)
1630 {
1631 wuap_w("Unable to send uap domain info");
1632 ret = MLAN_STATUS_FAILURE;
1633 }
1634
1635 LEAVE();
1636 return ret;
1637 }
1638 #endif
1639