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 .wlan_11d_handle_uap_domain_info_p = wlan_11d_handle_uap_domain_info,
809 };
810
811 static void *wlan_11d_support_apis = (wlan_11d_apis_t *)&wlan_11d_apis;
812
813 /**
814 * @brief This function enables 11D driver APIs
815 *
816 * @param pmpriv Pointer to mlan_private structure
817 *
818 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
819 */
wlan_11d_support_APIs(mlan_private * pmpriv)820 t_u16 wlan_11d_support_APIs(mlan_private *pmpriv)
821 {
822 ENTER();
823 LEAVE();
824
825 pmpriv->support_11d_APIs = wlan_11d_support_apis;
826
827 return (t_u16)MLAN_STATUS_SUCCESS;
828 }
829
830 /**
831 * @brief Initialize interface variable for 11D
832 *
833 * @param pmpriv Pointer to mlan_private structure
834 *
835 * @return N/A
836 */
wlan_11d_priv_init(mlan_private * pmpriv)837 t_void wlan_11d_priv_init(mlan_private *pmpriv)
838 {
839 wlan_802_11d_state_t *state = &pmpriv->state_11d;
840
841 ENTER();
842
843 /* Start in disabled mode */
844 state->enable_11d = DISABLE_11D;
845 if (!pmpriv->adapter->init_para.cfg_11d)
846 {
847 state->user_enable_11d = DEFAULT_11D_STATE;
848 }
849 else
850 {
851 state->user_enable_11d =
852 (pmpriv->adapter->init_para.cfg_11d == MLAN_INIT_PARA_DISABLED) ? DISABLE_11D : ENABLE_11D;
853 }
854
855 LEAVE();
856 return;
857 }
858
859 /**
860 * @brief Initialize device variable for 11D
861 *
862 * @param pmadapter Pointer to mlan_adapter structure
863 *
864 * @return N/A
865 */
wlan_11d_init(mlan_adapter * pmadapter)866 t_void wlan_11d_init(mlan_adapter *pmadapter)
867 {
868 ENTER();
869
870 #ifdef STA_SUPPORT
871 (void)__memset(pmadapter, &(pmadapter->parsed_region_chan), 0, sizeof(parsed_region_chan_11d_t));
872 (void)__memset(pmadapter, &(pmadapter->universal_channel), 0, sizeof(region_chan_t));
873 #endif
874 (void)__memset(pmadapter, &(pmadapter->domain_reg), 0, sizeof(wlan_802_11d_domain_reg_t));
875
876 LEAVE();
877 return;
878 }
879
880 /**
881 * @brief This function implements command CMD_802_11D_DOMAIN_INFO
882 *
883 * @param pmpriv A pointer to mlan_private structure
884 * @param pcmd A pointer to HostCmd_DS_COMMAND structure of
885 * command buffer
886 * @param cmd_action Command action
887 *
888 * @return MLAN_STATUS_SUCCESS
889 */
wlan_cmd_802_11d_domain_info(mlan_private * pmpriv,HostCmd_DS_COMMAND * pcmd,t_u16 cmd_action)890 mlan_status wlan_cmd_802_11d_domain_info(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd, t_u16 cmd_action)
891 {
892 mlan_adapter *pmadapter = pmpriv->adapter;
893 HostCmd_DS_802_11D_DOMAIN_INFO *pdomain_info = &pcmd->params.domain_info;
894 MrvlIEtypes_DomainParamSet_t *domain = &pdomain_info->domain;
895 t_u8 no_of_sub_band = pmadapter->domain_reg.no_of_sub_band;
896
897 ENTER();
898
899 PRINTM(MINFO, "11D: number of sub-band=0x%x\n", no_of_sub_band);
900
901 pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO);
902 pdomain_info->action = wlan_cpu_to_le16(cmd_action);
903 if (cmd_action == HostCmd_ACT_GEN_GET)
904 {
905 /* Dump domain info */
906 pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + S_DS_GEN);
907 HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *)pcmd, wlan_le16_to_cpu(pcmd->size));
908 LEAVE();
909 return MLAN_STATUS_SUCCESS;
910 }
911
912 /* Set domain info fields */
913 domain->header.type = wlan_cpu_to_le16(TLV_TYPE_DOMAIN);
914 (void)__memcpy(pmadapter, domain->country_code, pmadapter->domain_reg.country_code, sizeof(domain->country_code));
915
916 domain->header.len = (t_u16)(((no_of_sub_band * sizeof(IEEEtypes_SubbandSet_t)) + sizeof(domain->country_code)));
917
918 if (no_of_sub_band != 0U)
919 {
920 (void)__memcpy(pmadapter, domain->sub_band, pmadapter->domain_reg.sub_band,
921 MIN(MRVDRV_MAX_SUBBAND_802_11D, no_of_sub_band) * sizeof(IEEEtypes_SubbandSet_t));
922
923 pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + domain->header.len + sizeof(MrvlIEtypesHeader_t) +
924 S_DS_GEN);
925 }
926 else
927 {
928 pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + S_DS_GEN);
929 }
930 domain->header.len = wlan_cpu_to_le16(domain->header.len);
931
932 HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *)pcmd, wlan_le16_to_cpu(pcmd->size));
933
934 LEAVE();
935 return MLAN_STATUS_SUCCESS;
936 }
937
938 #ifdef STA_SUPPORT
939
940 /** Region code mapping */
941 typedef struct _region_code_t
942 {
943 /** Region */
944 t_u8 region[COUNTRY_CODE_LEN];
945 } region_code_t;
946
947 /**
948 * @brief This function check cfg80211 special region code.
949 *
950 * @param region_string Region string
951 *
952 * @return MTRUE/MFALSE
953 */
is_special_region_code(t_u8 * region_string)954 t_u8 is_special_region_code(t_u8 *region_string)
955 {
956 t_u8 i;
957 region_code_t special_region_code[] = {{"00 "}, {"99 "}, {"98 "}, {"97 "}};
958
959 for (i = 0; i < COUNTRY_CODE_LEN && region_string[i]; i++)
960 region_string[i] = toupper(region_string[i]);
961
962 for (i = 0; i < ARRAY_SIZE(special_region_code); i++)
963 {
964 if (!memcmp(region_string, special_region_code[i].region, COUNTRY_CODE_LEN))
965 {
966 PRINTM(MINFO, "special region code=%s\n", region_string);
967 return MTRUE;
968 }
969 }
970 return MFALSE;
971 }
972
973 /**
974 * @brief This function parses country information for region channel
975 *
976 * @param pmadapter Pointer to mlan_adapter structure
977 * @param country_info Country information
978 * @param band Chan band
979 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
980 *
981 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
982 */
wlan_11d_parse_domain_info(pmlan_adapter pmadapter,IEEEtypes_CountryInfoFullSet_t * country_info,t_u16 band,parsed_region_chan_11d_t * parsed_region_chan)983 mlan_status wlan_11d_parse_domain_info(pmlan_adapter pmadapter,
984 IEEEtypes_CountryInfoFullSet_t *country_info,
985 t_u16 band,
986 parsed_region_chan_11d_t *parsed_region_chan)
987 {
988 t_u8 no_of_sub_band, no_of_chan;
989 t_u8 last_chan, first_chan, cur_chan = 0;
990 t_u8 idx = 0;
991 t_u8 j, i;
992
993 ENTER();
994
995 /*
996 * Validation Rules:
997 * 1. Valid Region Code
998 * 2. First Chan increment
999 * 3. Channel range no overlap
1000 * 4. Channel is valid?
1001 * 5. Channel is supported by Region?
1002 * 6. Others
1003 */
1004
1005 HEXDUMP("country_info", (t_u8 *)country_info, 30);
1006 /* Step 1: Check region_code */
1007 if (!(*(country_info->country_code)) || (country_info->len <= COUNTRY_CODE_LEN))
1008 {
1009 /* No region info or wrong region info: treat as no 11D info */
1010 LEAVE();
1011 return MLAN_STATUS_FAILURE;
1012 }
1013
1014 no_of_sub_band = (t_u8)((country_info->len - COUNTRY_CODE_LEN) / sizeof(IEEEtypes_SubbandSet_t));
1015
1016 last_chan = 0;
1017 for (j = 0; j < no_of_sub_band; j++)
1018 {
1019 if (country_info->sub_band[j].first_chan <= last_chan)
1020 {
1021 /* Step2&3: Check First Chan Num increment and no overlap */
1022 PRINTM(MINFO, "11D: Chan[%d>%d] Overlap\n", country_info->sub_band[j].first_chan, last_chan);
1023 continue;
1024 }
1025
1026 first_chan = country_info->sub_band[j].first_chan;
1027 no_of_chan = country_info->sub_band[j].no_of_chan;
1028
1029 i = 0;
1030 while (idx < MAX_NO_OF_CHAN && i < no_of_chan)
1031 {
1032 /* Step 4 : Channel is supported? */
1033 if (wlan_11d_get_chan(pmadapter, band, first_chan, i, &cur_chan) == MFALSE)
1034 {
1035 /* Chan is not found in UN table */
1036 PRINTM(MWARN, "11D: channel is not supported: %d\n", i);
1037 break;
1038 }
1039
1040 last_chan = cur_chan;
1041
1042 /* Step 5: We don't need to check if cur_chan is supported by mrvl
1043 in region */
1044 parsed_region_chan->chan_pwr[idx].chan = cur_chan;
1045 parsed_region_chan->chan_pwr[idx].band = (t_u8)band;
1046 parsed_region_chan->chan_pwr[idx].pwr = country_info->sub_band[j].max_tx_pwr;
1047 idx++;
1048 i++;
1049 }
1050
1051 /* Step 6: Add other checking if any */
1052 }
1053
1054 parsed_region_chan->no_of_chan = idx;
1055
1056 PRINTM(MINFO, "11D: number of channel=0x%x\n", parsed_region_chan->no_of_chan);
1057 HEXDUMP("11D: parsed_region_chan", (t_u8 *)parsed_region_chan->chan_pwr, sizeof(chan_power_11d_t) * idx);
1058
1059 LEAVE();
1060 return MLAN_STATUS_SUCCESS;
1061 }
1062
1063 /**
1064 * @brief This function converts channel to frequency
1065 *
1066 * @param pmadapter A pointer to mlan_adapter structure
1067 * @param chan Channel number
1068 * @param band Band
1069 *
1070 * @return Channel frequency
1071 */
wlan_11d_chan_2_freq(pmlan_adapter pmadapter,t_u8 chan,t_u16 band)1072 t_u32 wlan_11d_chan_2_freq(pmlan_adapter pmadapter, t_u8 chan, t_u16 band)
1073 {
1074 const chan_freq_power_t *cf;
1075 t_u16 cnt;
1076 t_u16 i;
1077 t_u32 freq = 0;
1078
1079 ENTER();
1080
1081 #if CONFIG_5GHz_SUPPORT
1082 /* Get channel-frequency-power trios */
1083 if ((band & (BAND_A | BAND_AN)) != 0)
1084 {
1085 cf = channel_freq_power_UN_AJ;
1086 cnt = sizeof(channel_freq_power_UN_AJ) / sizeof(chan_freq_power_t);
1087 }
1088 else
1089 {
1090 #endif /* CONFIG_5GHz_SUPPORT */
1091 cf = (const chan_freq_power_t *)channel_freq_power_UN_BG;
1092 cnt = sizeof(channel_freq_power_UN_BG) / sizeof(chan_freq_power_t);
1093 #if CONFIG_5GHz_SUPPORT
1094 }
1095 #endif /* CONFIG_5GHz_SUPPORT */
1096
1097 /* Locate channel and return corresponding frequency */
1098 for (i = 0; i < cnt; i++)
1099 {
1100 if (chan == cf[i].channel)
1101 {
1102 freq = cf[i].freq;
1103 }
1104 }
1105
1106 LEAVE();
1107 return freq;
1108 }
1109
1110 /**
1111 * @brief This function setups scan channels
1112 *
1113 * @param pmpriv Pointer to mlan_private structure
1114 * @param band Band
1115 *
1116 * @return MLAN_STATUS_SUCCESS
1117 */
wlan_11d_set_universaltable(mlan_private * pmpriv,t_u16 band)1118 mlan_status wlan_11d_set_universaltable(mlan_private *pmpriv, t_u16 band)
1119 {
1120 mlan_adapter *pmadapter = pmpriv->adapter;
1121 t_u16 size = (t_u16)sizeof(chan_freq_power_t);
1122 t_u16 i = 0;
1123
1124 ENTER();
1125
1126 (void)__memset(pmadapter, pmadapter->universal_channel, 0, sizeof(pmadapter->universal_channel));
1127
1128 if ((band & (BAND_B | BAND_G | BAND_GN)) != 0U)
1129 /* If band B, G or N */
1130 {
1131 /* Set channel-frequency-power */
1132 pmadapter->universal_channel[i].num_cfp = (t_u8)(sizeof(channel_freq_power_UN_BG) / size);
1133 PRINTM(MINFO, "11D: BG-band num_cfp=%d\n", pmadapter->universal_channel[i].num_cfp);
1134
1135 pmadapter->universal_channel[i].pcfp = (const chan_freq_power_t *)channel_freq_power_UN_BG;
1136 pmadapter->universal_channel[i].valid = MTRUE;
1137
1138 /* Set region code */
1139 pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
1140
1141 /* Set band */
1142 if ((band & BAND_GN) != 0U)
1143 {
1144 pmadapter->universal_channel[i].band = BAND_G;
1145 }
1146 else
1147 {
1148 pmadapter->universal_channel[i].band = (band & BAND_G) ? BAND_G : BAND_B;
1149 }
1150 i++;
1151 }
1152
1153 #if CONFIG_5GHz_SUPPORT
1154 #if CONFIG_11AC
1155 if ((band & (BAND_A | BAND_AN | BAND_AAC)) != 0U)
1156 {
1157 #else
1158 if ((band & (BAND_A | BAND_AN)) != 0U)
1159 {
1160 #endif
1161 /* If band A */
1162
1163 /* Set channel-frequency-power */
1164 pmadapter->universal_channel[i].num_cfp = (t_u8)(sizeof(channel_freq_power_UN_AJ) / size);
1165 PRINTM(MINFO, "11D: AJ-band num_cfp=%d\n", pmadapter->universal_channel[i].num_cfp);
1166
1167 pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_AJ;
1168
1169 pmadapter->universal_channel[i].valid = MTRUE;
1170
1171 /* Set region code */
1172 pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
1173
1174 /* Set band */
1175 pmadapter->universal_channel[i].band = BAND_A;
1176 }
1177 #endif /* CONFIG_5GHz_SUPPORT */
1178
1179 LEAVE();
1180 return MLAN_STATUS_SUCCESS;
1181 }
1182
1183 /**
1184 * @brief This function calculates the scan type for channels
1185 *
1186 * @param pmadapter A pointer to mlan_adapter structure
1187 * @param band Band number
1188 * @param chan Chan number
1189 * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
1190 *
1191 * @return PASSIVE if chan is unknown; ACTIVE if chan is known
1192 */
1193 mlan_scan_type wlan_11d_get_scan_type(mlan_private *pmpriv,
1194 t_u16 band,
1195 t_u8 chan,
1196 parsed_region_chan_11d_t *parsed_region_chan)
1197 {
1198 mlan_adapter *pmadapter = pmpriv->adapter;
1199
1200 mlan_scan_type scan_type = MLAN_SCAN_TYPE_PASSIVE;
1201
1202 ENTER();
1203
1204 if (wlan_11d_channel_known(pmadapter, band, chan, parsed_region_chan) != 0U)
1205 {
1206 /* Channel found */
1207 PRINTM(MINFO, "11D: Channel found and doing Active Scan\n");
1208 scan_type = MLAN_SCAN_TYPE_ACTIVE;
1209 }
1210 else
1211 {
1212 PRINTM(MINFO, "11D: Channel not found and doing Passive Scan\n");
1213 }
1214
1215 LEAVE();
1216 return scan_type;
1217 }
1218
1219 /**
1220 * @brief This function clears the parsed region table, if 11D is enabled
1221 *
1222 * @param pmpriv A pointer to mlan_private structure
1223 *
1224 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1225 */
1226 mlan_status wlan_11d_clear_parsedtable(mlan_private *pmpriv)
1227 {
1228 mlan_adapter *pmadapter = pmpriv->adapter;
1229 mlan_status ret = MLAN_STATUS_SUCCESS;
1230
1231 ENTER();
1232
1233 if (wlan_11d_is_enabled(pmpriv))
1234 {
1235 (void)__memset(pmadapter, &(pmadapter->parsed_region_chan), 0, sizeof(parsed_region_chan_11d_t));
1236 }
1237 else
1238 {
1239 ret = MLAN_STATUS_FAILURE;
1240 }
1241
1242 LEAVE();
1243 return ret;
1244 }
1245
1246 /**
1247 * @brief This function generates 11D info from user specified regioncode
1248 * and download to FW
1249 *
1250 * @param pmpriv A pointer to mlan_private structure
1251 * @param band Band to create
1252 *
1253 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1254 */
1255 mlan_status wlan_11d_create_dnld_countryinfo(mlan_private *pmpriv, t_u16 band)
1256 {
1257 mlan_status ret = MLAN_STATUS_SUCCESS;
1258 mlan_adapter *pmadapter = pmpriv->adapter;
1259 region_chan_t *region_chan;
1260 parsed_region_chan_11d_t parsed_region_chan;
1261 t_u8 j;
1262
1263 ENTER();
1264
1265 /* Only valid if 11D is enabled */
1266 if (wlan_11d_is_enabled(pmpriv))
1267 {
1268 PRINTM(MINFO, "11D: Band[%d]\n", band);
1269
1270 /* Update parsed_region_chan; download domain info to FW */
1271
1272 /* Find region channel */
1273 for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++)
1274 {
1275 bool continue_loop = MFALSE;
1276 region_chan = &pmadapter->region_channel[j];
1277
1278 PRINTM(MINFO, "11D: [%d] region_chan->Band[%d]\n", j, region_chan->band);
1279
1280 if (region_chan == MNULL || !region_chan->valid || region_chan->pcfp == MNULL)
1281 {
1282 continue;
1283 }
1284 switch (region_chan->band)
1285 {
1286 #if CONFIG_5GHz_SUPPORT
1287 case BAND_A:
1288 switch (band)
1289 {
1290 case BAND_A:
1291 case BAND_AN:
1292 case BAND_A | BAND_AN:
1293 #if CONFIG_11AC
1294 case BAND_A | BAND_AN | BAND_AAC:
1295 #endif
1296 #if CONFIG_11AX
1297 case BAND_A | BAND_AN | BAND_AAC | BAND_AAX:
1298 #endif
1299 break;
1300 default:
1301 continue_loop = MTRUE;
1302 break;
1303 }
1304 break;
1305 #endif /* CONFIG_5GHz_SUPPORT */
1306 case BAND_B:
1307 case BAND_G:
1308 switch (band)
1309 {
1310 case BAND_B:
1311 case BAND_G:
1312 case BAND_G | BAND_B:
1313 case BAND_GN:
1314 case BAND_G | BAND_GN:
1315 case BAND_B | BAND_G | BAND_GN:
1316 #if CONFIG_11AC
1317 case BAND_B | BAND_G | BAND_GN | BAND_GAC:
1318 #endif
1319 #if CONFIG_11AX
1320 case BAND_B | BAND_G | BAND_GN | BAND_GAC | BAND_GAX:
1321 #endif
1322 break;
1323 default:
1324 continue_loop = MTRUE;
1325 break;
1326 }
1327 break;
1328 default:
1329 continue_loop = MTRUE;
1330 break;
1331 }
1332
1333 if (continue_loop == MTRUE)
1334 {
1335 continue;
1336 }
1337 else
1338 {
1339 break;
1340 }
1341 }
1342
1343 /* Check if region channel found */
1344 if (j >= MAX_REGION_CHANNEL_NUM)
1345 {
1346 PRINTM(MERROR, "11D: region_chan not found. Band[%d]\n", band);
1347 LEAVE();
1348 return MLAN_STATUS_FAILURE;
1349 }
1350
1351 /* Generate parsed region channel info from region channel */
1352 (void)__memset(pmadapter, &parsed_region_chan, 0, sizeof(parsed_region_chan_11d_t));
1353 wlan_11d_generate_parsed_region_chan(pmadapter, region_chan, &parsed_region_chan);
1354
1355 /* Generate domain info from parsed region channel info */
1356 (void)wlan_11d_generate_domain_info(pmadapter, &parsed_region_chan);
1357
1358 /* Set domain info */
1359 ret = wlan_11d_send_domain_info(pmpriv, MNULL, MFALSE);
1360 if (ret != MLAN_STATUS_SUCCESS)
1361 {
1362 PRINTM(MERROR, "11D: Error sending domain info to FW\n");
1363 }
1364 }
1365
1366 LEAVE();
1367 return ret;
1368 }
1369
1370 void wlan_filter_domain_channel(mlan_private *pmpriv,
1371 parsed_region_chan_11d_t *origin_region_chan,
1372 parsed_region_chan_11d_t *filtered_region_chan)
1373 {
1374 t_u32 i;
1375
1376 for (i = 0; (i < origin_region_chan->no_of_chan) && (i < MAX_NO_OF_CHAN); i++)
1377 {
1378 if(MTRUE == wlan_check_channel_by_region_table(pmpriv, origin_region_chan->chan_pwr[i].chan))
1379 {
1380 (void)__memcpy(pmpriv->adapter, &filtered_region_chan->chan_pwr[filtered_region_chan->no_of_chan],
1381 &origin_region_chan->chan_pwr[i], sizeof(chan_power_11d_t));
1382 filtered_region_chan->no_of_chan++;
1383 }
1384 }
1385 }
1386 /**
1387 * @brief This function parses country info from AP and
1388 * download country info to FW
1389 *
1390 * @param pmpriv A pointer to mlan_private structure
1391 * @param pbss_desc A pointer to BSS descriptor
1392 *
1393 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1394 */
1395 mlan_status wlan_11d_parse_dnld_countryinfo(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc)
1396 {
1397 mlan_status ret = MLAN_STATUS_SUCCESS;
1398 mlan_adapter *pmadapter = pmpriv->adapter;
1399 parsed_region_chan_11d_t region_chan;
1400 parsed_region_chan_11d_t bssdesc_region_chan;
1401 parsed_region_chan_11d_t filtered_region_chan;
1402 t_u32 i, j;
1403
1404 ENTER();
1405
1406 /* Only valid if 11D is enabled */
1407 if (wlan_11d_is_enabled(pmpriv))
1408 {
1409 (void)__memset(pmadapter, ®ion_chan, 0, sizeof(parsed_region_chan_11d_t));
1410 (void)__memset(pmadapter, &bssdesc_region_chan, 0, sizeof(parsed_region_chan_11d_t));
1411 (void)__memset(pmadapter, &filtered_region_chan, 0, sizeof(parsed_region_chan_11d_t));
1412
1413 (void)__memcpy(pmadapter, ®ion_chan, &pmadapter->parsed_region_chan, sizeof(parsed_region_chan_11d_t));
1414
1415 if (pbss_desc != MNULL)
1416 {
1417 /** Country code */
1418 t_u8 country_code[COUNTRY_CODE_LEN];
1419 country_code[0] = pbss_desc->country_info.country_code[0];
1420 country_code[1] = pbss_desc->country_info.country_code[1];
1421 country_code[2] = ' ';
1422
1423 if (is_special_region_code(country_code))
1424 {
1425 PRINTM(MINFO, "Skip special region code in CountryIE");
1426 LEAVE();
1427 return MLAN_STATUS_SUCCESS;
1428 }
1429 /* Parse domain info if available */
1430 ret = wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info, pbss_desc->bss_band,
1431 &bssdesc_region_chan);
1432 if (ret == MLAN_STATUS_SUCCESS)
1433 {
1434 /* Update the channel-power table */
1435 for (i = 0; ((i < bssdesc_region_chan.no_of_chan) && (i < MAX_NO_OF_CHAN)); i++)
1436 {
1437 for (j = 0; ((j < region_chan.no_of_chan) && (j < MAX_NO_OF_CHAN)); j++)
1438 {
1439 /*
1440 * Channel already exists, use minimum of existing
1441 * tx power and tx_power received from
1442 * country info of the current AP
1443 */
1444 if (region_chan.chan_pwr[i].chan == bssdesc_region_chan.chan_pwr[j].chan &&
1445 region_chan.chan_pwr[i].band == bssdesc_region_chan.chan_pwr[j].band)
1446 {
1447 region_chan.chan_pwr[j].pwr =
1448 MIN(region_chan.chan_pwr[j].pwr, bssdesc_region_chan.chan_pwr[i].pwr);
1449 break;
1450 }
1451 }
1452 }
1453 }
1454 }
1455
1456 /* Filter out channel list of current region code, then generate domain info */
1457 (void)wlan_filter_domain_channel(pmpriv, ®ion_chan, &filtered_region_chan);
1458
1459 /* Generate domain info */
1460 (void)wlan_11d_generate_domain_info(pmadapter, &filtered_region_chan);
1461
1462 /* Set domain info */
1463 ret = wlan_11d_send_domain_info(pmpriv, MNULL, MFALSE);
1464 if (ret != MLAN_STATUS_SUCCESS)
1465 {
1466 PRINTM(MERROR, "11D: Error sending domain info to FW\n");
1467 }
1468 }
1469
1470 LEAVE();
1471 return ret;
1472 }
1473
1474 /**
1475 * @brief This function prepares domain info from scan table and
1476 * downloads the domain info command to the FW.
1477 *
1478 * @param pmpriv A pointer to mlan_private structure
1479 *
1480 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1481 */
1482 mlan_status wlan_11d_prepare_dnld_domain_info_cmd(mlan_private *pmpriv)
1483 {
1484 mlan_status ret = MLAN_STATUS_SUCCESS;
1485 mlan_adapter *pmadapter = pmpriv->adapter;
1486 IEEEtypes_CountryInfoFullSet_t *pcountry_full = MNULL;
1487 t_u32 idx;
1488
1489 ENTER();
1490
1491 /* Only valid if 11D is enabled */
1492 if (wlan_11d_is_enabled(pmpriv) && pmadapter->num_in_scan_table != 0U)
1493 {
1494 for (idx = 0; idx < pmadapter->num_in_scan_table; idx++)
1495 {
1496 pcountry_full = &pmadapter->pscan_table[idx].country_info;
1497
1498 ret = wlan_11d_update_chan_pwr_table(pmpriv, &pmadapter->pscan_table[idx]);
1499
1500 if (*(pcountry_full->country_code) != 0U && (pcountry_full->len > COUNTRY_CODE_LEN))
1501 {
1502 /* Country info found in the BSS Descriptor */
1503 ret = wlan_11d_process_country_info(pmpriv, &pmadapter->pscan_table[idx]);
1504 }
1505 }
1506
1507 /* Sort parsed_region_chan in ascending channel number */
1508 wlan_11d_sort_parsed_region_chan(&pmadapter->parsed_region_chan);
1509
1510 #if 0
1511 /* Check if connected */
1512 if (pmpriv->media_connected == MTRUE)
1513 {
1514 ret = wlan_11d_parse_dnld_countryinfo(pmpriv, &pmpriv->curr_bss_params.bss_descriptor);
1515 }
1516 else
1517 {
1518 ret = wlan_11d_parse_dnld_countryinfo(pmpriv, MNULL);
1519 }
1520 #endif
1521 }
1522
1523 LEAVE();
1524 return ret;
1525 }
1526
1527 /**
1528 * @brief This function sets up domain_reg and downloads CMD to FW
1529 *
1530 * @param pmadapter A pointer to mlan_adapter structure
1531 * @param pioctl_req Pointer to the IOCTL request buffer
1532 *
1533 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1534 */
1535 mlan_status wlan_11d_cfg_domain_info(IN pmlan_adapter pmadapter, IN mlan_ioctl_req *pioctl_req)
1536 {
1537 mlan_status ret = MLAN_STATUS_SUCCESS;
1538 mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
1539 mlan_ds_11d_domain_info *domain_info = MNULL;
1540 mlan_ds_11d_cfg *cfg_11d = MNULL;
1541 t_u8 region_code = 0;
1542
1543 ENTER();
1544
1545 cfg_11d = (mlan_ds_11d_cfg *)(void *)pioctl_req->pbuf;
1546 domain_info = &cfg_11d->param.domain_info;
1547
1548 #ifdef OTP_CHANINFO
1549 if ((pmadapter->otp_region != MNULL) && (pmadapter->otp_region->force_reg != 0U))
1550 {
1551 (void)PRINTF("ForceRegionRule is set in the on-chip OTP memory\r\n");
1552 ret = MLAN_STATUS_FAILURE;
1553 goto done;
1554 }
1555 #endif
1556
1557 /* Update region code and table based on country code */
1558 if (wlan_11d_region_2_code(pmadapter, domain_info->country_code, ®ion_code) == MLAN_STATUS_SUCCESS)
1559 {
1560 pmadapter->region_code = region_code;
1561 ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands);
1562 if (ret != MLAN_STATUS_SUCCESS)
1563 {
1564 goto done;
1565 }
1566 }
1567
1568 (void)wlan_11d_set_domain_info(pmpriv, domain_info->band, domain_info->country_code, domain_info->no_of_sub_band,
1569 (IEEEtypes_SubbandSet_t *)(void *)domain_info->sub_band);
1570 ret = wlan_11d_send_domain_info(pmpriv, pioctl_req, MFALSE);
1571
1572 if (ret == MLAN_STATUS_SUCCESS)
1573 {
1574 ret = MLAN_STATUS_PENDING;
1575 }
1576
1577 done:
1578 LEAVE();
1579 return ret;
1580 }
1581 #endif /* STA_SUPPORT */
1582
1583 #if defined(UAP_SUPPORT)
1584 /**
1585 * @brief This function handles domain info data from UAP interface.
1586 * Checks conditions, sets up domain_reg, then downloads CMD.
1587 *
1588 * @param pmpriv A pointer to mlan_private structure
1589 * @param band Band interface is operating on
1590 * @param domain_tlv Pointer to domain_info tlv
1591 * @param pioctl_buf Pointer to the IOCTL buffer
1592 *
1593 * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
1594 */
1595 mlan_status wlan_11d_handle_uap_domain_info(mlan_private *pmpriv, t_u16 band, t_u8 *domain_tlv, t_void *pioctl_buf)
1596 {
1597 mlan_status ret = MLAN_STATUS_SUCCESS;
1598 mlan_adapter *pmadapter = pmpriv->adapter;
1599 MrvlIEtypes_DomainParamSet_t *pdomain_tlv;
1600 t_u8 num_sub_band = 0;
1601 t_u8 region_code = 0;
1602
1603 ENTER();
1604
1605 pdomain_tlv = (MrvlIEtypes_DomainParamSet_t *)(void *)domain_tlv;
1606
1607 // update region code & table based on country string
1608 if (wlan_11d_region_2_code(pmadapter, pdomain_tlv->country_code, ®ion_code) == MLAN_STATUS_SUCCESS)
1609 {
1610 pmadapter->region_code = region_code;
1611 ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands);
1612 }
1613
1614 num_sub_band = (t_u8)((pdomain_tlv->header.len - COUNTRY_CODE_LEN) / sizeof(IEEEtypes_SubbandSet_t));
1615
1616 // TODO: don't just clobber pmadapter->domain_reg.
1617 // Add some checking or merging between STA & UAP domain_info
1618
1619 (void)wlan_11d_set_domain_info(pmpriv, band, pdomain_tlv->country_code, num_sub_band, pdomain_tlv->sub_band);
1620
1621 /* wmsdk: We do not yet have mechanism in wlan_prepare_cmd() to
1622 separate uao and sta commands. Hence we have to call uap cmd send
1623 function here manually */
1624 /* ret = wlan_11d_send_domain_info(pmpriv, pioctl_buf); */
1625 int rv = wifi_uap_prepare_and_send_cmd(pmpriv, HostCmd_CMD_802_11D_DOMAIN_INFO, HostCmd_ACT_GEN_SET, 0,
1626 (t_void *)pioctl_buf, MNULL, MLAN_BSS_TYPE_UAP, NULL);
1627 if (rv != 0)
1628 {
1629 wuap_w("Unable to send uap domain info");
1630 ret = MLAN_STATUS_FAILURE;
1631 }
1632
1633 LEAVE();
1634 return ret;
1635 }
1636 #endif
1637