2 * This file contains functions for 802.11D.
4 #include <linux/ctype.h>
5 #include <linux/kernel.h>
6 #include <linux/wireless.h>
14 #define TX_PWR_DEFAULT 10
16 static struct region_code_mapping region_code_mapping
[] = {
17 {"US ", 0x10}, /* US FCC */
18 {"CA ", 0x10}, /* IC Canada */
19 {"SG ", 0x10}, /* Singapore */
20 {"EU ", 0x30}, /* ETSI */
21 {"AU ", 0x30}, /* Australia */
22 {"KR ", 0x30}, /* Republic Of Korea */
23 {"ES ", 0x31}, /* Spain */
24 {"FR ", 0x32}, /* France */
25 {"JP ", 0x40}, /* Japan */
28 /* Following 2 structure defines the supported channels */
29 static struct chan_freq_power channel_freq_power_UN_BG
[] = {
30 {1, 2412, TX_PWR_DEFAULT
},
31 {2, 2417, TX_PWR_DEFAULT
},
32 {3, 2422, TX_PWR_DEFAULT
},
33 {4, 2427, TX_PWR_DEFAULT
},
34 {5, 2432, TX_PWR_DEFAULT
},
35 {6, 2437, TX_PWR_DEFAULT
},
36 {7, 2442, TX_PWR_DEFAULT
},
37 {8, 2447, TX_PWR_DEFAULT
},
38 {9, 2452, TX_PWR_DEFAULT
},
39 {10, 2457, TX_PWR_DEFAULT
},
40 {11, 2462, TX_PWR_DEFAULT
},
41 {12, 2467, TX_PWR_DEFAULT
},
42 {13, 2472, TX_PWR_DEFAULT
},
43 {14, 2484, TX_PWR_DEFAULT
}
46 static u8
wlan_region_2_code(u8
* region
)
50 for (i
= 0; region
[i
] && i
< COUNTRY_CODE_LEN
; i
++)
51 region
[i
] = toupper(region
[i
]);
53 for (i
= 0; i
< ARRAY_SIZE(region_code_mapping
); i
++) {
54 if (!memcmp(region
, region_code_mapping
[i
].region
,
56 return (region_code_mapping
[i
].code
);
60 return (region_code_mapping
[0].code
);
63 static u8
*wlan_code_2_region(u8 code
)
67 for (i
= 0; i
< ARRAY_SIZE(region_code_mapping
); i
++) {
68 if (region_code_mapping
[i
].code
== code
)
69 return (region_code_mapping
[i
].region
);
72 return (region_code_mapping
[0].region
);
76 * @brief This function finds the nrchan-th chan after the firstchan
78 * @param firstchan first channel number
79 * @param nrchan number of channels
80 * @return the nrchan-th chan number
82 static u8
wlan_get_chan_11d(u8 band
, u8 firstchan
, u8 nrchan
, u8
* chan
)
83 /*find the nrchan-th chan after the firstchan*/
86 struct chan_freq_power
*cfp
;
89 cfp
= channel_freq_power_UN_BG
;
90 cfp_no
= ARRAY_SIZE(channel_freq_power_UN_BG
);
92 for (i
= 0; i
< cfp_no
; i
++) {
93 if ((cfp
+ i
)->channel
== firstchan
) {
94 lbs_deb_11d("firstchan found\n");
100 /*if beyond the boundary */
101 if (i
+ nrchan
< cfp_no
) {
102 *chan
= (cfp
+ i
+ nrchan
)->channel
;
111 * @brief This function Checks if chan txpwr is learned from AP/IBSS
112 * @param chan chan number
113 * @param parsed_region_chan pointer to parsed_region_chan_11d
114 * @return TRUE; FALSE
116 static u8
wlan_channel_known_11d(u8 chan
,
117 struct parsed_region_chan_11d
* parsed_region_chan
)
119 struct chan_power_11d
*chanpwr
= parsed_region_chan
->chanpwr
;
120 u8 nr_chan
= parsed_region_chan
->nr_chan
;
123 lbs_deb_hex(LBS_DEB_11D
, "parsed_region_chan", (char *)chanpwr
,
124 sizeof(struct chan_power_11d
) * nr_chan
);
126 for (i
= 0; i
< nr_chan
; i
++) {
127 if (chan
== chanpwr
[i
].chan
) {
128 lbs_deb_11d("found chan %d\n", chan
);
133 lbs_deb_11d("chan %d not found\n", chan
);
137 u32
libertas_chan_2_freq(u8 chan
, u8 band
)
139 struct chan_freq_power
*cf
;
143 cf
= channel_freq_power_UN_BG
;
145 for (i
= 0; i
< ARRAY_SIZE(channel_freq_power_UN_BG
); i
++) {
146 if (chan
== cf
[i
].channel
)
153 static int generate_domain_info_11d(struct parsed_region_chan_11d
155 struct wlan_802_11d_domain_reg
* domaininfo
)
159 u8 nr_chan
= parsed_region_chan
->nr_chan
;
160 u8 nr_parsedchan
= 0;
162 u8 firstchan
= 0, nextchan
= 0, maxpwr
= 0;
166 memcpy(domaininfo
->countrycode
, parsed_region_chan
->countrycode
,
169 lbs_deb_11d("nrchan %d\n", nr_chan
);
170 lbs_deb_hex(LBS_DEB_11D
, "parsed_region_chan", (char *)parsed_region_chan
,
171 sizeof(struct parsed_region_chan_11d
));
173 for (i
= 0; i
< nr_chan
; i
++) {
176 nextchan
= firstchan
=
177 parsed_region_chan
->chanpwr
[i
].chan
;
178 maxpwr
= parsed_region_chan
->chanpwr
[i
].pwr
;
183 if (parsed_region_chan
->chanpwr
[i
].chan
== nextchan
+ 1 &&
184 parsed_region_chan
->chanpwr
[i
].pwr
== maxpwr
) {
188 domaininfo
->subband
[nr_subband
].firstchan
= firstchan
;
189 domaininfo
->subband
[nr_subband
].nrchan
=
191 domaininfo
->subband
[nr_subband
].maxtxpwr
= maxpwr
;
193 nextchan
= firstchan
=
194 parsed_region_chan
->chanpwr
[i
].chan
;
195 maxpwr
= parsed_region_chan
->chanpwr
[i
].pwr
;
200 domaininfo
->subband
[nr_subband
].firstchan
= firstchan
;
201 domaininfo
->subband
[nr_subband
].nrchan
= nr_parsedchan
;
202 domaininfo
->subband
[nr_subband
].maxtxpwr
= maxpwr
;
205 domaininfo
->nr_subband
= nr_subband
;
207 lbs_deb_11d("nr_subband=%x\n", domaininfo
->nr_subband
);
208 lbs_deb_hex(LBS_DEB_11D
, "domaininfo", (char *)domaininfo
,
209 COUNTRY_CODE_LEN
+ 1 +
210 sizeof(struct ieeetypes_subbandset
) * nr_subband
);
215 * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS
216 * @param region_chan pointer to struct region_channel
217 * @param *parsed_region_chan pointer to parsed_region_chan_11d
220 static void wlan_generate_parsed_region_chan_11d(struct region_channel
* region_chan
,
221 struct parsed_region_chan_11d
*
225 struct chan_freq_power
*cfp
;
227 if (region_chan
== NULL
) {
228 lbs_deb_11d("region_chan is NULL\n");
232 cfp
= region_chan
->CFP
;
234 lbs_deb_11d("cfp is NULL \n");
238 parsed_region_chan
->band
= region_chan
->band
;
239 parsed_region_chan
->region
= region_chan
->region
;
240 memcpy(parsed_region_chan
->countrycode
,
241 wlan_code_2_region(region_chan
->region
), COUNTRY_CODE_LEN
);
243 lbs_deb_11d("region 0x%x, band %d\n", parsed_region_chan
->region
,
244 parsed_region_chan
->band
);
246 for (i
= 0; i
< region_chan
->nrcfp
; i
++, cfp
++) {
247 parsed_region_chan
->chanpwr
[i
].chan
= cfp
->channel
;
248 parsed_region_chan
->chanpwr
[i
].pwr
= cfp
->maxtxpower
;
249 lbs_deb_11d("chan %d, pwr %d\n",
250 parsed_region_chan
->chanpwr
[i
].chan
,
251 parsed_region_chan
->chanpwr
[i
].pwr
);
253 parsed_region_chan
->nr_chan
= region_chan
->nrcfp
;
255 lbs_deb_11d("nrchan %d\n", parsed_region_chan
->nr_chan
);
261 * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS
262 * @param region region ID
267 static u8
wlan_region_chan_supported_11d(u8 region
, u8 band
, u8 chan
)
269 struct chan_freq_power
*cfp
;
274 lbs_deb_enter(LBS_DEB_11D
);
276 cfp
= libertas_get_region_cfp_table(region
, band
, &cfp_no
);
280 for (idx
= 0; idx
< cfp_no
; idx
++) {
281 if (chan
== (cfp
+ idx
)->channel
) {
282 /* If Mrvl Chip Supported? */
283 if ((cfp
+ idx
)->unsupported
) {
292 /*chan is not in the region table */
295 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);
300 * @brief This function checks if chan txpwr is learned from AP/IBSS
301 * @param chan chan number
302 * @param parsed_region_chan pointer to parsed_region_chan_11d
305 static int parse_domain_info_11d(struct ieeetypes_countryinfofullset
*
308 struct parsed_region_chan_11d
*
311 u8 nr_subband
, nrchan
;
312 u8 lastchan
, firstchan
;
316 u8 idx
= 0; /*chan index in parsed_region_chan */
320 lbs_deb_enter(LBS_DEB_11D
);
324 2. First Chan increment
325 3. channel range no overlap
327 5. channel is supported by region?
331 lbs_deb_hex(LBS_DEB_11D
, "countryinfo", (u8
*) countryinfo
, 30);
333 if ((*(countryinfo
->countrycode
)) == 0
334 || (countryinfo
->len
<= COUNTRY_CODE_LEN
)) {
335 /* No region Info or Wrong region info: treat as No 11D info */
339 /*Step1: check region_code */
340 parsed_region_chan
->region
= region
=
341 wlan_region_2_code(countryinfo
->countrycode
);
343 lbs_deb_11d("regioncode=%x\n", (u8
) parsed_region_chan
->region
);
344 lbs_deb_hex(LBS_DEB_11D
, "countrycode", (char *)countryinfo
->countrycode
,
347 parsed_region_chan
->band
= band
;
349 memcpy(parsed_region_chan
->countrycode
, countryinfo
->countrycode
,
352 nr_subband
= (countryinfo
->len
- COUNTRY_CODE_LEN
) /
353 sizeof(struct ieeetypes_subbandset
);
355 for (j
= 0, lastchan
= 0; j
< nr_subband
; j
++) {
357 if (countryinfo
->subband
[j
].firstchan
<= lastchan
) {
358 /*Step2&3. Check First Chan Num increment and no overlap */
359 lbs_deb_11d("chan %d>%d, overlap\n",
360 countryinfo
->subband
[j
].firstchan
, lastchan
);
364 firstchan
= countryinfo
->subband
[j
].firstchan
;
365 nrchan
= countryinfo
->subband
[j
].nrchan
;
367 for (i
= 0; idx
< MAX_NO_OF_CHAN
&& i
< nrchan
; i
++) {
368 /*step4: channel is supported? */
370 if (!wlan_get_chan_11d(band
, firstchan
, i
, &curchan
)) {
371 /* Chan is not found in UN table */
372 lbs_deb_11d("chan is not supported: %d \n", i
);
378 if (wlan_region_chan_supported_11d
379 (region
, band
, curchan
)) {
380 /*step5: Check if curchan is supported by mrvl in region */
381 parsed_region_chan
->chanpwr
[idx
].chan
= curchan
;
382 parsed_region_chan
->chanpwr
[idx
].pwr
=
383 countryinfo
->subband
[j
].maxtxpwr
;
386 /*not supported and ignore the chan */
388 "i %d, chan %d unsupported in region %x, band %d\n",
389 i
, curchan
, region
, band
);
393 /*Step6: Add other checking if any */
397 parsed_region_chan
->nr_chan
= idx
;
399 lbs_deb_11d("nrchan=%x\n", parsed_region_chan
->nr_chan
);
400 lbs_deb_hex(LBS_DEB_11D
, "parsed_region_chan", (u8
*) parsed_region_chan
,
401 2 + COUNTRY_CODE_LEN
+ sizeof(struct parsed_region_chan_11d
) * idx
);
404 lbs_deb_enter(LBS_DEB_11D
);
409 * @brief This function calculates the scan type for channels
410 * @param chan chan number
411 * @param parsed_region_chan pointer to parsed_region_chan_11d
412 * @return PASSIVE if chan is unknown; ACTIVE if chan is known
414 u8
libertas_get_scan_type_11d(u8 chan
,
415 struct parsed_region_chan_11d
* parsed_region_chan
)
417 u8 scan_type
= CMD_SCAN_TYPE_PASSIVE
;
419 lbs_deb_enter(LBS_DEB_11D
);
421 if (wlan_channel_known_11d(chan
, parsed_region_chan
)) {
422 lbs_deb_11d("found, do active scan\n");
423 scan_type
= CMD_SCAN_TYPE_ACTIVE
;
425 lbs_deb_11d("not found, do passive scan\n");
428 lbs_deb_leave_args(LBS_DEB_11D
, "ret scan_type %d", scan_type
);
433 void libertas_init_11d(wlan_private
* priv
)
435 priv
->adapter
->enable11d
= 0;
436 memset(&(priv
->adapter
->parsed_region_chan
), 0,
437 sizeof(struct parsed_region_chan_11d
));
442 * @brief This function sets DOMAIN INFO to FW
443 * @param priv pointer to wlan_private
446 static int set_domain_info_11d(wlan_private
* priv
)
450 if (!priv
->adapter
->enable11d
) {
451 lbs_deb_11d("dnld domain Info with 11d disabled\n");
455 ret
= libertas_prepare_and_send_command(priv
, CMD_802_11D_DOMAIN_INFO
,
457 CMD_OPTION_WAITFORRSP
, 0, NULL
);
459 lbs_deb_11d("fail to dnld domain info\n");
465 * @brief This function setups scan channels
466 * @param priv pointer to wlan_private
470 int libertas_set_universaltable(wlan_private
* priv
, u8 band
)
472 wlan_adapter
*adapter
= priv
->adapter
;
473 u16 size
= sizeof(struct chan_freq_power
);
476 memset(adapter
->universal_channel
, 0,
477 sizeof(adapter
->universal_channel
));
479 adapter
->universal_channel
[i
].nrcfp
=
480 sizeof(channel_freq_power_UN_BG
) / size
;
481 lbs_deb_11d("BG-band nrcfp %d\n",
482 adapter
->universal_channel
[i
].nrcfp
);
484 adapter
->universal_channel
[i
].CFP
= channel_freq_power_UN_BG
;
485 adapter
->universal_channel
[i
].valid
= 1;
486 adapter
->universal_channel
[i
].region
= UNIVERSAL_REGION_CODE
;
487 adapter
->universal_channel
[i
].band
= band
;
494 * @brief This function implements command CMD_802_11D_DOMAIN_INFO
495 * @param priv pointer to wlan_private
496 * @param cmd pointer to cmd buffer
497 * @param cmdno cmd ID
498 * @param cmdOption cmd action
501 int libertas_cmd_802_11d_domain_info(wlan_private
* priv
,
502 struct cmd_ds_command
*cmd
, u16 cmdno
,
505 struct cmd_ds_802_11d_domain_info
*pdomaininfo
=
506 &cmd
->params
.domaininfo
;
507 struct mrvlietypes_domainparamset
*domain
= &pdomaininfo
->domain
;
508 wlan_adapter
*adapter
= priv
->adapter
;
509 u8 nr_subband
= adapter
->domainreg
.nr_subband
;
511 lbs_deb_enter(LBS_DEB_11D
);
513 lbs_deb_11d("nr_subband=%x\n", nr_subband
);
515 cmd
->command
= cpu_to_le16(cmdno
);
516 pdomaininfo
->action
= cpu_to_le16(cmdoption
);
517 if (cmdoption
== CMD_ACT_GET
) {
519 cpu_to_le16(sizeof(pdomaininfo
->action
) + S_DS_GEN
);
520 lbs_deb_hex(LBS_DEB_11D
, "802_11D_DOMAIN_INFO", (u8
*) cmd
,
525 domain
->header
.type
= cpu_to_le16(TLV_TYPE_DOMAIN
);
526 memcpy(domain
->countrycode
, adapter
->domainreg
.countrycode
,
527 sizeof(domain
->countrycode
));
530 cpu_to_le16(nr_subband
* sizeof(struct ieeetypes_subbandset
) +
531 sizeof(domain
->countrycode
));
534 memcpy(domain
->subband
, adapter
->domainreg
.subband
,
535 nr_subband
* sizeof(struct ieeetypes_subbandset
));
537 cmd
->size
= cpu_to_le16(sizeof(pdomaininfo
->action
) +
538 le16_to_cpu(domain
->header
.len
) +
539 sizeof(struct mrvlietypesheader
) +
543 cpu_to_le16(sizeof(pdomaininfo
->action
) + S_DS_GEN
);
546 lbs_deb_hex(LBS_DEB_11D
, "802_11D_DOMAIN_INFO", (u8
*) cmd
, le16_to_cpu(cmd
->size
));
549 lbs_deb_enter(LBS_DEB_11D
);
554 * @brief This function parses countryinfo from AP and download country info to FW
555 * @param priv pointer to wlan_private
556 * @param resp pointer to command response buffer
559 int libertas_ret_802_11d_domain_info(wlan_private
* priv
,
560 struct cmd_ds_command
*resp
)
562 struct cmd_ds_802_11d_domain_info
*domaininfo
= &resp
->params
.domaininforesp
;
563 struct mrvlietypes_domainparamset
*domain
= &domaininfo
->domain
;
564 u16 action
= le16_to_cpu(domaininfo
->action
);
568 lbs_deb_enter(LBS_DEB_11D
);
570 lbs_deb_hex(LBS_DEB_11D
, "domain info resp", (u8
*) resp
,
571 (int)le16_to_cpu(resp
->size
));
573 nr_subband
= (le16_to_cpu(domain
->header
.len
) - COUNTRY_CODE_LEN
) /
574 sizeof(struct ieeetypes_subbandset
);
576 lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband
);
578 if (nr_subband
> MRVDRV_MAX_SUBBAND_802_11D
) {
579 lbs_deb_11d("Invalid Numrer of Subband returned!!\n");
584 case CMD_ACT_SET
: /*Proc Set action */
590 lbs_deb_11d("Invalid action:%d\n", domaininfo
->action
);
595 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);
600 * @brief This function parses countryinfo from AP and download country info to FW
601 * @param priv pointer to wlan_private
604 int libertas_parse_dnld_countryinfo_11d(wlan_private
* priv
,
605 struct bss_descriptor
* bss
)
608 wlan_adapter
*adapter
= priv
->adapter
;
610 lbs_deb_enter(LBS_DEB_11D
);
611 if (priv
->adapter
->enable11d
) {
612 memset(&adapter
->parsed_region_chan
, 0,
613 sizeof(struct parsed_region_chan_11d
));
614 ret
= parse_domain_info_11d(&bss
->countryinfo
, 0,
615 &adapter
->parsed_region_chan
);
618 lbs_deb_11d("error parsing domain_info from AP\n");
622 memset(&adapter
->domainreg
, 0,
623 sizeof(struct wlan_802_11d_domain_reg
));
624 generate_domain_info_11d(&adapter
->parsed_region_chan
,
625 &adapter
->domainreg
);
627 ret
= set_domain_info_11d(priv
);
630 lbs_deb_11d("error setting domain info\n");
637 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);
642 * @brief This function generates 11D info from user specified regioncode and download to FW
643 * @param priv pointer to wlan_private
646 int libertas_create_dnld_countryinfo_11d(wlan_private
* priv
)
649 wlan_adapter
*adapter
= priv
->adapter
;
650 struct region_channel
*region_chan
;
653 lbs_deb_enter(LBS_DEB_11D
);
654 lbs_deb_11d("curbssparams.band %d\n", adapter
->curbssparams
.band
);
656 if (priv
->adapter
->enable11d
) {
657 /* update parsed_region_chan_11; dnld domaininf to FW */
659 for (j
= 0; j
< ARRAY_SIZE(adapter
->region_channel
); j
++) {
660 region_chan
= &adapter
->region_channel
[j
];
662 lbs_deb_11d("%d region_chan->band %d\n", j
,
665 if (!region_chan
|| !region_chan
->valid
666 || !region_chan
->CFP
)
668 if (region_chan
->band
!= adapter
->curbssparams
.band
)
673 if (j
>= ARRAY_SIZE(adapter
->region_channel
)) {
674 lbs_deb_11d("region_chan not found, band %d\n",
675 adapter
->curbssparams
.band
);
680 memset(&adapter
->parsed_region_chan
, 0,
681 sizeof(struct parsed_region_chan_11d
));
682 wlan_generate_parsed_region_chan_11d(region_chan
,
686 memset(&adapter
->domainreg
, 0,
687 sizeof(struct wlan_802_11d_domain_reg
));
688 generate_domain_info_11d(&adapter
->parsed_region_chan
,
689 &adapter
->domainreg
);
691 ret
= set_domain_info_11d(priv
);
694 lbs_deb_11d("error setting domain info\n");
702 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);