diff options
author | Luis R. Rodriguez <lrodriguez@atheros.com> | 2009-01-22 18:16:48 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 16:01:20 -0500 |
commit | 5f8e077c0adc0dc7cfad64cdc05276e1961a1394 (patch) | |
tree | dc918d9eacab12998d8e67f259de32dbbb409e81 /drivers/net/wireless/ath9k/regd.c | |
parent | 24ed1da1337b92e3b0a89f2c2b7cd33b9a8fcb62 (diff) |
ath9k: simplify regulatory code
Now that cfg80211 has its own regulatory infrastructure we can
condense ath9k's regulatory code considerably. We only keep data
we need to provide our own regulatory_hint(), reg_notifier() and
information necessary for calibration.
Atheros hardware supports 12 world regulatory domains, since these
are custom we apply them through the the new wiphy_apply_custom_regulatory().
Although we have 12 we can consolidate these into 5 structures based on
frequency and apply a different set of flags that differentiate them on
a case by case basis through the reg_notifier().
If CRDA is not found our own custom world regulatory domain is applied,
this is identical to cfg80211's except we enable passive scan on most
frequencies.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath9k/regd.c')
-rw-r--r-- | drivers/net/wireless/ath9k/regd.c | 1188 |
1 files changed, 328 insertions, 860 deletions
diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c index 64043e99facf..90f0c982430c 100644 --- a/drivers/net/wireless/ath9k/regd.c +++ b/drivers/net/wireless/ath9k/regd.c | |||
@@ -21,174 +21,323 @@ | |||
21 | #include "regd.h" | 21 | #include "regd.h" |
22 | #include "regd_common.h" | 22 | #include "regd_common.h" |
23 | 23 | ||
24 | static int ath9k_regd_chansort(const void *a, const void *b) | 24 | /* |
25 | { | 25 | * This is a set of common rules used by our world regulatory domains. |
26 | const struct ath9k_channel *ca = a; | 26 | * We have 12 world regulatory domains. To save space we consolidate |
27 | const struct ath9k_channel *cb = b; | 27 | * the regulatory domains in 5 structures by frequency and change |
28 | 28 | * the flags on our reg_notifier() on a case by case basis. | |
29 | return (ca->channel == cb->channel) ? | 29 | */ |
30 | (ca->channelFlags & CHAN_FLAGS) - | ||
31 | (cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel; | ||
32 | } | ||
33 | 30 | ||
34 | static void | 31 | /* Only these channels all allow active scan on all world regulatory domains */ |
35 | ath9k_regd_sort(void *a, u32 n, u32 size, ath_hal_cmp_t *cmp) | 32 | #define ATH9K_2GHZ_CH01_11 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0) |
36 | { | 33 | |
37 | u8 *aa = a; | 34 | /* We enable active scan on these a case by case basis by regulatory domain */ |
38 | u8 *ai, *t; | 35 | #define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\ |
39 | 36 | NL80211_RRF_PASSIVE_SCAN) | |
40 | for (ai = aa + size; --n >= 1; ai += size) | 37 | #define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\ |
41 | for (t = ai; t > aa; t -= size) { | 38 | NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM) |
42 | u8 *u = t - size; | 39 | |
43 | if (cmp(u, t) <= 0) | 40 | /* We allow IBSS on these on a case by case basis by regulatory domain */ |
44 | break; | 41 | #define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 40, 0, 30,\ |
45 | swap_array(u, t, size); | 42 | NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) |
46 | } | 43 | #define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 40, 0, 30,\ |
47 | } | 44 | NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) |
45 | #define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 40, 0, 30,\ | ||
46 | NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) | ||
47 | |||
48 | #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ | ||
49 | ATH9K_2GHZ_CH12_13, \ | ||
50 | ATH9K_2GHZ_CH14 | ||
51 | |||
52 | #define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \ | ||
53 | ATH9K_5GHZ_5470_5850 | ||
54 | /* This one skips what we call "mid band" */ | ||
55 | #define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \ | ||
56 | ATH9K_5GHZ_5725_5850 | ||
57 | |||
58 | /* Can be used for: | ||
59 | * 0x60, 0x61, 0x62 */ | ||
60 | static const struct ieee80211_regdomain ath9k_world_regdom_60_61_62 = { | ||
61 | .n_reg_rules = 5, | ||
62 | .alpha2 = "99", | ||
63 | .reg_rules = { | ||
64 | ATH9K_2GHZ_ALL, | ||
65 | ATH9K_5GHZ_ALL, | ||
66 | } | ||
67 | }; | ||
68 | |||
69 | /* Can be used by 0x63 and 0x65 */ | ||
70 | static const struct ieee80211_regdomain ath9k_world_regdom_63_65 = { | ||
71 | .n_reg_rules = 4, | ||
72 | .alpha2 = "99", | ||
73 | .reg_rules = { | ||
74 | ATH9K_2GHZ_CH01_11, | ||
75 | ATH9K_2GHZ_CH12_13, | ||
76 | ATH9K_5GHZ_NO_MIDBAND, | ||
77 | } | ||
78 | }; | ||
79 | |||
80 | /* Can be used by 0x64 only */ | ||
81 | static const struct ieee80211_regdomain ath9k_world_regdom_64 = { | ||
82 | .n_reg_rules = 3, | ||
83 | .alpha2 = "99", | ||
84 | .reg_rules = { | ||
85 | ATH9K_2GHZ_CH01_11, | ||
86 | ATH9K_5GHZ_NO_MIDBAND, | ||
87 | } | ||
88 | }; | ||
89 | |||
90 | /* Can be used by 0x66 and 0x69 */ | ||
91 | static const struct ieee80211_regdomain ath9k_world_regdom_66_69 = { | ||
92 | .n_reg_rules = 3, | ||
93 | .alpha2 = "99", | ||
94 | .reg_rules = { | ||
95 | ATH9K_2GHZ_CH01_11, | ||
96 | ATH9K_5GHZ_ALL, | ||
97 | } | ||
98 | }; | ||
99 | |||
100 | /* Can be used by 0x67, 0x6A and 0x68 */ | ||
101 | static const struct ieee80211_regdomain ath9k_world_regdom_67_68_6A = { | ||
102 | .n_reg_rules = 4, | ||
103 | .alpha2 = "99", | ||
104 | .reg_rules = { | ||
105 | ATH9K_2GHZ_CH01_11, | ||
106 | ATH9K_2GHZ_CH12_13, | ||
107 | ATH9K_5GHZ_ALL, | ||
108 | } | ||
109 | }; | ||
48 | 110 | ||
49 | static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah) | 111 | static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah) |
50 | { | 112 | { |
51 | return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG; | 113 | return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG; |
52 | } | 114 | } |
53 | 115 | ||
54 | static bool ath9k_regd_is_chan_bm_zero(u64 *bitmask) | 116 | u16 ath9k_regd_get_rd(struct ath_hal *ah) |
55 | { | 117 | { |
56 | int i; | 118 | return ath9k_regd_get_eepromRD(ah); |
119 | } | ||
57 | 120 | ||
58 | for (i = 0; i < BMLEN; i++) { | 121 | bool ath9k_is_world_regd(struct ath_hal *ah) |
59 | if (bitmask[i] != 0) | 122 | { |
60 | return false; | 123 | return isWwrSKU(ah); |
61 | } | ||
62 | return true; | ||
63 | } | 124 | } |
64 | 125 | ||
65 | static bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah) | 126 | const struct ieee80211_regdomain *ath9k_default_world_regdomain(void) |
66 | { | 127 | { |
67 | u16 rd = ath9k_regd_get_eepromRD(ah); | 128 | /* this is the most restrictive */ |
68 | int i; | 129 | return &ath9k_world_regdom_64; |
130 | } | ||
69 | 131 | ||
70 | if (rd & COUNTRY_ERD_FLAG) { | 132 | const struct ieee80211_regdomain *ath9k_world_regdomain(struct ath_hal *ah) |
71 | u16 cc = rd & ~COUNTRY_ERD_FLAG; | 133 | { |
72 | for (i = 0; i < ARRAY_SIZE(allCountries); i++) | 134 | switch (ah->regpair->regDmnEnum) { |
73 | if (allCountries[i].countryCode == cc) | 135 | case 0x60: |
74 | return true; | 136 | case 0x61: |
75 | } else { | 137 | case 0x62: |
76 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) | 138 | return &ath9k_world_regdom_60_61_62; |
77 | if (regDomainPairs[i].regDmnEnum == rd) | 139 | case 0x63: |
78 | return true; | 140 | case 0x65: |
141 | return &ath9k_world_regdom_63_65; | ||
142 | case 0x64: | ||
143 | return &ath9k_world_regdom_64; | ||
144 | case 0x66: | ||
145 | case 0x69: | ||
146 | return &ath9k_world_regdom_66_69; | ||
147 | case 0x67: | ||
148 | case 0x68: | ||
149 | case 0x6A: | ||
150 | return &ath9k_world_regdom_67_68_6A; | ||
151 | default: | ||
152 | WARN_ON(1); | ||
153 | return ath9k_default_world_regdomain(); | ||
79 | } | 154 | } |
80 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
81 | "invalid regulatory domain/country code 0x%x\n", rd); | ||
82 | return false; | ||
83 | } | 155 | } |
84 | 156 | ||
85 | static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah) | 157 | /* Enable adhoc on 5 GHz if allowed by 11d */ |
158 | static void ath9k_reg_apply_5ghz_adhoc_flags(struct wiphy *wiphy, | ||
159 | enum reg_set_by setby) | ||
86 | { | 160 | { |
87 | u32 regcap; | 161 | struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); |
88 | 162 | struct ath_softc *sc = hw->priv; | |
89 | regcap = ah->ah_caps.reg_cap; | 163 | struct ieee80211_supported_band *sband; |
164 | const struct ieee80211_reg_rule *reg_rule; | ||
165 | struct ieee80211_channel *ch; | ||
166 | unsigned int i; | ||
167 | u32 bandwidth = 0; | ||
168 | int r; | ||
169 | |||
170 | if (setby != REGDOM_SET_BY_COUNTRY_IE) | ||
171 | return; | ||
172 | if (!test_bit(ATH9K_MODE_11A, | ||
173 | sc->sc_ah->ah_caps.wireless_modes)) | ||
174 | return; | ||
90 | 175 | ||
91 | if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND) | 176 | sband = wiphy->bands[IEEE80211_BAND_5GHZ]; |
92 | return true; | 177 | for (i = 0; i < sband->n_channels; i++) { |
93 | else | 178 | ch = &sband->channels[i]; |
94 | return false; | 179 | r = freq_reg_info(wiphy, ch->center_freq, |
180 | &bandwidth, ®_rule); | ||
181 | if (r) | ||
182 | continue; | ||
183 | /* If 11d had a rule for this channel ensure we enable adhoc | ||
184 | * if it allows us to use it. Note that we would have disabled | ||
185 | * it by applying our static world regdomain by default during | ||
186 | * probe */ | ||
187 | if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) | ||
188 | ch->flags &= ~NL80211_RRF_NO_IBSS; | ||
189 | } | ||
95 | } | 190 | } |
96 | 191 | ||
97 | static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah, | 192 | /* Allows active scan scan on Ch 12 and 13 */ |
98 | u16 cc) | 193 | static void ath9k_reg_apply_active_scan_flags(struct wiphy *wiphy, |
194 | enum reg_set_by setby) | ||
99 | { | 195 | { |
100 | u16 rd; | 196 | struct ieee80211_supported_band *sband; |
101 | int i; | 197 | struct ieee80211_channel *ch; |
102 | 198 | const struct ieee80211_reg_rule *reg_rule; | |
103 | if (cc == CTRY_DEFAULT) | 199 | u32 bandwidth = 0; |
104 | return true; | 200 | int r; |
105 | if (cc == CTRY_DEBUG) | 201 | |
106 | return true; | 202 | /* Force passive scan on Channels 12-13 */ |
203 | sband = wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
204 | |||
205 | /* If no country IE has been received always enable active scan | ||
206 | * on these channels */ | ||
207 | if (setby != REGDOM_SET_BY_COUNTRY_IE) { | ||
208 | ch = &sband->channels[11]; /* CH 12 */ | ||
209 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) | ||
210 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; | ||
211 | ch = &sband->channels[12]; /* CH 13 */ | ||
212 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) | ||
213 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; | ||
214 | return; | ||
215 | } | ||
107 | 216 | ||
108 | rd = ath9k_regd_get_eepromRD(ah); | 217 | /* If a country IE has been recieved check its rule for this |
109 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "EEPROM regdomain 0x%x\n", rd); | 218 | * channel first before enabling active scan. The passive scan |
219 | * would have been enforced by the initial probe processing on | ||
220 | * our custom regulatory domain. */ | ||
110 | 221 | ||
111 | if (rd & COUNTRY_ERD_FLAG) { | 222 | ch = &sband->channels[11]; /* CH 12 */ |
112 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | 223 | r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, ®_rule); |
113 | "EEPROM setting is country code %u\n", | 224 | if (!r) { |
114 | rd & ~COUNTRY_ERD_FLAG); | 225 | if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) |
115 | return cc == (rd & ~COUNTRY_ERD_FLAG); | 226 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) |
227 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; | ||
116 | } | 228 | } |
117 | 229 | ||
118 | for (i = 0; i < ARRAY_SIZE(allCountries); i++) { | 230 | ch = &sband->channels[12]; /* CH 13 */ |
119 | if (cc == allCountries[i].countryCode) { | 231 | r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, ®_rule); |
120 | #ifdef AH_SUPPORT_11D | 232 | if (!r) { |
121 | if ((rd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) | 233 | if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) |
122 | return true; | 234 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) |
123 | #endif | 235 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; |
124 | if (allCountries[i].regDmnEnum == rd || | ||
125 | rd == DEBUG_REG_DMN || rd == NO_ENUMRD) | ||
126 | return true; | ||
127 | } | ||
128 | } | 236 | } |
129 | return false; | ||
130 | } | 237 | } |
131 | 238 | ||
132 | static void | 239 | /* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */ |
133 | ath9k_regd_get_wmodes_nreg(struct ath_hal *ah, | 240 | void ath9k_reg_apply_radar_flags(struct wiphy *wiphy) |
134 | struct country_code_to_enum_rd *country, | ||
135 | struct regDomain *rd5GHz, | ||
136 | unsigned long *modes_allowed) | ||
137 | { | 241 | { |
138 | bitmap_copy(modes_allowed, ah->ah_caps.wireless_modes, ATH9K_MODE_MAX); | 242 | struct ieee80211_supported_band *sband; |
243 | struct ieee80211_channel *ch; | ||
244 | unsigned int i; | ||
139 | 245 | ||
140 | if (test_bit(ATH9K_MODE_11G, ah->ah_caps.wireless_modes) && | 246 | if (!wiphy->bands[IEEE80211_BAND_5GHZ]) |
141 | (!country->allow11g)) | 247 | return; |
142 | clear_bit(ATH9K_MODE_11G, modes_allowed); | ||
143 | 248 | ||
144 | if (test_bit(ATH9K_MODE_11A, ah->ah_caps.wireless_modes) && | 249 | sband = wiphy->bands[IEEE80211_BAND_5GHZ]; |
145 | (ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a))) | ||
146 | clear_bit(ATH9K_MODE_11A, modes_allowed); | ||
147 | 250 | ||
148 | if (test_bit(ATH9K_MODE_11NG_HT20, ah->ah_caps.wireless_modes) | 251 | for (i = 0; i < sband->n_channels; i++) { |
149 | && (!country->allow11ng20)) | 252 | ch = &sband->channels[i]; |
150 | clear_bit(ATH9K_MODE_11NG_HT20, modes_allowed); | 253 | if (ch->center_freq < 5260) |
254 | continue; | ||
255 | if (ch->center_freq > 5700) | ||
256 | continue; | ||
257 | /* We always enable radar detection/DFS on this | ||
258 | * frequency range. Additionally we also apply on | ||
259 | * this frequency range: | ||
260 | * - If STA mode does not yet have DFS supports disable | ||
261 | * active scanning | ||
262 | * - If adhoc mode does not support DFS yet then | ||
263 | * disable adhoc in the frequency. | ||
264 | * - If AP mode does not yet support radar detection/DFS | ||
265 | * do not allow AP mode | ||
266 | */ | ||
267 | if (!(ch->flags & IEEE80211_CHAN_DISABLED)) | ||
268 | ch->flags |= IEEE80211_CHAN_RADAR | | ||
269 | IEEE80211_CHAN_NO_IBSS | | ||
270 | IEEE80211_CHAN_PASSIVE_SCAN; | ||
271 | } | ||
272 | } | ||
151 | 273 | ||
152 | if (test_bit(ATH9K_MODE_11NA_HT20, ah->ah_caps.wireless_modes) | 274 | void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby) |
153 | && (!country->allow11na20)) | 275 | { |
154 | clear_bit(ATH9K_MODE_11NA_HT20, modes_allowed); | 276 | struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); |
277 | struct ath_softc *sc = hw->priv; | ||
278 | struct ath_hal *ah = sc->sc_ah; | ||
279 | |||
280 | switch (ah->regpair->regDmnEnum) { | ||
281 | case 0x60: | ||
282 | case 0x63: | ||
283 | case 0x66: | ||
284 | case 0x67: | ||
285 | ath9k_reg_apply_5ghz_adhoc_flags(wiphy, setby); | ||
286 | break; | ||
287 | case 0x68: | ||
288 | ath9k_reg_apply_5ghz_adhoc_flags(wiphy, setby); | ||
289 | ath9k_reg_apply_active_scan_flags(wiphy, setby); | ||
290 | break; | ||
291 | } | ||
292 | return; | ||
293 | } | ||
155 | 294 | ||
156 | if (test_bit(ATH9K_MODE_11NG_HT40PLUS, ah->ah_caps.wireless_modes) && | 295 | int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) |
157 | (!country->allow11ng40)) | 296 | { |
158 | clear_bit(ATH9K_MODE_11NG_HT40PLUS, modes_allowed); | 297 | struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); |
298 | struct ath_softc *sc = hw->priv; | ||
159 | 299 | ||
160 | if (test_bit(ATH9K_MODE_11NG_HT40MINUS, ah->ah_caps.wireless_modes) && | 300 | /* We always apply this */ |
161 | (!country->allow11ng40)) | 301 | ath9k_reg_apply_radar_flags(wiphy); |
162 | clear_bit(ATH9K_MODE_11NG_HT40MINUS, modes_allowed); | ||
163 | 302 | ||
164 | if (test_bit(ATH9K_MODE_11NA_HT40PLUS, ah->ah_caps.wireless_modes) && | 303 | switch (request->initiator) { |
165 | (!country->allow11na40)) | 304 | case REGDOM_SET_BY_DRIVER: |
166 | clear_bit(ATH9K_MODE_11NA_HT40PLUS, modes_allowed); | 305 | case REGDOM_SET_BY_INIT: |
306 | case REGDOM_SET_BY_CORE: | ||
307 | case REGDOM_SET_BY_USER: | ||
308 | break; | ||
309 | case REGDOM_SET_BY_COUNTRY_IE: | ||
310 | if (ath9k_is_world_regd(sc->sc_ah)) | ||
311 | ath9k_reg_apply_world_flags(wiphy, request->initiator); | ||
312 | break; | ||
313 | } | ||
167 | 314 | ||
168 | if (test_bit(ATH9K_MODE_11NA_HT40MINUS, ah->ah_caps.wireless_modes) && | 315 | return 0; |
169 | (!country->allow11na40)) | ||
170 | clear_bit(ATH9K_MODE_11NA_HT40MINUS, modes_allowed); | ||
171 | } | 316 | } |
172 | 317 | ||
173 | bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah) | 318 | bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah) |
174 | { | 319 | { |
175 | u16 rd; | 320 | u16 rd = ath9k_regd_get_eepromRD(ah); |
176 | 321 | int i; | |
177 | rd = ath9k_regd_get_eepromRD(ah); | ||
178 | 322 | ||
179 | switch (rd) { | 323 | if (rd & COUNTRY_ERD_FLAG) { |
180 | case FCC4_FCCA: | 324 | /* EEPROM value is a country code */ |
181 | case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG): | 325 | u16 cc = rd & ~COUNTRY_ERD_FLAG; |
182 | return true; | 326 | for (i = 0; i < ARRAY_SIZE(allCountries); i++) |
183 | case DEBUG_REG_DMN: | 327 | if (allCountries[i].countryCode == cc) |
184 | case NO_ENUMRD: | 328 | return true; |
185 | if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49) | 329 | } else { |
186 | return true; | 330 | /* EEPROM value is a regpair value */ |
187 | break; | 331 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) |
332 | if (regDomainPairs[i].regDmnEnum == rd) | ||
333 | return true; | ||
188 | } | 334 | } |
335 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
336 | "invalid regulatory domain/country code 0x%x\n", rd); | ||
189 | return false; | 337 | return false; |
190 | } | 338 | } |
191 | 339 | ||
340 | /* EEPROM country code to regpair mapping */ | ||
192 | static struct country_code_to_enum_rd* | 341 | static struct country_code_to_enum_rd* |
193 | ath9k_regd_find_country(u16 countryCode) | 342 | ath9k_regd_find_country(u16 countryCode) |
194 | { | 343 | { |
@@ -201,10 +350,23 @@ ath9k_regd_find_country(u16 countryCode) | |||
201 | return NULL; | 350 | return NULL; |
202 | } | 351 | } |
203 | 352 | ||
353 | /* EEPROM rd code to regpair mapping */ | ||
354 | static struct country_code_to_enum_rd* | ||
355 | ath9k_regd_find_country_by_rd(int regdmn) | ||
356 | { | ||
357 | int i; | ||
358 | |||
359 | for (i = 0; i < ARRAY_SIZE(allCountries); i++) { | ||
360 | if (allCountries[i].regDmnEnum == regdmn) | ||
361 | return &allCountries[i]; | ||
362 | } | ||
363 | return NULL; | ||
364 | } | ||
365 | |||
366 | /* Returns the map of the EEPROM set RD to a country code */ | ||
204 | static u16 ath9k_regd_get_default_country(struct ath_hal *ah) | 367 | static u16 ath9k_regd_get_default_country(struct ath_hal *ah) |
205 | { | 368 | { |
206 | u16 rd; | 369 | u16 rd; |
207 | int i; | ||
208 | 370 | ||
209 | rd = ath9k_regd_get_eepromRD(ah); | 371 | rd = ath9k_regd_get_eepromRD(ah); |
210 | if (rd & COUNTRY_ERD_FLAG) { | 372 | if (rd & COUNTRY_ERD_FLAG) { |
@@ -216,798 +378,104 @@ static u16 ath9k_regd_get_default_country(struct ath_hal *ah) | |||
216 | return cc; | 378 | return cc; |
217 | } | 379 | } |
218 | 380 | ||
219 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) | ||
220 | if (regDomainPairs[i].regDmnEnum == rd) { | ||
221 | if (regDomainPairs[i].singleCC != 0) | ||
222 | return regDomainPairs[i].singleCC; | ||
223 | else | ||
224 | i = ARRAY_SIZE(regDomainPairs); | ||
225 | } | ||
226 | return CTRY_DEFAULT; | 381 | return CTRY_DEFAULT; |
227 | } | 382 | } |
228 | 383 | ||
229 | static bool ath9k_regd_is_valid_reg_domain(int regDmn, | 384 | static struct reg_dmn_pair_mapping* |
230 | struct regDomain *rd) | 385 | ath9k_get_regpair(int regdmn) |
231 | { | ||
232 | int i; | ||
233 | |||
234 | for (i = 0; i < ARRAY_SIZE(regDomains); i++) { | ||
235 | if (regDomains[i].regDmnEnum == regDmn) { | ||
236 | if (rd != NULL) { | ||
237 | memcpy(rd, ®Domains[i], | ||
238 | sizeof(struct regDomain)); | ||
239 | } | ||
240 | return true; | ||
241 | } | ||
242 | } | ||
243 | return false; | ||
244 | } | ||
245 | |||
246 | static bool ath9k_regd_is_valid_reg_domainPair(int regDmnPair) | ||
247 | { | 386 | { |
248 | int i; | 387 | int i; |
249 | 388 | ||
250 | if (regDmnPair == NO_ENUMRD) | 389 | if (regdmn == NO_ENUMRD) |
251 | return false; | 390 | return NULL; |
252 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { | 391 | for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { |
253 | if (regDomainPairs[i].regDmnEnum == regDmnPair) | 392 | if (regDomainPairs[i].regDmnEnum == regdmn) |
254 | return true; | 393 | return ®DomainPairs[i]; |
255 | } | 394 | } |
256 | return false; | 395 | return NULL; |
257 | } | ||
258 | |||
259 | static bool | ||
260 | ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn, | ||
261 | u16 channelFlag, struct regDomain *rd) | ||
262 | { | ||
263 | int i, found; | ||
264 | u64 flags = NO_REQ; | ||
265 | struct reg_dmn_pair_mapping *regPair = NULL; | ||
266 | int regOrg; | ||
267 | |||
268 | regOrg = regDmn; | ||
269 | if (regDmn == CTRY_DEFAULT) { | ||
270 | u16 rdnum; | ||
271 | rdnum = ath9k_regd_get_eepromRD(ah); | ||
272 | |||
273 | if (!(rdnum & COUNTRY_ERD_FLAG)) { | ||
274 | if (ath9k_regd_is_valid_reg_domain(rdnum, NULL) || | ||
275 | ath9k_regd_is_valid_reg_domainPair(rdnum)) { | ||
276 | regDmn = rdnum; | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | if ((regDmn & MULTI_DOMAIN_MASK) == 0) { | ||
282 | for (i = 0, found = 0; | ||
283 | (i < ARRAY_SIZE(regDomainPairs)) && (!found); i++) { | ||
284 | if (regDomainPairs[i].regDmnEnum == regDmn) { | ||
285 | regPair = ®DomainPairs[i]; | ||
286 | found = 1; | ||
287 | } | ||
288 | } | ||
289 | if (!found) { | ||
290 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
291 | "Failed to find reg domain pair %u\n", regDmn); | ||
292 | return false; | ||
293 | } | ||
294 | if (!(channelFlag & CHANNEL_2GHZ)) { | ||
295 | regDmn = regPair->regDmn5GHz; | ||
296 | flags = regPair->flags5GHz; | ||
297 | } | ||
298 | if (channelFlag & CHANNEL_2GHZ) { | ||
299 | regDmn = regPair->regDmn2GHz; | ||
300 | flags = regPair->flags2GHz; | ||
301 | } | ||
302 | } | ||
303 | |||
304 | found = ath9k_regd_is_valid_reg_domain(regDmn, rd); | ||
305 | if (!found) { | ||
306 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
307 | "Failed to find unitary reg domain %u\n", regDmn); | ||
308 | return false; | ||
309 | } else { | ||
310 | rd->pscan &= regPair->pscanMask; | ||
311 | if (((regOrg & MULTI_DOMAIN_MASK) == 0) && | ||
312 | (flags != NO_REQ)) { | ||
313 | rd->flags = flags; | ||
314 | } | ||
315 | |||
316 | rd->flags &= (channelFlag & CHANNEL_2GHZ) ? | ||
317 | REG_DOMAIN_2GHZ_MASK : REG_DOMAIN_5GHZ_MASK; | ||
318 | return true; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | static bool ath9k_regd_is_bit_set(int bit, u64 *bitmask) | ||
323 | { | ||
324 | int byteOffset, bitnum; | ||
325 | u64 val; | ||
326 | |||
327 | byteOffset = bit / 64; | ||
328 | bitnum = bit - byteOffset * 64; | ||
329 | val = ((u64) 1) << bitnum; | ||
330 | if (bitmask[byteOffset] & val) | ||
331 | return true; | ||
332 | else | ||
333 | return false; | ||
334 | } | ||
335 | |||
336 | static void | ||
337 | ath9k_regd_add_reg_classid(u8 *regclassids, u32 maxregids, | ||
338 | u32 *nregids, u8 regclassid) | ||
339 | { | ||
340 | int i; | ||
341 | |||
342 | if (regclassid == 0) | ||
343 | return; | ||
344 | |||
345 | for (i = 0; i < maxregids; i++) { | ||
346 | if (regclassids[i] == regclassid) | ||
347 | return; | ||
348 | if (regclassids[i] == 0) | ||
349 | break; | ||
350 | } | ||
351 | |||
352 | if (i == maxregids) | ||
353 | return; | ||
354 | else { | ||
355 | regclassids[i] = regclassid; | ||
356 | *nregids += 1; | ||
357 | } | ||
358 | |||
359 | return; | ||
360 | } | ||
361 | |||
362 | static bool | ||
363 | ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal *ah, | ||
364 | enum reg_ext_bitmap bit) | ||
365 | { | ||
366 | return (ah->ah_currentRDExt & (1 << bit)) ? true : false; | ||
367 | } | ||
368 | |||
369 | #ifdef ATH_NF_PER_CHAN | ||
370 | |||
371 | static void ath9k_regd_init_rf_buffer(struct ath9k_channel *ichans, | ||
372 | int nchans) | ||
373 | { | ||
374 | int i, j, next; | ||
375 | |||
376 | for (next = 0; next < nchans; next++) { | ||
377 | for (i = 0; i < NUM_NF_READINGS; i++) { | ||
378 | ichans[next].nfCalHist[i].currIndex = 0; | ||
379 | ichans[next].nfCalHist[i].privNF = | ||
380 | AR_PHY_CCA_MAX_GOOD_VALUE; | ||
381 | ichans[next].nfCalHist[i].invalidNFcount = | ||
382 | AR_PHY_CCA_FILTERWINDOW_LENGTH; | ||
383 | for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { | ||
384 | ichans[next].nfCalHist[i].nfCalBuffer[j] = | ||
385 | AR_PHY_CCA_MAX_GOOD_VALUE; | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | } | ||
390 | #endif | ||
391 | |||
392 | static int ath9k_regd_is_chan_present(struct ath_hal *ah, | ||
393 | u16 c) | ||
394 | { | ||
395 | int i; | ||
396 | |||
397 | for (i = 0; i < 150; i++) { | ||
398 | if (!ah->ah_channels[i].channel) | ||
399 | return -1; | ||
400 | else if (ah->ah_channels[i].channel == c) | ||
401 | return i; | ||
402 | } | ||
403 | |||
404 | return -1; | ||
405 | } | ||
406 | |||
407 | static bool | ||
408 | ath9k_regd_add_channel(struct ath_hal *ah, | ||
409 | u16 c, | ||
410 | u16 c_lo, | ||
411 | u16 c_hi, | ||
412 | u16 maxChan, | ||
413 | u8 ctl, | ||
414 | int pos, | ||
415 | struct regDomain rd5GHz, | ||
416 | struct RegDmnFreqBand *fband, | ||
417 | struct regDomain *rd, | ||
418 | const struct cmode *cm, | ||
419 | struct ath9k_channel *ichans, | ||
420 | bool enableExtendedChannels) | ||
421 | { | ||
422 | struct ath9k_channel *chan; | ||
423 | int ret; | ||
424 | u32 channelFlags = 0; | ||
425 | u8 privFlags = 0; | ||
426 | |||
427 | if (!(c_lo <= c && c <= c_hi)) { | ||
428 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
429 | "c %u out of range [%u..%u]\n", | ||
430 | c, c_lo, c_hi); | ||
431 | return false; | ||
432 | } | ||
433 | if ((fband->channelBW == CHANNEL_HALF_BW) && | ||
434 | !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_HALFRATE)) { | ||
435 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
436 | "Skipping %u half rate channel\n", c); | ||
437 | return false; | ||
438 | } | ||
439 | |||
440 | if ((fband->channelBW == CHANNEL_QUARTER_BW) && | ||
441 | !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_QUARTERRATE)) { | ||
442 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
443 | "Skipping %u quarter rate channel\n", c); | ||
444 | return false; | ||
445 | } | ||
446 | |||
447 | if (((c + fband->channelSep) / 2) > (maxChan + HALF_MAXCHANBW)) { | ||
448 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
449 | "c %u > maxChan %u\n", c, maxChan); | ||
450 | return false; | ||
451 | } | ||
452 | |||
453 | if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) { | ||
454 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
455 | "Skipping ecm channel\n"); | ||
456 | return false; | ||
457 | } | ||
458 | |||
459 | if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == NL80211_IFTYPE_AP)) { | ||
460 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
461 | "Skipping HOSTAP channel\n"); | ||
462 | return false; | ||
463 | } | ||
464 | |||
465 | if (IS_HT40_MODE(cm->mode) && | ||
466 | !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_FCC_DFS_HT40)) && | ||
467 | (fband->useDfs) && | ||
468 | (rd->conformanceTestLimit != MKK)) { | ||
469 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
470 | "Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n"); | ||
471 | return false; | ||
472 | } | ||
473 | |||
474 | if (IS_HT40_MODE(cm->mode) && | ||
475 | !(ath9k_regd_get_eeprom_reg_ext_bits(ah, | ||
476 | REG_EXT_JAPAN_NONDFS_HT40)) && | ||
477 | !(fband->useDfs) && (rd->conformanceTestLimit == MKK)) { | ||
478 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
479 | "Skipping HT40 channel (en_jap_ht40 = 0)\n"); | ||
480 | return false; | ||
481 | } | ||
482 | |||
483 | if (IS_HT40_MODE(cm->mode) && | ||
484 | !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_JAPAN_DFS_HT40)) && | ||
485 | (fband->useDfs) && | ||
486 | (rd->conformanceTestLimit == MKK)) { | ||
487 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
488 | "Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n"); | ||
489 | return false; | ||
490 | } | ||
491 | |||
492 | /* Calculate channel flags */ | ||
493 | |||
494 | channelFlags = cm->flags; | ||
495 | |||
496 | switch (fband->channelBW) { | ||
497 | case CHANNEL_HALF_BW: | ||
498 | channelFlags |= CHANNEL_HALF; | ||
499 | break; | ||
500 | case CHANNEL_QUARTER_BW: | ||
501 | channelFlags |= CHANNEL_QUARTER; | ||
502 | break; | ||
503 | } | ||
504 | |||
505 | if (fband->usePassScan & rd->pscan) | ||
506 | channelFlags |= CHANNEL_PASSIVE; | ||
507 | else | ||
508 | channelFlags &= ~CHANNEL_PASSIVE; | ||
509 | if (fband->useDfs & rd->dfsMask) | ||
510 | privFlags = CHANNEL_DFS; | ||
511 | else | ||
512 | privFlags = 0; | ||
513 | if (rd->flags & LIMIT_FRAME_4MS) | ||
514 | privFlags |= CHANNEL_4MS_LIMIT; | ||
515 | if (privFlags & CHANNEL_DFS) | ||
516 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
517 | if (rd->flags & ADHOC_PER_11D) | ||
518 | privFlags |= CHANNEL_PER_11D_ADHOC; | ||
519 | |||
520 | if (channelFlags & CHANNEL_PASSIVE) { | ||
521 | if ((c < 2412) || (c > 2462)) { | ||
522 | if (rd5GHz.regDmnEnum == MKK1 || | ||
523 | rd5GHz.regDmnEnum == MKK2) { | ||
524 | u32 regcap = ah->ah_caps.reg_cap; | ||
525 | if (!(regcap & | ||
526 | (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | | ||
527 | AR_EEPROM_EEREGCAP_EN_KK_U2 | | ||
528 | AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) && | ||
529 | isUNII1OddChan(c)) { | ||
530 | channelFlags &= ~CHANNEL_PASSIVE; | ||
531 | } else { | ||
532 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
533 | } | ||
534 | } else { | ||
535 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
536 | } | ||
537 | } | ||
538 | } | ||
539 | |||
540 | if ((cm->mode == ATH9K_MODE_11A) || | ||
541 | (cm->mode == ATH9K_MODE_11NA_HT20) || | ||
542 | (cm->mode == ATH9K_MODE_11NA_HT40PLUS) || | ||
543 | (cm->mode == ATH9K_MODE_11NA_HT40MINUS)) { | ||
544 | if (rd->flags & (ADHOC_NO_11A | DISALLOW_ADHOC_11A)) | ||
545 | privFlags |= CHANNEL_DISALLOW_ADHOC; | ||
546 | } | ||
547 | |||
548 | /* Fill in channel details */ | ||
549 | |||
550 | ret = ath9k_regd_is_chan_present(ah, c); | ||
551 | if (ret == -1) { | ||
552 | chan = &ah->ah_channels[pos]; | ||
553 | chan->channel = c; | ||
554 | chan->maxRegTxPower = fband->powerDfs; | ||
555 | chan->antennaMax = fband->antennaMax; | ||
556 | chan->regDmnFlags = rd->flags; | ||
557 | chan->maxTxPower = AR5416_MAX_RATE_POWER; | ||
558 | chan->minTxPower = AR5416_MAX_RATE_POWER; | ||
559 | chan->channelFlags = channelFlags; | ||
560 | chan->privFlags = privFlags; | ||
561 | } else { | ||
562 | chan = &ah->ah_channels[ret]; | ||
563 | chan->channelFlags |= channelFlags; | ||
564 | chan->privFlags |= privFlags; | ||
565 | } | ||
566 | |||
567 | /* Set CTLs */ | ||
568 | |||
569 | if ((cm->flags & CHANNEL_ALL) == CHANNEL_A) | ||
570 | chan->conformanceTestLimit[0] = ctl; | ||
571 | else if ((cm->flags & CHANNEL_ALL) == CHANNEL_B) | ||
572 | chan->conformanceTestLimit[1] = ctl; | ||
573 | else if ((cm->flags & CHANNEL_ALL) == CHANNEL_G) | ||
574 | chan->conformanceTestLimit[2] = ctl; | ||
575 | |||
576 | return (ret == -1) ? true : false; | ||
577 | } | ||
578 | |||
579 | static bool ath9k_regd_japan_check(struct ath_hal *ah, | ||
580 | int b, | ||
581 | struct regDomain *rd5GHz) | ||
582 | { | ||
583 | bool skipband = false; | ||
584 | int i; | ||
585 | u32 regcap; | ||
586 | |||
587 | for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) { | ||
588 | if (j_bandcheck[i].freqbandbit == b) { | ||
589 | regcap = ah->ah_caps.reg_cap; | ||
590 | if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) { | ||
591 | skipband = true; | ||
592 | } else if ((regcap & AR_EEPROM_EEREGCAP_EN_KK_U2) || | ||
593 | (regcap & AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) { | ||
594 | rd5GHz->dfsMask |= DFS_MKK4; | ||
595 | rd5GHz->pscan |= PSCAN_MKK3; | ||
596 | } | ||
597 | break; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
602 | "Skipping %d freq band\n", j_bandcheck[i].freqbandbit); | ||
603 | |||
604 | return skipband; | ||
605 | } | 396 | } |
606 | 397 | ||
607 | bool | 398 | int ath9k_regd_init(struct ath_hal *ah) |
608 | ath9k_regd_init_channels(struct ath_hal *ah, | ||
609 | u32 maxchans, | ||
610 | u32 *nchans, u8 *regclassids, | ||
611 | u32 maxregids, u32 *nregids, u16 cc, | ||
612 | bool enableOutdoor, | ||
613 | bool enableExtendedChannels) | ||
614 | { | 399 | { |
615 | u16 maxChan = 7000; | ||
616 | struct country_code_to_enum_rd *country = NULL; | 400 | struct country_code_to_enum_rd *country = NULL; |
617 | struct regDomain rd5GHz, rd2GHz; | ||
618 | const struct cmode *cm; | ||
619 | struct ath9k_channel *ichans = &ah->ah_channels[0]; | ||
620 | int next = 0, b; | ||
621 | u8 ctl; | ||
622 | int regdmn; | 401 | int regdmn; |
623 | u16 chanSep; | ||
624 | unsigned long *modes_avail; | ||
625 | DECLARE_BITMAP(modes_allowed, ATH9K_MODE_MAX); | ||
626 | |||
627 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "cc %u %s %s\n", cc, | ||
628 | enableOutdoor ? "Enable outdoor" : "", | ||
629 | enableExtendedChannels ? "Enable ecm" : ""); | ||
630 | |||
631 | if (!ath9k_regd_is_ccode_valid(ah, cc)) { | ||
632 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
633 | "Invalid country code %d\n", cc); | ||
634 | return false; | ||
635 | } | ||
636 | 402 | ||
637 | if (!ath9k_regd_is_eeprom_valid(ah)) { | 403 | if (!ath9k_regd_is_eeprom_valid(ah)) { |
638 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | 404 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, |
639 | "Invalid EEPROM contents\n"); | 405 | "Invalid EEPROM contents\n"); |
640 | return false; | 406 | return -EINVAL; |
641 | } | 407 | } |
642 | 408 | ||
643 | ah->ah_countryCode = ath9k_regd_get_default_country(ah); | 409 | ah->ah_countryCode = ath9k_regd_get_default_country(ah); |
644 | 410 | ||
645 | if (ah->ah_countryCode == CTRY_DEFAULT) { | 411 | if (ah->ah_countryCode == CTRY_DEFAULT && |
646 | ah->ah_countryCode = cc & COUNTRY_CODE_MASK; | 412 | ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT) |
647 | if ((ah->ah_countryCode == CTRY_DEFAULT) && | 413 | ah->ah_countryCode = CTRY_UNITED_STATES; |
648 | (ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) { | ||
649 | ah->ah_countryCode = CTRY_UNITED_STATES; | ||
650 | } | ||
651 | } | ||
652 | 414 | ||
653 | #ifdef AH_SUPPORT_11D | ||
654 | if (ah->ah_countryCode == CTRY_DEFAULT) { | 415 | if (ah->ah_countryCode == CTRY_DEFAULT) { |
655 | regdmn = ath9k_regd_get_eepromRD(ah); | 416 | regdmn = ath9k_regd_get_eepromRD(ah); |
656 | country = NULL; | 417 | country = NULL; |
657 | } else { | 418 | } else { |
658 | #endif | ||
659 | country = ath9k_regd_find_country(ah->ah_countryCode); | 419 | country = ath9k_regd_find_country(ah->ah_countryCode); |
660 | if (country == NULL) { | 420 | if (country == NULL) { |
661 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | 421 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, |
662 | "Country is NULL!!!!, cc= %d\n", | 422 | "Country is NULL!!!!, cc= %d\n", |
663 | ah->ah_countryCode); | 423 | ah->ah_countryCode); |
664 | return false; | 424 | return -EINVAL; |
665 | } else { | 425 | } else |
666 | regdmn = country->regDmnEnum; | 426 | regdmn = country->regDmnEnum; |
667 | #ifdef AH_SUPPORT_11D | ||
668 | if (((ath9k_regd_get_eepromRD(ah) & | ||
669 | WORLD_SKU_MASK) == WORLD_SKU_PREFIX) && | ||
670 | (cc == CTRY_UNITED_STATES)) { | ||
671 | if (!isWwrSKU_NoMidband(ah) | ||
672 | && ath9k_regd_is_fcc_midband_supported(ah)) | ||
673 | regdmn = FCC3_FCCA; | ||
674 | else | ||
675 | regdmn = FCC1_FCCA; | ||
676 | } | ||
677 | #endif | ||
678 | } | ||
679 | #ifdef AH_SUPPORT_11D | ||
680 | } | ||
681 | #endif | ||
682 | if (!ath9k_regd_get_wmode_regdomain(ah, | ||
683 | regdmn, | ||
684 | ~CHANNEL_2GHZ, | ||
685 | &rd5GHz)) { | ||
686 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
687 | "Couldn't find unitary " | ||
688 | "5GHz reg domain for country %u\n", | ||
689 | ah->ah_countryCode); | ||
690 | return false; | ||
691 | } | ||
692 | if (!ath9k_regd_get_wmode_regdomain(ah, | ||
693 | regdmn, | ||
694 | CHANNEL_2GHZ, | ||
695 | &rd2GHz)) { | ||
696 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
697 | "Couldn't find unitary 2GHz " | ||
698 | "reg domain for country %u\n", | ||
699 | ah->ah_countryCode); | ||
700 | return false; | ||
701 | } | 427 | } |
702 | 428 | ||
703 | if (!isWwrSKU(ah) && ((rd5GHz.regDmnEnum == FCC1) || | 429 | ah->ah_currentRDInUse = regdmn; |
704 | (rd5GHz.regDmnEnum == FCC2))) { | 430 | ah->regpair = ath9k_get_regpair(regdmn); |
705 | if (ath9k_regd_is_fcc_midband_supported(ah)) { | ||
706 | if (!ath9k_regd_get_wmode_regdomain(ah, | ||
707 | FCC3_FCCA, | ||
708 | ~CHANNEL_2GHZ, | ||
709 | &rd5GHz)) { | ||
710 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
711 | "Couldn't find unitary 5GHz " | ||
712 | "reg domain for country %u\n", | ||
713 | ah->ah_countryCode); | ||
714 | return false; | ||
715 | } | ||
716 | } | ||
717 | } | ||
718 | |||
719 | if (country == NULL) { | ||
720 | modes_avail = ah->ah_caps.wireless_modes; | ||
721 | } else { | ||
722 | ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz, modes_allowed); | ||
723 | modes_avail = modes_allowed; | ||
724 | |||
725 | if (!enableOutdoor) | ||
726 | maxChan = country->outdoorChanStart; | ||
727 | } | ||
728 | |||
729 | next = 0; | ||
730 | |||
731 | if (maxchans > ARRAY_SIZE(ah->ah_channels)) | ||
732 | maxchans = ARRAY_SIZE(ah->ah_channels); | ||
733 | |||
734 | for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) { | ||
735 | u16 c, c_hi, c_lo; | ||
736 | u64 *channelBM = NULL; | ||
737 | struct regDomain *rd = NULL; | ||
738 | struct RegDmnFreqBand *fband = NULL, *freqs; | ||
739 | int8_t low_adj = 0, hi_adj = 0; | ||
740 | |||
741 | if (!test_bit(cm->mode, modes_avail)) { | ||
742 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
743 | "!avail mode %d flags 0x%x\n", | ||
744 | cm->mode, cm->flags); | ||
745 | continue; | ||
746 | } | ||
747 | if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) { | ||
748 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
749 | "channels 0x%x not supported " | ||
750 | "by hardware\n", cm->flags); | ||
751 | continue; | ||
752 | } | ||
753 | |||
754 | switch (cm->mode) { | ||
755 | case ATH9K_MODE_11A: | ||
756 | case ATH9K_MODE_11NA_HT20: | ||
757 | case ATH9K_MODE_11NA_HT40PLUS: | ||
758 | case ATH9K_MODE_11NA_HT40MINUS: | ||
759 | rd = &rd5GHz; | ||
760 | channelBM = rd->chan11a; | ||
761 | freqs = ®Dmn5GhzFreq[0]; | ||
762 | ctl = rd->conformanceTestLimit; | ||
763 | break; | ||
764 | case ATH9K_MODE_11B: | ||
765 | rd = &rd2GHz; | ||
766 | channelBM = rd->chan11b; | ||
767 | freqs = ®Dmn2GhzFreq[0]; | ||
768 | ctl = rd->conformanceTestLimit | CTL_11B; | ||
769 | break; | ||
770 | case ATH9K_MODE_11G: | ||
771 | case ATH9K_MODE_11NG_HT20: | ||
772 | case ATH9K_MODE_11NG_HT40PLUS: | ||
773 | case ATH9K_MODE_11NG_HT40MINUS: | ||
774 | rd = &rd2GHz; | ||
775 | channelBM = rd->chan11g; | ||
776 | freqs = ®Dmn2Ghz11gFreq[0]; | ||
777 | ctl = rd->conformanceTestLimit | CTL_11G; | ||
778 | break; | ||
779 | default: | ||
780 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
781 | "Unknown HAL mode 0x%x\n", cm->mode); | ||
782 | continue; | ||
783 | } | ||
784 | |||
785 | if (ath9k_regd_is_chan_bm_zero(channelBM)) | ||
786 | continue; | ||
787 | |||
788 | if ((cm->mode == ATH9K_MODE_11NA_HT40PLUS) || | ||
789 | (cm->mode == ATH9K_MODE_11NG_HT40PLUS)) { | ||
790 | hi_adj = -20; | ||
791 | } | ||
792 | |||
793 | if ((cm->mode == ATH9K_MODE_11NA_HT40MINUS) || | ||
794 | (cm->mode == ATH9K_MODE_11NG_HT40MINUS)) { | ||
795 | low_adj = 20; | ||
796 | } | ||
797 | |||
798 | /* XXX: Add a helper here instead */ | ||
799 | for (b = 0; b < 64 * BMLEN; b++) { | ||
800 | if (ath9k_regd_is_bit_set(b, channelBM)) { | ||
801 | fband = &freqs[b]; | ||
802 | if (rd5GHz.regDmnEnum == MKK1 | ||
803 | || rd5GHz.regDmnEnum == MKK2) { | ||
804 | if (ath9k_regd_japan_check(ah, | ||
805 | b, | ||
806 | &rd5GHz)) | ||
807 | continue; | ||
808 | } | ||
809 | |||
810 | ath9k_regd_add_reg_classid(regclassids, | ||
811 | maxregids, | ||
812 | nregids, | ||
813 | fband-> | ||
814 | regClassId); | ||
815 | |||
816 | if (IS_HT40_MODE(cm->mode) && (rd == &rd5GHz)) { | ||
817 | chanSep = 40; | ||
818 | if (fband->lowChannel == 5280) | ||
819 | low_adj += 20; | ||
820 | |||
821 | if (fband->lowChannel == 5170) | ||
822 | continue; | ||
823 | } else | ||
824 | chanSep = fband->channelSep; | ||
825 | |||
826 | for (c = fband->lowChannel + low_adj; | ||
827 | ((c <= (fband->highChannel + hi_adj)) && | ||
828 | (c >= (fband->lowChannel + low_adj))); | ||
829 | c += chanSep) { | ||
830 | if (next >= maxchans) { | ||
831 | DPRINTF(ah->ah_sc, | ||
832 | ATH_DBG_REGULATORY, | ||
833 | "too many channels " | ||
834 | "for channel table\n"); | ||
835 | goto done; | ||
836 | } | ||
837 | if (ath9k_regd_add_channel(ah, | ||
838 | c, c_lo, c_hi, | ||
839 | maxChan, ctl, | ||
840 | next, | ||
841 | rd5GHz, | ||
842 | fband, rd, cm, | ||
843 | ichans, | ||
844 | enableExtendedChannels)) | ||
845 | next++; | ||
846 | } | ||
847 | if (IS_HT40_MODE(cm->mode) && | ||
848 | (fband->lowChannel == 5280)) { | ||
849 | low_adj -= 20; | ||
850 | } | ||
851 | } | ||
852 | } | ||
853 | } | ||
854 | done: | ||
855 | if (next != 0) { | ||
856 | int i; | ||
857 | 431 | ||
858 | if (next > ARRAY_SIZE(ah->ah_channels)) { | 432 | if (!ah->regpair) { |
859 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | 433 | DPRINTF(ah->ah_sc, ATH_DBG_FATAL, |
860 | "too many channels %u; truncating to %u\n", | 434 | "No regulatory domain pair found, cannot continue\n"); |
861 | next, (int) ARRAY_SIZE(ah->ah_channels)); | 435 | return -EINVAL; |
862 | next = ARRAY_SIZE(ah->ah_channels); | ||
863 | } | ||
864 | #ifdef ATH_NF_PER_CHAN | ||
865 | ath9k_regd_init_rf_buffer(ichans, next); | ||
866 | #endif | ||
867 | ath9k_regd_sort(ichans, next, | ||
868 | sizeof(struct ath9k_channel), | ||
869 | ath9k_regd_chansort); | ||
870 | |||
871 | ah->ah_nchan = next; | ||
872 | |||
873 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "Channel list:\n"); | ||
874 | for (i = 0; i < next; i++) { | ||
875 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
876 | "chan: %d flags: 0x%x\n", | ||
877 | ah->ah_channels[i].channel, | ||
878 | ah->ah_channels[i].channelFlags); | ||
879 | } | ||
880 | } | 436 | } |
881 | *nchans = next; | ||
882 | 437 | ||
883 | ah->ah_countryCode = ah->ah_countryCode; | 438 | if (!country) |
439 | country = ath9k_regd_find_country_by_rd(regdmn); | ||
884 | 440 | ||
885 | ah->ah_currentRDInUse = regdmn; | 441 | if (country) { |
886 | ah->ah_currentRD5G = rd5GHz.regDmnEnum; | 442 | ah->alpha2[0] = country->isoName[0]; |
887 | ah->ah_currentRD2G = rd2GHz.regDmnEnum; | 443 | ah->alpha2[1] = country->isoName[1]; |
888 | if (country == NULL) { | ||
889 | ah->ah_iso[0] = 0; | ||
890 | ah->ah_iso[1] = 0; | ||
891 | } else { | 444 | } else { |
892 | ah->ah_iso[0] = country->isoName[0]; | 445 | ah->alpha2[0] = '0'; |
893 | ah->ah_iso[1] = country->isoName[1]; | 446 | ah->alpha2[1] = '0'; |
894 | } | 447 | } |
895 | 448 | ||
896 | return next != 0; | ||
897 | } | ||
898 | |||
899 | struct ath9k_channel* | ||
900 | ath9k_regd_check_channel(struct ath_hal *ah, | ||
901 | const struct ath9k_channel *c) | ||
902 | { | ||
903 | struct ath9k_channel *base, *cc; | ||
904 | |||
905 | int flags = c->channelFlags & CHAN_FLAGS; | ||
906 | int n, lim; | ||
907 | |||
908 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | 449 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, |
909 | "channel %u/0x%x (0x%x) requested\n", | 450 | "Country alpha2 being used: %c%c\n", |
910 | c->channel, c->channelFlags, flags); | 451 | "Regpair detected: 0x%0x\n", |
911 | 452 | ah->alpha2[0], ah->alpha2[1], | |
912 | cc = ah->ah_curchan; | 453 | ah->regpair->regDmnEnum); |
913 | if (cc != NULL && cc->channel == c->channel && | ||
914 | (cc->channelFlags & CHAN_FLAGS) == flags) { | ||
915 | if ((cc->privFlags & CHANNEL_INTERFERENCE) && | ||
916 | (cc->privFlags & CHANNEL_DFS)) | ||
917 | return NULL; | ||
918 | else | ||
919 | return cc; | ||
920 | } | ||
921 | 454 | ||
922 | base = ah->ah_channels; | 455 | return 0; |
923 | n = ah->ah_nchan; | ||
924 | |||
925 | for (lim = n; lim != 0; lim >>= 1) { | ||
926 | int d; | ||
927 | cc = &base[lim >> 1]; | ||
928 | d = c->channel - cc->channel; | ||
929 | if (d == 0) { | ||
930 | if ((cc->channelFlags & CHAN_FLAGS) == flags) { | ||
931 | if ((cc->privFlags & CHANNEL_INTERFERENCE) && | ||
932 | (cc->privFlags & CHANNEL_DFS)) | ||
933 | return NULL; | ||
934 | else | ||
935 | return cc; | ||
936 | } | ||
937 | d = flags - (cc->channelFlags & CHAN_FLAGS); | ||
938 | } | ||
939 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, | ||
940 | "channel %u/0x%x d %d\n", | ||
941 | cc->channel, cc->channelFlags, d); | ||
942 | if (d > 0) { | ||
943 | base = cc + 1; | ||
944 | lim--; | ||
945 | } | ||
946 | } | ||
947 | DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "no match for %u/0x%x\n", | ||
948 | c->channel, c->channelFlags); | ||
949 | return NULL; | ||
950 | } | ||
951 | |||
952 | u32 | ||
953 | ath9k_regd_get_antenna_allowed(struct ath_hal *ah, | ||
954 | struct ath9k_channel *chan) | ||
955 | { | ||
956 | struct ath9k_channel *ichan = NULL; | ||
957 | |||
958 | ichan = ath9k_regd_check_channel(ah, chan); | ||
959 | if (!ichan) | ||
960 | return 0; | ||
961 | |||
962 | return ichan->antennaMax; | ||
963 | } | 456 | } |
964 | 457 | ||
965 | u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan) | 458 | u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan) |
966 | { | 459 | { |
967 | u32 ctl = NO_CTL; | 460 | u32 ctl = NO_CTL; |
968 | struct ath9k_channel *ichan; | ||
969 | 461 | ||
970 | if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) { | 462 | if (!ah->regpair || |
463 | (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah))) { | ||
971 | if (IS_CHAN_B(chan)) | 464 | if (IS_CHAN_B(chan)) |
972 | ctl = SD_NO_CTL | CTL_11B; | 465 | ctl = SD_NO_CTL | CTL_11B; |
973 | else if (IS_CHAN_G(chan)) | 466 | else if (IS_CHAN_G(chan)) |
974 | ctl = SD_NO_CTL | CTL_11G; | 467 | ctl = SD_NO_CTL | CTL_11G; |
975 | else | 468 | else |
976 | ctl = SD_NO_CTL | CTL_11A; | 469 | ctl = SD_NO_CTL | CTL_11A; |
977 | } else { | 470 | return ctl; |
978 | ichan = ath9k_regd_check_channel(ah, chan); | ||
979 | if (ichan != NULL) { | ||
980 | /* FIXME */ | ||
981 | if (IS_CHAN_A(ichan)) | ||
982 | ctl = ichan->conformanceTestLimit[0]; | ||
983 | else if (IS_CHAN_B(ichan)) | ||
984 | ctl = ichan->conformanceTestLimit[1]; | ||
985 | else if (IS_CHAN_G(ichan)) | ||
986 | ctl = ichan->conformanceTestLimit[2]; | ||
987 | |||
988 | if (IS_CHAN_G(chan) && (ctl & 0xf) == CTL_11B) | ||
989 | ctl = (ctl & ~0xf) | CTL_11G; | ||
990 | } | ||
991 | } | 471 | } |
992 | return ctl; | ||
993 | } | ||
994 | 472 | ||
995 | void ath9k_regd_get_current_country(struct ath_hal *ah, | 473 | if (IS_CHAN_B(chan)) |
996 | struct ath9k_country_entry *ctry) | 474 | ctl = ah->regpair->reg_2ghz_ctl | CTL_11B; |
997 | { | 475 | else if (IS_CHAN_G(chan)) |
998 | u16 rd = ath9k_regd_get_eepromRD(ah); | 476 | ctl = ah->regpair->reg_5ghz_ctl | CTL_11G; |
477 | else | ||
478 | ctl = ah->regpair->reg_5ghz_ctl | CTL_11A; | ||
999 | 479 | ||
1000 | ctry->isMultidomain = false; | 480 | return ctl; |
1001 | if (rd == CTRY_DEFAULT) | ||
1002 | ctry->isMultidomain = true; | ||
1003 | else if (!(rd & COUNTRY_ERD_FLAG)) | ||
1004 | ctry->isMultidomain = isWwrSKU(ah); | ||
1005 | |||
1006 | ctry->countryCode = ah->ah_countryCode; | ||
1007 | ctry->regDmnEnum = ah->ah_currentRD; | ||
1008 | ctry->regDmn5G = ah->ah_currentRD5G; | ||
1009 | ctry->regDmn2G = ah->ah_currentRD2G; | ||
1010 | ctry->iso[0] = ah->ah_iso[0]; | ||
1011 | ctry->iso[1] = ah->ah_iso[1]; | ||
1012 | ctry->iso[2] = ah->ah_iso[2]; | ||
1013 | } | 481 | } |