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
)
49 u8 size
= sizeof(region_code_mapping
)/
50 sizeof(struct region_code_mapping
);
52 for (i
= 0; region
[i
] && i
< COUNTRY_CODE_LEN
; i
++)
53 region
[i
] = toupper(region
[i
]);
55 for (i
= 0; i
< size
; i
++) {
56 if (!memcmp(region
, region_code_mapping
[i
].region
,
58 return (region_code_mapping
[i
].code
);
62 return (region_code_mapping
[0].code
);
65 static u8
*wlan_code_2_region(u8 code
)
68 u8 size
= sizeof(region_code_mapping
)
69 / sizeof(struct region_code_mapping
);
70 for (i
= 0; i
< size
; i
++) {
71 if (region_code_mapping
[i
].code
== code
)
72 return (region_code_mapping
[i
].region
);
75 return (region_code_mapping
[0].region
);
79 * @brief This function finds the nrchan-th chan after the firstchan
81 * @param firstchan first channel number
82 * @param nrchan number of channels
83 * @return the nrchan-th chan number
85 static u8
wlan_get_chan_11d(u8 band
, u8 firstchan
, u8 nrchan
, u8
* chan
)
86 /*find the nrchan-th chan after the firstchan*/
89 struct chan_freq_power
*cfp
;
92 cfp
= channel_freq_power_UN_BG
;
93 cfp_no
= sizeof(channel_freq_power_UN_BG
) /
94 sizeof(struct chan_freq_power
);
96 for (i
= 0; i
< cfp_no
; i
++) {
97 if ((cfp
+ i
)->channel
== firstchan
) {
98 lbs_deb_11d("firstchan found\n");
104 /*if beyond the boundary */
105 if (i
+ nrchan
< cfp_no
) {
106 *chan
= (cfp
+ i
+ nrchan
)->channel
;
115 * @brief This function Checks if chan txpwr is learned from AP/IBSS
116 * @param chan chan number
117 * @param parsed_region_chan pointer to parsed_region_chan_11d
118 * @return TRUE; FALSE
120 static u8
wlan_channel_known_11d(u8 chan
,
121 struct parsed_region_chan_11d
* parsed_region_chan
)
123 struct chan_power_11d
*chanpwr
= parsed_region_chan
->chanpwr
;
124 u8 nr_chan
= parsed_region_chan
->nr_chan
;
127 lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr
,
128 sizeof(struct chan_power_11d
) * nr_chan
);
130 for (i
= 0; i
< nr_chan
; i
++) {
131 if (chan
== chanpwr
[i
].chan
) {
132 lbs_deb_11d("11D: Found Chan:%d\n", chan
);
137 lbs_deb_11d("11D: Not Find Chan:%d\n", chan
);
141 u32
libertas_chan_2_freq(u8 chan
, u8 band
)
143 struct chan_freq_power
*cf
;
148 cf
= channel_freq_power_UN_BG
;
150 sizeof(channel_freq_power_UN_BG
) /
151 sizeof(struct chan_freq_power
);
153 for (i
= 0; i
< cnt
; i
++) {
154 if (chan
== cf
[i
].channel
)
161 static int generate_domain_info_11d(struct parsed_region_chan_11d
163 struct wlan_802_11d_domain_reg
* domaininfo
)
167 u8 nr_chan
= parsed_region_chan
->nr_chan
;
168 u8 nr_parsedchan
= 0;
170 u8 firstchan
= 0, nextchan
= 0, maxpwr
= 0;
174 memcpy(domaininfo
->countrycode
, parsed_region_chan
->countrycode
,
177 lbs_deb_11d("11D:nrchan=%d\n", nr_chan
);
178 lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan
,
179 sizeof(struct parsed_region_chan_11d
));
181 for (i
= 0; i
< nr_chan
; i
++) {
184 nextchan
= firstchan
=
185 parsed_region_chan
->chanpwr
[i
].chan
;
186 maxpwr
= parsed_region_chan
->chanpwr
[i
].pwr
;
191 if (parsed_region_chan
->chanpwr
[i
].chan
== nextchan
+ 1 &&
192 parsed_region_chan
->chanpwr
[i
].pwr
== maxpwr
) {
196 domaininfo
->subband
[nr_subband
].firstchan
= firstchan
;
197 domaininfo
->subband
[nr_subband
].nrchan
=
199 domaininfo
->subband
[nr_subband
].maxtxpwr
= maxpwr
;
201 nextchan
= firstchan
=
202 parsed_region_chan
->chanpwr
[i
].chan
;
203 maxpwr
= parsed_region_chan
->chanpwr
[i
].pwr
;
208 domaininfo
->subband
[nr_subband
].firstchan
= firstchan
;
209 domaininfo
->subband
[nr_subband
].nrchan
= nr_parsedchan
;
210 domaininfo
->subband
[nr_subband
].maxtxpwr
= maxpwr
;
213 domaininfo
->nr_subband
= nr_subband
;
215 lbs_deb_11d("nr_subband=%x\n", domaininfo
->nr_subband
);
216 lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo
,
217 COUNTRY_CODE_LEN
+ 1 +
218 sizeof(struct ieeetypes_subbandset
) * nr_subband
);
223 * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS
224 * @param region_chan pointer to struct region_channel
225 * @param *parsed_region_chan pointer to parsed_region_chan_11d
228 static void wlan_generate_parsed_region_chan_11d(struct region_channel
* region_chan
,
229 struct parsed_region_chan_11d
*
233 struct chan_freq_power
*cfp
;
235 if (region_chan
== NULL
) {
236 lbs_deb_11d("11D: region_chan is NULL\n");
240 cfp
= region_chan
->CFP
;
242 lbs_deb_11d("11D: cfp equal NULL \n");
246 parsed_region_chan
->band
= region_chan
->band
;
247 parsed_region_chan
->region
= region_chan
->region
;
248 memcpy(parsed_region_chan
->countrycode
,
249 wlan_code_2_region(region_chan
->region
), COUNTRY_CODE_LEN
);
251 lbs_deb_11d("11D: region[0x%x] band[%d]\n", parsed_region_chan
->region
,
252 parsed_region_chan
->band
);
254 for (i
= 0; i
< region_chan
->nrcfp
; i
++, cfp
++) {
255 parsed_region_chan
->chanpwr
[i
].chan
= cfp
->channel
;
256 parsed_region_chan
->chanpwr
[i
].pwr
= cfp
->maxtxpower
;
257 lbs_deb_11d("11D: Chan[%d] Pwr[%d]\n",
258 parsed_region_chan
->chanpwr
[i
].chan
,
259 parsed_region_chan
->chanpwr
[i
].pwr
);
261 parsed_region_chan
->nr_chan
= region_chan
->nrcfp
;
263 lbs_deb_11d("11D: nrchan[%d]\n", parsed_region_chan
->nr_chan
);
269 * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS
270 * @param region region ID
275 static u8
wlan_region_chan_supported_11d(u8 region
, u8 band
, u8 chan
)
277 struct chan_freq_power
*cfp
;
282 lbs_deb_enter(LBS_DEB_11D
);
284 cfp
= libertas_get_region_cfp_table(region
, band
, &cfp_no
);
288 for (idx
= 0; idx
< cfp_no
; idx
++) {
289 if (chan
== (cfp
+ idx
)->channel
) {
290 /* If Mrvl Chip Supported? */
291 if ((cfp
+ idx
)->unsupported
) {
300 /*chan is not in the region table */
303 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);
308 * @brief This function checks if chan txpwr is learned from AP/IBSS
309 * @param chan chan number
310 * @param parsed_region_chan pointer to parsed_region_chan_11d
313 static int parse_domain_info_11d(struct ieeetypes_countryinfofullset
*
316 struct parsed_region_chan_11d
*
319 u8 nr_subband
, nrchan
;
320 u8 lastchan
, firstchan
;
324 u8 idx
= 0; /*chan index in parsed_region_chan */
328 lbs_deb_enter(LBS_DEB_11D
);
332 2. First Chan increment
333 3. channel range no overlap
335 5. channel is supported by region?
339 lbs_dbg_hex("CountryInfo:", (u8
*) countryinfo
, 30);
341 if ((*(countryinfo
->countrycode
)) == 0
342 || (countryinfo
->len
<= COUNTRY_CODE_LEN
)) {
343 /* No region Info or Wrong region info: treat as No 11D info */
347 /*Step1: check region_code */
348 parsed_region_chan
->region
= region
=
349 wlan_region_2_code(countryinfo
->countrycode
);
351 lbs_deb_11d("regioncode=%x\n", (u8
) parsed_region_chan
->region
);
352 lbs_dbg_hex("CountryCode:", (char *)countryinfo
->countrycode
,
355 parsed_region_chan
->band
= band
;
357 memcpy(parsed_region_chan
->countrycode
, countryinfo
->countrycode
,
360 nr_subband
= (countryinfo
->len
- COUNTRY_CODE_LEN
) /
361 sizeof(struct ieeetypes_subbandset
);
363 for (j
= 0, lastchan
= 0; j
< nr_subband
; j
++) {
365 if (countryinfo
->subband
[j
].firstchan
<= lastchan
) {
366 /*Step2&3. Check First Chan Num increment and no overlap */
367 lbs_deb_11d("11D: Chan[%d>%d] Overlap\n",
368 countryinfo
->subband
[j
].firstchan
, lastchan
);
372 firstchan
= countryinfo
->subband
[j
].firstchan
;
373 nrchan
= countryinfo
->subband
[j
].nrchan
;
375 for (i
= 0; idx
< MAX_NO_OF_CHAN
&& i
< nrchan
; i
++) {
376 /*step4: channel is supported? */
378 if (!wlan_get_chan_11d(band
, firstchan
, i
, &curchan
)) {
379 /* Chan is not found in UN table */
380 lbs_deb_11d("chan is not supported: %d \n", i
);
386 if (wlan_region_chan_supported_11d
387 (region
, band
, curchan
)) {
388 /*step5: Check if curchan is supported by mrvl in region */
389 parsed_region_chan
->chanpwr
[idx
].chan
= curchan
;
390 parsed_region_chan
->chanpwr
[idx
].pwr
=
391 countryinfo
->subband
[j
].maxtxpwr
;
394 /*not supported and ignore the chan */
396 "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n",
397 i
, curchan
, region
, band
);
401 /*Step6: Add other checking if any */
405 parsed_region_chan
->nr_chan
= idx
;
407 lbs_deb_11d("nrchan=%x\n", parsed_region_chan
->nr_chan
);
408 lbs_dbg_hex("11D:parsed_region_chan:", (u8
*) parsed_region_chan
,
409 2 + COUNTRY_CODE_LEN
+ sizeof(struct parsed_region_chan_11d
) * idx
);
412 lbs_deb_enter(LBS_DEB_11D
);
417 * @brief This function calculates the scan type for channels
418 * @param chan chan number
419 * @param parsed_region_chan pointer to parsed_region_chan_11d
420 * @return PASSIVE if chan is unknown; ACTIVE if chan is known
422 u8
libertas_get_scan_type_11d(u8 chan
,
423 struct parsed_region_chan_11d
* parsed_region_chan
)
425 u8 scan_type
= cmd_scan_type_passive
;
427 lbs_deb_enter(LBS_DEB_11D
);
429 if (wlan_channel_known_11d(chan
, parsed_region_chan
)) {
430 lbs_deb_11d("11D: Found and do Active Scan\n");
431 scan_type
= cmd_scan_type_active
;
433 lbs_deb_11d("11D: Not Find and do Passive Scan\n");
436 lbs_deb_leave_args(LBS_DEB_11D
, "ret scan_type %d", scan_type
);
441 void libertas_init_11d(wlan_private
* priv
)
443 priv
->adapter
->enable11d
= 0;
444 memset(&(priv
->adapter
->parsed_region_chan
), 0,
445 sizeof(struct parsed_region_chan_11d
));
449 static int wlan_enable_11d(wlan_private
* priv
, u8 flag
)
453 priv
->adapter
->enable11d
= flag
;
455 /* send cmd to FW to enable/disable 11D function in FW */
456 ret
= libertas_prepare_and_send_command(priv
,
459 cmd_option_waitforrsp
,
461 &priv
->adapter
->enable11d
);
463 lbs_deb_11d("11D: Fail to enable 11D \n");
469 * @brief This function sets DOMAIN INFO to FW
470 * @param priv pointer to wlan_private
473 static int set_domain_info_11d(wlan_private
* priv
)
477 if (!priv
->adapter
->enable11d
) {
478 lbs_deb_11d("11D: dnld domain Info with 11d disabled\n");
482 ret
= libertas_prepare_and_send_command(priv
, cmd_802_11d_domain_info
,
484 cmd_option_waitforrsp
, 0, NULL
);
486 lbs_deb_11d("11D: Fail to dnld domain Info\n");
492 * @brief This function setups scan channels
493 * @param priv pointer to wlan_private
497 int libertas_set_universaltable(wlan_private
* priv
, u8 band
)
499 wlan_adapter
*adapter
= priv
->adapter
;
500 u16 size
= sizeof(struct chan_freq_power
);
503 memset(adapter
->universal_channel
, 0,
504 sizeof(adapter
->universal_channel
));
506 adapter
->universal_channel
[i
].nrcfp
=
507 sizeof(channel_freq_power_UN_BG
) / size
;
508 lbs_deb_11d("11D: BG-band nrcfp=%d\n",
509 adapter
->universal_channel
[i
].nrcfp
);
511 adapter
->universal_channel
[i
].CFP
= channel_freq_power_UN_BG
;
512 adapter
->universal_channel
[i
].valid
= 1;
513 adapter
->universal_channel
[i
].region
= UNIVERSAL_REGION_CODE
;
514 adapter
->universal_channel
[i
].band
= band
;
521 * @brief This function implements command CMD_802_11D_DOMAIN_INFO
522 * @param priv pointer to wlan_private
523 * @param cmd pointer to cmd buffer
524 * @param cmdno cmd ID
525 * @param cmdOption cmd action
528 int libertas_cmd_802_11d_domain_info(wlan_private
* priv
,
529 struct cmd_ds_command
*cmd
, u16 cmdno
,
532 struct cmd_ds_802_11d_domain_info
*pdomaininfo
=
533 &cmd
->params
.domaininfo
;
534 struct mrvlietypes_domainparamset
*domain
= &pdomaininfo
->domain
;
535 wlan_adapter
*adapter
= priv
->adapter
;
536 u8 nr_subband
= adapter
->domainreg
.nr_subband
;
538 lbs_deb_enter(LBS_DEB_11D
);
540 lbs_deb_11d("nr_subband=%x\n", nr_subband
);
542 cmd
->command
= cpu_to_le16(cmdno
);
543 pdomaininfo
->action
= cpu_to_le16(cmdoption
);
544 if (cmdoption
== cmd_act_get
) {
546 cpu_to_le16(sizeof(pdomaininfo
->action
) + S_DS_GEN
);
547 lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8
*) cmd
,
552 domain
->header
.type
= cpu_to_le16(TLV_TYPE_DOMAIN
);
553 memcpy(domain
->countrycode
, adapter
->domainreg
.countrycode
,
554 sizeof(domain
->countrycode
));
557 cpu_to_le16(nr_subband
* sizeof(struct ieeetypes_subbandset
) +
558 sizeof(domain
->countrycode
));
561 memcpy(domain
->subband
, adapter
->domainreg
.subband
,
562 nr_subband
* sizeof(struct ieeetypes_subbandset
));
564 cmd
->size
= cpu_to_le16(sizeof(pdomaininfo
->action
) +
565 le16_to_cpu(domain
->header
.len
) +
566 sizeof(struct mrvlietypesheader
) +
570 cpu_to_le16(sizeof(pdomaininfo
->action
) + S_DS_GEN
);
573 lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8
*) cmd
, le16_to_cpu(cmd
->size
));
576 lbs_deb_enter(LBS_DEB_11D
);
581 * @brief This function implements private cmd: enable/disable 11D
582 * @param priv pointer to wlan_private
583 * @param wrq pointer to user data
586 int libertas_cmd_enable_11d(wlan_private
* priv
, struct iwreq
*wrq
)
591 lbs_deb_enter(LBS_DEB_11D
);
592 data
= SUBCMD_DATA(wrq
);
594 lbs_deb_11d("enable 11D: %s\n",
595 (data
== 1) ? "enable" : "Disable");
597 wlan_enable_11d(priv
, data
);
598 val
= (int *)wrq
->u
.name
;
599 *val
= priv
->adapter
->enable11d
;
601 lbs_deb_enter(LBS_DEB_11D
);
606 * @brief This function parses countryinfo from AP and download country info to FW
607 * @param priv pointer to wlan_private
608 * @param resp pointer to command response buffer
611 int libertas_ret_802_11d_domain_info(wlan_private
* priv
,
612 struct cmd_ds_command
*resp
)
614 struct cmd_ds_802_11d_domain_info
*domaininfo
= &resp
->params
.domaininforesp
;
615 struct mrvlietypes_domainparamset
*domain
= &domaininfo
->domain
;
616 u16 action
= le16_to_cpu(domaininfo
->action
);
620 lbs_deb_enter(LBS_DEB_11D
);
622 lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8
*) resp
,
623 (int)le16_to_cpu(resp
->size
));
625 nr_subband
= (le16_to_cpu(domain
->header
.len
) - COUNTRY_CODE_LEN
) /
626 sizeof(struct ieeetypes_subbandset
);
628 lbs_deb_11d("11D Domain Info Resp: nr_subband=%d\n", nr_subband
);
630 if (nr_subband
> MRVDRV_MAX_SUBBAND_802_11D
) {
631 lbs_deb_11d("Invalid Numrer of Subband returned!!\n");
636 case cmd_act_set
: /*Proc Set action */
642 lbs_deb_11d("Invalid action:%d\n", domaininfo
->action
);
647 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);
652 * @brief This function parses countryinfo from AP and download country info to FW
653 * @param priv pointer to wlan_private
656 int libertas_parse_dnld_countryinfo_11d(wlan_private
* priv
,
657 struct bss_descriptor
* bss
)
660 wlan_adapter
*adapter
= priv
->adapter
;
662 lbs_deb_enter(LBS_DEB_11D
);
663 if (priv
->adapter
->enable11d
) {
664 memset(&adapter
->parsed_region_chan
, 0,
665 sizeof(struct parsed_region_chan_11d
));
666 ret
= parse_domain_info_11d(&bss
->countryinfo
, 0,
667 &adapter
->parsed_region_chan
);
670 lbs_deb_11d("11D: Err Parse domain_info from AP..\n");
674 memset(&adapter
->domainreg
, 0,
675 sizeof(struct wlan_802_11d_domain_reg
));
676 generate_domain_info_11d(&adapter
->parsed_region_chan
,
677 &adapter
->domainreg
);
679 ret
= set_domain_info_11d(priv
);
682 lbs_deb_11d("11D: Err set domainInfo to FW\n");
689 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);
694 * @brief This function generates 11D info from user specified regioncode and download to FW
695 * @param priv pointer to wlan_private
698 int libertas_create_dnld_countryinfo_11d(wlan_private
* priv
)
701 wlan_adapter
*adapter
= priv
->adapter
;
702 struct region_channel
*region_chan
;
705 lbs_deb_enter(LBS_DEB_11D
);
706 lbs_deb_11d("11D:curbssparams.band[%d]\n", adapter
->curbssparams
.band
);
708 if (priv
->adapter
->enable11d
) {
709 /* update parsed_region_chan_11; dnld domaininf to FW */
711 for (j
= 0; j
< sizeof(adapter
->region_channel
) /
712 sizeof(adapter
->region_channel
[0]); j
++) {
713 region_chan
= &adapter
->region_channel
[j
];
715 lbs_deb_11d("11D:[%d] region_chan->band[%d]\n", j
,
718 if (!region_chan
|| !region_chan
->valid
719 || !region_chan
->CFP
)
721 if (region_chan
->band
!= adapter
->curbssparams
.band
)
726 if (j
>= sizeof(adapter
->region_channel
) /
727 sizeof(adapter
->region_channel
[0])) {
728 lbs_deb_11d("11D:region_chan not found. band[%d]\n",
729 adapter
->curbssparams
.band
);
734 memset(&adapter
->parsed_region_chan
, 0,
735 sizeof(struct parsed_region_chan_11d
));
736 wlan_generate_parsed_region_chan_11d(region_chan
,
740 memset(&adapter
->domainreg
, 0,
741 sizeof(struct wlan_802_11d_domain_reg
));
742 generate_domain_info_11d(&adapter
->parsed_region_chan
,
743 &adapter
->domainreg
);
745 ret
= set_domain_info_11d(priv
);
748 lbs_deb_11d("11D: Err set domainInfo to FW\n");
756 lbs_deb_leave_args(LBS_DEB_11D
, "ret %d", ret
);