2 * Copyright (c) 2008 Atheros Communications Inc.
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <linux/kernel.h>
18 #include <linux/slab.h>
22 #include "regd_common.h"
24 static int ath9k_regd_chansort(const void *a
, const void *b
)
26 const struct ath9k_channel
*ca
= a
;
27 const struct ath9k_channel
*cb
= b
;
29 return (ca
->channel
== cb
->channel
) ?
30 (ca
->channelFlags
& CHAN_FLAGS
) -
31 (cb
->channelFlags
& CHAN_FLAGS
) : ca
->channel
- cb
->channel
;
35 ath9k_regd_sort(void *a
, u32 n
, u32 size
, ath_hal_cmp_t
*cmp
)
40 for (ai
= aa
+ size
; --n
>= 1; ai
+= size
)
41 for (t
= ai
; t
> aa
; t
-= size
) {
49 static u16
ath9k_regd_get_eepromRD(struct ath_hal
*ah
)
51 return ah
->ah_currentRD
& ~WORLDWIDE_ROAMING_FLAG
;
54 static bool ath9k_regd_is_chan_bm_zero(u64
*bitmask
)
58 for (i
= 0; i
< BMLEN
; i
++) {
65 static bool ath9k_regd_is_eeprom_valid(struct ath_hal
*ah
)
67 u16 rd
= ath9k_regd_get_eepromRD(ah
);
70 if (rd
& COUNTRY_ERD_FLAG
) {
71 u16 cc
= rd
& ~COUNTRY_ERD_FLAG
;
72 for (i
= 0; i
< ARRAY_SIZE(allCountries
); i
++)
73 if (allCountries
[i
].countryCode
== cc
)
76 for (i
= 0; i
< ARRAY_SIZE(regDomainPairs
); i
++)
77 if (regDomainPairs
[i
].regDmnEnum
== rd
)
80 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
81 "%s: invalid regulatory domain/country code 0x%x\n",
86 static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal
*ah
)
90 regcap
= ah
->ah_caps
.reg_cap
;
92 if (regcap
& AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND
)
98 static bool ath9k_regd_is_ccode_valid(struct ath_hal
*ah
,
104 if (cc
== CTRY_DEFAULT
)
106 if (cc
== CTRY_DEBUG
)
109 rd
= ath9k_regd_get_eepromRD(ah
);
110 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
, "%s: EEPROM regdomain 0x%x\n",
113 if (rd
& COUNTRY_ERD_FLAG
) {
114 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
115 "%s: EEPROM setting is country code %u\n",
116 __func__
, rd
& ~COUNTRY_ERD_FLAG
);
117 return cc
== (rd
& ~COUNTRY_ERD_FLAG
);
120 for (i
= 0; i
< ARRAY_SIZE(allCountries
); i
++) {
121 if (cc
== allCountries
[i
].countryCode
) {
122 #ifdef AH_SUPPORT_11D
123 if ((rd
& WORLD_SKU_MASK
) == WORLD_SKU_PREFIX
)
126 if (allCountries
[i
].regDmnEnum
== rd
||
127 rd
== DEBUG_REG_DMN
|| rd
== NO_ENUMRD
)
135 ath9k_regd_get_wmodes_nreg(struct ath_hal
*ah
,
136 struct country_code_to_enum_rd
*country
,
137 struct regDomain
*rd5GHz
)
141 modesAvail
= ah
->ah_caps
.wireless_modes
;
143 if ((modesAvail
& ATH9K_MODE_SEL_11G
) && (!country
->allow11g
))
144 modesAvail
&= ~ATH9K_MODE_SEL_11G
;
145 if ((modesAvail
& ATH9K_MODE_SEL_11A
) &&
146 (ath9k_regd_is_chan_bm_zero(rd5GHz
->chan11a
)))
147 modesAvail
&= ~ATH9K_MODE_SEL_11A
;
149 if ((modesAvail
& ATH9K_MODE_SEL_11NG_HT20
)
150 && (!country
->allow11ng20
))
151 modesAvail
&= ~ATH9K_MODE_SEL_11NG_HT20
;
153 if ((modesAvail
& ATH9K_MODE_SEL_11NA_HT20
)
154 && (!country
->allow11na20
))
155 modesAvail
&= ~ATH9K_MODE_SEL_11NA_HT20
;
157 if ((modesAvail
& ATH9K_MODE_SEL_11NG_HT40PLUS
) &&
158 (!country
->allow11ng40
))
159 modesAvail
&= ~ATH9K_MODE_SEL_11NG_HT40PLUS
;
161 if ((modesAvail
& ATH9K_MODE_SEL_11NG_HT40MINUS
) &&
162 (!country
->allow11ng40
))
163 modesAvail
&= ~ATH9K_MODE_SEL_11NG_HT40MINUS
;
165 if ((modesAvail
& ATH9K_MODE_SEL_11NA_HT40PLUS
) &&
166 (!country
->allow11na40
))
167 modesAvail
&= ~ATH9K_MODE_SEL_11NA_HT40PLUS
;
169 if ((modesAvail
& ATH9K_MODE_SEL_11NA_HT40MINUS
) &&
170 (!country
->allow11na40
))
171 modesAvail
&= ~ATH9K_MODE_SEL_11NA_HT40MINUS
;
176 bool ath9k_regd_is_public_safety_sku(struct ath_hal
*ah
)
180 rd
= ath9k_regd_get_eepromRD(ah
);
184 case (CTRY_UNITED_STATES_FCC49
| COUNTRY_ERD_FLAG
):
188 if (ah
->ah_countryCode
== CTRY_UNITED_STATES_FCC49
)
195 static struct country_code_to_enum_rd
*
196 ath9k_regd_find_country(u16 countryCode
)
200 for (i
= 0; i
< ARRAY_SIZE(allCountries
); i
++) {
201 if (allCountries
[i
].countryCode
== countryCode
)
202 return &allCountries
[i
];
207 static u16
ath9k_regd_get_default_country(struct ath_hal
*ah
)
212 rd
= ath9k_regd_get_eepromRD(ah
);
213 if (rd
& COUNTRY_ERD_FLAG
) {
214 struct country_code_to_enum_rd
*country
= NULL
;
215 u16 cc
= rd
& ~COUNTRY_ERD_FLAG
;
217 country
= ath9k_regd_find_country(cc
);
222 for (i
= 0; i
< ARRAY_SIZE(regDomainPairs
); i
++)
223 if (regDomainPairs
[i
].regDmnEnum
== rd
) {
224 if (regDomainPairs
[i
].singleCC
!= 0)
225 return regDomainPairs
[i
].singleCC
;
227 i
= ARRAY_SIZE(regDomainPairs
);
232 static bool ath9k_regd_is_valid_reg_domain(int regDmn
,
233 struct regDomain
*rd
)
237 for (i
= 0; i
< ARRAY_SIZE(regDomains
); i
++) {
238 if (regDomains
[i
].regDmnEnum
== regDmn
) {
240 memcpy(rd
, ®Domains
[i
],
241 sizeof(struct regDomain
));
249 static bool ath9k_regd_is_valid_reg_domainPair(int regDmnPair
)
253 if (regDmnPair
== NO_ENUMRD
)
255 for (i
= 0; i
< ARRAY_SIZE(regDomainPairs
); i
++) {
256 if (regDomainPairs
[i
].regDmnEnum
== regDmnPair
)
263 ath9k_regd_get_wmode_regdomain(struct ath_hal
*ah
, int regDmn
,
264 u16 channelFlag
, struct regDomain
*rd
)
268 struct reg_dmn_pair_mapping
*regPair
= NULL
;
272 if (regDmn
== CTRY_DEFAULT
) {
274 rdnum
= ath9k_regd_get_eepromRD(ah
);
276 if (!(rdnum
& COUNTRY_ERD_FLAG
)) {
277 if (ath9k_regd_is_valid_reg_domain(rdnum
, NULL
) ||
278 ath9k_regd_is_valid_reg_domainPair(rdnum
)) {
284 if ((regDmn
& MULTI_DOMAIN_MASK
) == 0) {
285 for (i
= 0, found
= 0;
286 (i
< ARRAY_SIZE(regDomainPairs
)) && (!found
); i
++) {
287 if (regDomainPairs
[i
].regDmnEnum
== regDmn
) {
288 regPair
= ®DomainPairs
[i
];
293 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
294 "%s: Failed to find reg domain pair %u\n",
298 if (!(channelFlag
& CHANNEL_2GHZ
)) {
299 regDmn
= regPair
->regDmn5GHz
;
300 flags
= regPair
->flags5GHz
;
302 if (channelFlag
& CHANNEL_2GHZ
) {
303 regDmn
= regPair
->regDmn2GHz
;
304 flags
= regPair
->flags2GHz
;
308 found
= ath9k_regd_is_valid_reg_domain(regDmn
, rd
);
310 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
311 "%s: Failed to find unitary reg domain %u\n",
315 rd
->pscan
&= regPair
->pscanMask
;
316 if (((regOrg
& MULTI_DOMAIN_MASK
) == 0) &&
321 rd
->flags
&= (channelFlag
& CHANNEL_2GHZ
) ?
322 REG_DOMAIN_2GHZ_MASK
: REG_DOMAIN_5GHZ_MASK
;
327 static bool ath9k_regd_is_bit_set(int bit
, u64
*bitmask
)
329 int byteOffset
, bitnum
;
332 byteOffset
= bit
/ 64;
333 bitnum
= bit
- byteOffset
* 64;
334 val
= ((u64
) 1) << bitnum
;
335 if (bitmask
[byteOffset
] & val
)
342 ath9k_regd_add_reg_classid(u8
*regclassids
, u32 maxregids
,
343 u32
*nregids
, u8 regclassid
)
350 for (i
= 0; i
< maxregids
; i
++) {
351 if (regclassids
[i
] == regclassid
)
353 if (regclassids
[i
] == 0)
360 regclassids
[i
] = regclassid
;
368 ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal
*ah
,
369 enum reg_ext_bitmap bit
)
371 return (ah
->ah_currentRDExt
& (1 << bit
)) ? true : false;
374 #ifdef ATH_NF_PER_CHAN
376 static void ath9k_regd_init_rf_buffer(struct ath9k_channel
*ichans
,
381 for (next
= 0; next
< nchans
; next
++) {
382 for (i
= 0; i
< NUM_NF_READINGS
; i
++) {
383 ichans
[next
].nfCalHist
[i
].currIndex
= 0;
384 ichans
[next
].nfCalHist
[i
].privNF
=
385 AR_PHY_CCA_MAX_GOOD_VALUE
;
386 ichans
[next
].nfCalHist
[i
].invalidNFcount
=
387 AR_PHY_CCA_FILTERWINDOW_LENGTH
;
388 for (j
= 0; j
< ATH9K_NF_CAL_HIST_MAX
; j
++) {
389 ichans
[next
].nfCalHist
[i
].nfCalBuffer
[j
] =
390 AR_PHY_CCA_MAX_GOOD_VALUE
;
397 static int ath9k_regd_is_chan_present(struct ath_hal
*ah
,
402 for (i
= 0; i
< 150; i
++) {
403 if (!ah
->ah_channels
[i
].channel
)
405 else if (ah
->ah_channels
[i
].channel
== c
)
413 ath9k_regd_add_channel(struct ath_hal
*ah
,
420 struct regDomain rd5GHz
,
421 struct RegDmnFreqBand
*fband
,
422 struct regDomain
*rd
,
423 const struct cmode
*cm
,
424 struct ath9k_channel
*ichans
,
425 bool enableExtendedChannels
)
427 struct ath9k_channel
*chan
;
429 u32 channelFlags
= 0;
432 if (!(c_lo
<= c
&& c
<= c_hi
)) {
433 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
434 "%s: c %u out of range [%u..%u]\n",
435 __func__
, c
, c_lo
, c_hi
);
438 if ((fband
->channelBW
== CHANNEL_HALF_BW
) &&
439 !(ah
->ah_caps
.hw_caps
& ATH9K_HW_CAP_CHAN_HALFRATE
)) {
440 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
441 "%s: Skipping %u half rate channel\n",
446 if ((fband
->channelBW
== CHANNEL_QUARTER_BW
) &&
447 !(ah
->ah_caps
.hw_caps
& ATH9K_HW_CAP_CHAN_QUARTERRATE
)) {
448 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
449 "%s: Skipping %u quarter rate channel\n",
454 if (((c
+ fband
->channelSep
) / 2) > (maxChan
+ HALF_MAXCHANBW
)) {
455 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
456 "%s: c %u > maxChan %u\n",
457 __func__
, c
, maxChan
);
461 if ((fband
->usePassScan
& IS_ECM_CHAN
) && !enableExtendedChannels
) {
462 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
463 "Skipping ecm channel\n");
467 if ((rd
->flags
& NO_HOSTAP
) && (ah
->ah_opmode
== ATH9K_M_HOSTAP
)) {
468 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
469 "Skipping HOSTAP channel\n");
473 if (IS_HT40_MODE(cm
->mode
) &&
474 !(ath9k_regd_get_eeprom_reg_ext_bits(ah
, REG_EXT_FCC_DFS_HT40
)) &&
476 (rd
->conformanceTestLimit
!= MKK
)) {
477 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
478 "Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n");
482 if (IS_HT40_MODE(cm
->mode
) &&
483 !(ath9k_regd_get_eeprom_reg_ext_bits(ah
,
484 REG_EXT_JAPAN_NONDFS_HT40
)) &&
485 !(fband
->useDfs
) && (rd
->conformanceTestLimit
== MKK
)) {
486 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
487 "Skipping HT40 channel (en_jap_ht40 = 0)\n");
491 if (IS_HT40_MODE(cm
->mode
) &&
492 !(ath9k_regd_get_eeprom_reg_ext_bits(ah
, REG_EXT_JAPAN_DFS_HT40
)) &&
494 (rd
->conformanceTestLimit
== MKK
)) {
495 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
496 "Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n");
500 /* Calculate channel flags */
502 channelFlags
= cm
->flags
;
504 switch (fband
->channelBW
) {
505 case CHANNEL_HALF_BW
:
506 channelFlags
|= CHANNEL_HALF
;
508 case CHANNEL_QUARTER_BW
:
509 channelFlags
|= CHANNEL_QUARTER
;
513 if (fband
->usePassScan
& rd
->pscan
)
514 channelFlags
|= CHANNEL_PASSIVE
;
516 channelFlags
&= ~CHANNEL_PASSIVE
;
517 if (fband
->useDfs
& rd
->dfsMask
)
518 privFlags
= CHANNEL_DFS
;
521 if (rd
->flags
& LIMIT_FRAME_4MS
)
522 privFlags
|= CHANNEL_4MS_LIMIT
;
523 if (privFlags
& CHANNEL_DFS
)
524 privFlags
|= CHANNEL_DISALLOW_ADHOC
;
525 if (rd
->flags
& ADHOC_PER_11D
)
526 privFlags
|= CHANNEL_PER_11D_ADHOC
;
528 if (channelFlags
& CHANNEL_PASSIVE
) {
529 if ((c
< 2412) || (c
> 2462)) {
530 if (rd5GHz
.regDmnEnum
== MKK1
||
531 rd5GHz
.regDmnEnum
== MKK2
) {
532 u32 regcap
= ah
->ah_caps
.reg_cap
;
534 (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN
|
535 AR_EEPROM_EEREGCAP_EN_KK_U2
|
536 AR_EEPROM_EEREGCAP_EN_KK_MIDBAND
)) &&
538 channelFlags
&= ~CHANNEL_PASSIVE
;
540 privFlags
|= CHANNEL_DISALLOW_ADHOC
;
543 privFlags
|= CHANNEL_DISALLOW_ADHOC
;
548 if (cm
->mode
& (ATH9K_MODE_SEL_11A
|
549 ATH9K_MODE_SEL_11NA_HT20
|
550 ATH9K_MODE_SEL_11NA_HT40PLUS
|
551 ATH9K_MODE_SEL_11NA_HT40MINUS
)) {
552 if (rd
->flags
& (ADHOC_NO_11A
| DISALLOW_ADHOC_11A
))
553 privFlags
|= CHANNEL_DISALLOW_ADHOC
;
556 /* Fill in channel details */
558 ret
= ath9k_regd_is_chan_present(ah
, c
);
560 chan
= &ah
->ah_channels
[pos
];
562 chan
->maxRegTxPower
= fband
->powerDfs
;
563 chan
->antennaMax
= fband
->antennaMax
;
564 chan
->regDmnFlags
= rd
->flags
;
565 chan
->maxTxPower
= AR5416_MAX_RATE_POWER
;
566 chan
->minTxPower
= AR5416_MAX_RATE_POWER
;
567 chan
->channelFlags
= channelFlags
;
568 chan
->privFlags
= privFlags
;
570 chan
= &ah
->ah_channels
[ret
];
571 chan
->channelFlags
|= channelFlags
;
572 chan
->privFlags
|= privFlags
;
577 if ((cm
->flags
& CHANNEL_ALL
) == CHANNEL_A
)
578 chan
->conformanceTestLimit
[0] = ctl
;
579 else if ((cm
->flags
& CHANNEL_ALL
) == CHANNEL_B
)
580 chan
->conformanceTestLimit
[1] = ctl
;
581 else if ((cm
->flags
& CHANNEL_ALL
) == CHANNEL_G
)
582 chan
->conformanceTestLimit
[2] = ctl
;
584 return (ret
== -1) ? true : false;
587 static bool ath9k_regd_japan_check(struct ath_hal
*ah
,
589 struct regDomain
*rd5GHz
)
591 bool skipband
= false;
595 for (i
= 0; i
< ARRAY_SIZE(j_bandcheck
); i
++) {
596 if (j_bandcheck
[i
].freqbandbit
== b
) {
597 regcap
= ah
->ah_caps
.reg_cap
;
598 if ((j_bandcheck
[i
].eepromflagtocheck
& regcap
) == 0) {
600 } else if ((regcap
& AR_EEPROM_EEREGCAP_EN_KK_U2
) ||
601 (regcap
& AR_EEPROM_EEREGCAP_EN_KK_MIDBAND
)) {
602 rd5GHz
->dfsMask
|= DFS_MKK4
;
603 rd5GHz
->pscan
|= PSCAN_MKK3
;
609 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
610 "%s: Skipping %d freq band\n",
611 __func__
, j_bandcheck
[i
].freqbandbit
);
617 ath9k_regd_init_channels(struct ath_hal
*ah
,
619 u32
*nchans
, u8
*regclassids
,
620 u32 maxregids
, u32
*nregids
, u16 cc
,
621 u32 modeSelect
, bool enableOutdoor
,
622 bool enableExtendedChannels
)
626 struct country_code_to_enum_rd
*country
= NULL
;
627 struct regDomain rd5GHz
, rd2GHz
;
628 const struct cmode
*cm
;
629 struct ath9k_channel
*ichans
= &ah
->ah_channels
[0];
635 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
, "%s: cc %u mode 0x%x%s%s\n",
636 __func__
, cc
, modeSelect
,
637 enableOutdoor
? " Enable outdoor" : " ",
638 enableExtendedChannels
? " Enable ecm" : "");
640 if (!ath9k_regd_is_ccode_valid(ah
, cc
)) {
641 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
642 "%s: invalid country code %d\n", __func__
, cc
);
646 if (!ath9k_regd_is_eeprom_valid(ah
)) {
647 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
648 "%s: invalid EEPROM contents\n", __func__
);
652 ah
->ah_countryCode
= ath9k_regd_get_default_country(ah
);
654 if (ah
->ah_countryCode
== CTRY_DEFAULT
) {
655 ah
->ah_countryCode
= cc
& COUNTRY_CODE_MASK
;
656 if ((ah
->ah_countryCode
== CTRY_DEFAULT
) &&
657 (ath9k_regd_get_eepromRD(ah
) == CTRY_DEFAULT
)) {
658 ah
->ah_countryCode
= CTRY_UNITED_STATES
;
662 #ifdef AH_SUPPORT_11D
663 if (ah
->ah_countryCode
== CTRY_DEFAULT
) {
664 regdmn
= ath9k_regd_get_eepromRD(ah
);
668 country
= ath9k_regd_find_country(ah
->ah_countryCode
);
669 if (country
== NULL
) {
670 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
671 "Country is NULL!!!!, cc= %d\n",
675 regdmn
= country
->regDmnEnum
;
676 #ifdef AH_SUPPORT_11D
677 if (((ath9k_regd_get_eepromRD(ah
) &
678 WORLD_SKU_MASK
) == WORLD_SKU_PREFIX
) &&
679 (cc
== CTRY_UNITED_STATES
)) {
680 if (!isWwrSKU_NoMidband(ah
)
681 && ath9k_regd_is_fcc_midband_supported(ah
))
688 #ifdef AH_SUPPORT_11D
691 if (!ath9k_regd_get_wmode_regdomain(ah
,
695 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
696 "%s: couldn't find unitary "
697 "5GHz reg domain for country %u\n",
698 __func__
, ah
->ah_countryCode
);
701 if (!ath9k_regd_get_wmode_regdomain(ah
,
705 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
706 "%s: couldn't find unitary 2GHz "
707 "reg domain for country %u\n",
708 __func__
, ah
->ah_countryCode
);
712 if (!isWwrSKU(ah
) && ((rd5GHz
.regDmnEnum
== FCC1
) ||
713 (rd5GHz
.regDmnEnum
== FCC2
))) {
714 if (ath9k_regd_is_fcc_midband_supported(ah
)) {
715 if (!ath9k_regd_get_wmode_regdomain(ah
,
719 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
720 "%s: couldn't find unitary 5GHz "
721 "reg domain for country %u\n",
722 __func__
, ah
->ah_countryCode
);
728 if (country
== NULL
) {
729 modesAvail
= ah
->ah_caps
.wireless_modes
;
731 modesAvail
= ath9k_regd_get_wmodes_nreg(ah
, country
, &rd5GHz
);
733 maxChan
= country
->outdoorChanStart
;
738 if (maxchans
> ARRAY_SIZE(ah
->ah_channels
))
739 maxchans
= ARRAY_SIZE(ah
->ah_channels
);
741 for (cm
= modes
; cm
< &modes
[ARRAY_SIZE(modes
)]; cm
++) {
743 u64
*channelBM
= NULL
;
744 struct regDomain
*rd
= NULL
;
745 struct RegDmnFreqBand
*fband
= NULL
, *freqs
;
746 int8_t low_adj
= 0, hi_adj
= 0;
748 if ((cm
->mode
& modeSelect
) == 0) {
749 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
750 "%s: skip mode 0x%x flags 0x%x\n",
751 __func__
, cm
->mode
, cm
->flags
);
754 if ((cm
->mode
& modesAvail
) == 0) {
755 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
756 "%s: !avail mode 0x%x (0x%x) flags 0x%x\n",
757 __func__
, modesAvail
, cm
->mode
,
761 if (!ath9k_get_channel_edges(ah
, cm
->flags
, &c_lo
, &c_hi
)) {
762 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
763 "%s: channels 0x%x not supported "
765 __func__
, cm
->flags
);
770 case ATH9K_MODE_SEL_11A
:
771 case ATH9K_MODE_SEL_11NA_HT20
:
772 case ATH9K_MODE_SEL_11NA_HT40PLUS
:
773 case ATH9K_MODE_SEL_11NA_HT40MINUS
:
775 channelBM
= rd
->chan11a
;
776 freqs
= ®Dmn5GhzFreq
[0];
777 ctl
= rd
->conformanceTestLimit
;
779 case ATH9K_MODE_SEL_11B
:
781 channelBM
= rd
->chan11b
;
782 freqs
= ®Dmn2GhzFreq
[0];
783 ctl
= rd
->conformanceTestLimit
| CTL_11B
;
785 case ATH9K_MODE_SEL_11G
:
786 case ATH9K_MODE_SEL_11NG_HT20
:
787 case ATH9K_MODE_SEL_11NG_HT40PLUS
:
788 case ATH9K_MODE_SEL_11NG_HT40MINUS
:
790 channelBM
= rd
->chan11g
;
791 freqs
= ®Dmn2Ghz11gFreq
[0];
792 ctl
= rd
->conformanceTestLimit
| CTL_11G
;
795 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
796 "%s: Unknown HAL mode 0x%x\n", __func__
,
801 if (ath9k_regd_is_chan_bm_zero(channelBM
))
804 if ((cm
->mode
== ATH9K_MODE_SEL_11NA_HT40PLUS
) ||
805 (cm
->mode
== ATH9K_MODE_SEL_11NG_HT40PLUS
)) {
809 if ((cm
->mode
== ATH9K_MODE_SEL_11NA_HT40MINUS
) ||
810 (cm
->mode
== ATH9K_MODE_SEL_11NG_HT40MINUS
)) {
814 /* XXX: Add a helper here instead */
815 for (b
= 0; b
< 64 * BMLEN
; b
++) {
816 if (ath9k_regd_is_bit_set(b
, channelBM
)) {
818 if (rd5GHz
.regDmnEnum
== MKK1
819 || rd5GHz
.regDmnEnum
== MKK2
) {
820 if (ath9k_regd_japan_check(ah
,
826 ath9k_regd_add_reg_classid(regclassids
,
832 if (IS_HT40_MODE(cm
->mode
) && (rd
== &rd5GHz
)) {
834 if (fband
->lowChannel
== 5280)
837 if (fband
->lowChannel
== 5170)
840 chanSep
= fband
->channelSep
;
842 for (c
= fband
->lowChannel
+ low_adj
;
843 ((c
<= (fband
->highChannel
+ hi_adj
)) &&
844 (c
>= (fband
->lowChannel
+ low_adj
)));
846 if (next
>= maxchans
) {
849 "%s: too many channels "
850 "for channel table\n",
854 if (ath9k_regd_add_channel(ah
,
861 enableExtendedChannels
))
864 if (IS_HT40_MODE(cm
->mode
) &&
865 (fband
->lowChannel
== 5280)) {
875 if (next
> ARRAY_SIZE(ah
->ah_channels
)) {
876 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
877 "%s: too many channels %u; truncating to %u\n",
879 (int) ARRAY_SIZE(ah
->ah_channels
));
880 next
= ARRAY_SIZE(ah
->ah_channels
);
882 #ifdef ATH_NF_PER_CHAN
883 ath9k_regd_init_rf_buffer(ichans
, next
);
885 ath9k_regd_sort(ichans
, next
,
886 sizeof(struct ath9k_channel
),
887 ath9k_regd_chansort
);
891 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
, "Channel list:\n");
892 for (i
= 0; i
< next
; i
++) {
893 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
894 "chan: %d flags: 0x%x\n",
895 ah
->ah_channels
[i
].channel
,
896 ah
->ah_channels
[i
].channelFlags
);
901 ah
->ah_countryCode
= ah
->ah_countryCode
;
903 ah
->ah_currentRDInUse
= regdmn
;
904 ah
->ah_currentRD5G
= rd5GHz
.regDmnEnum
;
905 ah
->ah_currentRD2G
= rd2GHz
.regDmnEnum
;
906 if (country
== NULL
) {
910 ah
->ah_iso
[0] = country
->isoName
[0];
911 ah
->ah_iso
[1] = country
->isoName
[1];
917 struct ath9k_channel
*
918 ath9k_regd_check_channel(struct ath_hal
*ah
,
919 const struct ath9k_channel
*c
)
921 struct ath9k_channel
*base
, *cc
;
923 int flags
= c
->channelFlags
& CHAN_FLAGS
;
926 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
927 "%s: channel %u/0x%x (0x%x) requested\n", __func__
,
928 c
->channel
, c
->channelFlags
, flags
);
931 if (cc
!= NULL
&& cc
->channel
== c
->channel
&&
932 (cc
->channelFlags
& CHAN_FLAGS
) == flags
) {
933 if ((cc
->privFlags
& CHANNEL_INTERFERENCE
) &&
934 (cc
->privFlags
& CHANNEL_DFS
))
940 base
= ah
->ah_channels
;
943 for (lim
= n
; lim
!= 0; lim
>>= 1) {
945 cc
= &base
[lim
>> 1];
946 d
= c
->channel
- cc
->channel
;
948 if ((cc
->channelFlags
& CHAN_FLAGS
) == flags
) {
949 if ((cc
->privFlags
& CHANNEL_INTERFERENCE
) &&
950 (cc
->privFlags
& CHANNEL_DFS
))
955 d
= flags
- (cc
->channelFlags
& CHAN_FLAGS
);
957 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
,
958 "%s: channel %u/0x%x d %d\n", __func__
,
959 cc
->channel
, cc
->channelFlags
, d
);
965 DPRINTF(ah
->ah_sc
, ATH_DBG_REGULATORY
, "%s: no match for %u/0x%x\n",
966 __func__
, c
->channel
, c
->channelFlags
);
971 ath9k_regd_get_antenna_allowed(struct ath_hal
*ah
,
972 struct ath9k_channel
*chan
)
974 struct ath9k_channel
*ichan
= NULL
;
976 ichan
= ath9k_regd_check_channel(ah
, chan
);
980 return ichan
->antennaMax
;
983 u32
ath9k_regd_get_ctl(struct ath_hal
*ah
, struct ath9k_channel
*chan
)
986 struct ath9k_channel
*ichan
;
988 if (ah
->ah_countryCode
== CTRY_DEFAULT
&& isWwrSKU(ah
)) {
990 ctl
= SD_NO_CTL
| CTL_11B
;
991 else if (IS_CHAN_G(chan
))
992 ctl
= SD_NO_CTL
| CTL_11G
;
994 ctl
= SD_NO_CTL
| CTL_11A
;
996 ichan
= ath9k_regd_check_channel(ah
, chan
);
999 if (IS_CHAN_A(ichan
))
1000 ctl
= ichan
->conformanceTestLimit
[0];
1001 else if (IS_CHAN_B(ichan
))
1002 ctl
= ichan
->conformanceTestLimit
[1];
1003 else if (IS_CHAN_G(ichan
))
1004 ctl
= ichan
->conformanceTestLimit
[2];
1006 if (IS_CHAN_G(chan
) && (ctl
& 0xf) == CTL_11B
)
1007 ctl
= (ctl
& ~0xf) | CTL_11G
;
1013 void ath9k_regd_get_current_country(struct ath_hal
*ah
,
1014 struct ath9k_country_entry
*ctry
)
1016 u16 rd
= ath9k_regd_get_eepromRD(ah
);
1018 ctry
->isMultidomain
= false;
1019 if (rd
== CTRY_DEFAULT
)
1020 ctry
->isMultidomain
= true;
1021 else if (!(rd
& COUNTRY_ERD_FLAG
))
1022 ctry
->isMultidomain
= isWwrSKU(ah
);
1024 ctry
->countryCode
= ah
->ah_countryCode
;
1025 ctry
->regDmnEnum
= ah
->ah_currentRD
;
1026 ctry
->regDmn5G
= ah
->ah_currentRD5G
;
1027 ctry
->regDmn2G
= ah
->ah_currentRD2G
;
1028 ctry
->iso
[0] = ah
->ah_iso
[0];
1029 ctry
->iso
[1] = ah
->ah_iso
[1];
1030 ctry
->iso
[2] = ah
->ah_iso
[2];