aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath9k/regd.c
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2008-08-04 03:16:41 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-08-07 09:49:42 -0400
commitf078f209704849c86bd43c0beccfc1f410ed1c66 (patch)
tree9b965db2c86e7369002a05808f6b418c8a9aa985 /drivers/net/wireless/ath9k/regd.c
parentb1a5215004130689aeee9e522585c879d3b71472 (diff)
ath9k: Add new Atheros IEEE 802.11n driver
This adds the new mac80211 11n ath9k Atheros driver. Only STA support is currently enabled and tested. Signed-off-by: Senthil Balasubramanian <senthilkumar@atheros.com> Signed-off-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: Jack Howarth <howarth@bromo.msbb.uc.edu> Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com> Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com> Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: Pavel Roskin <proski@gnu.org> Signed-off-by: Vasanthakumar Thiagarajan <vasanth@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.c1031
1 files changed, 1031 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c
new file mode 100644
index 000000000000..7b0176e3eb40
--- /dev/null
+++ b/drivers/net/wireless/ath9k/regd.c
@@ -0,0 +1,1031 @@
1/*
2 * Copyright (c) 2008 Atheros Communications Inc.
3 *
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.
7 *
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.
15 */
16
17#include <linux/kernel.h>
18#include <linux/slab.h>
19#include "core.h"
20#include "hw.h"
21#include "regd.h"
22#include "regd_common.h"
23
24static int ath9k_regd_chansort(const void *a, const void *b)
25{
26 const struct ath9k_channel *ca = a;
27 const struct ath9k_channel *cb = b;
28
29 return (ca->channel == cb->channel) ?
30 (ca->channelFlags & CHAN_FLAGS) -
31 (cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel;
32}
33
34static void
35ath9k_regd_sort(void *a, u32 n, u32 size, ath_hal_cmp_t *cmp)
36{
37 u8 *aa = a;
38 u8 *ai, *t;
39
40 for (ai = aa + size; --n >= 1; ai += size)
41 for (t = ai; t > aa; t -= size) {
42 u8 *u = t - size;
43 if (cmp(u, t) <= 0)
44 break;
45 swap(u, t, size);
46 }
47}
48
49static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah)
50{
51 return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG;
52}
53
54static bool ath9k_regd_is_chan_bm_zero(u64 *bitmask)
55{
56 int i;
57
58 for (i = 0; i < BMLEN; i++) {
59 if (bitmask[i] != 0)
60 return false;
61 }
62 return true;
63}
64
65static bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah)
66{
67 u16 rd = ath9k_regd_get_eepromRD(ah);
68 int i;
69
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)
74 return true;
75 } else {
76 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
77 if (regDomainPairs[i].regDmnEnum == rd)
78 return true;
79 }
80 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
81 "%s: invalid regulatory domain/country code 0x%x\n",
82 __func__, rd);
83 return false;
84}
85
86static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah)
87{
88 u32 regcap;
89
90 regcap = ah->ah_caps.halRegCap;
91
92 if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND)
93 return true;
94 else
95 return false;
96}
97
98static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah,
99 u16 cc)
100{
101 u16 rd;
102 int i;
103
104 if (cc == CTRY_DEFAULT)
105 return true;
106 if (cc == CTRY_DEBUG)
107 return true;
108
109 rd = ath9k_regd_get_eepromRD(ah);
110 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n",
111 __func__, rd);
112
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);
118 }
119
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)
124 return true;
125#endif
126 if (allCountries[i].regDmnEnum == rd ||
127 rd == DEBUG_REG_DMN || rd == NO_ENUMRD)
128 return true;
129 }
130 }
131 return false;
132}
133
134static u32
135ath9k_regd_get_wmodes_nreg(struct ath_hal *ah,
136 struct country_code_to_enum_rd *country,
137 struct regDomain *rd5GHz)
138{
139 u32 modesAvail;
140
141 modesAvail = ah->ah_caps.halWirelessModes;
142
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;
148
149 if ((modesAvail & ATH9K_MODE_SEL_11NG_HT20)
150 && (!country->allow11ng20))
151 modesAvail &= ~ATH9K_MODE_SEL_11NG_HT20;
152
153 if ((modesAvail & ATH9K_MODE_SEL_11NA_HT20)
154 && (!country->allow11na20))
155 modesAvail &= ~ATH9K_MODE_SEL_11NA_HT20;
156
157 if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40PLUS) &&
158 (!country->allow11ng40))
159 modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40PLUS;
160
161 if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40MINUS) &&
162 (!country->allow11ng40))
163 modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40MINUS;
164
165 if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40PLUS) &&
166 (!country->allow11na40))
167 modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40PLUS;
168
169 if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40MINUS) &&
170 (!country->allow11na40))
171 modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40MINUS;
172
173 return modesAvail;
174}
175
176bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah)
177{
178 u16 rd;
179
180 rd = ath9k_regd_get_eepromRD(ah);
181
182 switch (rd) {
183 case FCC4_FCCA:
184 case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG):
185 return true;
186 case DEBUG_REG_DMN:
187 case NO_ENUMRD:
188 if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49)
189 return true;
190 break;
191 }
192 return false;
193}
194
195static struct country_code_to_enum_rd*
196ath9k_regd_find_country(u16 countryCode)
197{
198 int i;
199
200 for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
201 if (allCountries[i].countryCode == countryCode)
202 return &allCountries[i];
203 }
204 return NULL;
205}
206
207static u16 ath9k_regd_get_default_country(struct ath_hal *ah)
208{
209 u16 rd;
210 int i;
211
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;
216
217 country = ath9k_regd_find_country(cc);
218 if (country != NULL)
219 return cc;
220 }
221
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;
226 else
227 i = ARRAY_SIZE(regDomainPairs);
228 }
229 return CTRY_DEFAULT;
230}
231
232static bool ath9k_regd_is_valid_reg_domain(int regDmn,
233 struct regDomain *rd)
234{
235 int i;
236
237 for (i = 0; i < ARRAY_SIZE(regDomains); i++) {
238 if (regDomains[i].regDmnEnum == regDmn) {
239 if (rd != NULL) {
240 memcpy(rd, &regDomains[i],
241 sizeof(struct regDomain));
242 }
243 return true;
244 }
245 }
246 return false;
247}
248
249static bool ath9k_regd_is_valid_reg_domainPair(int regDmnPair)
250{
251 int i;
252
253 if (regDmnPair == NO_ENUMRD)
254 return false;
255 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
256 if (regDomainPairs[i].regDmnEnum == regDmnPair)
257 return true;
258 }
259 return false;
260}
261
262static bool
263ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn,
264 u16 channelFlag, struct regDomain *rd)
265{
266 int i, found;
267 u64 flags = NO_REQ;
268 struct reg_dmn_pair_mapping *regPair = NULL;
269 int regOrg;
270
271 regOrg = regDmn;
272 if (regDmn == CTRY_DEFAULT) {
273 u16 rdnum;
274 rdnum = ath9k_regd_get_eepromRD(ah);
275
276 if (!(rdnum & COUNTRY_ERD_FLAG)) {
277 if (ath9k_regd_is_valid_reg_domain(rdnum, NULL) ||
278 ath9k_regd_is_valid_reg_domainPair(rdnum)) {
279 regDmn = rdnum;
280 }
281 }
282 }
283
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 = &regDomainPairs[i];
289 found = 1;
290 }
291 }
292 if (!found) {
293 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
294 "%s: Failed to find reg domain pair %u\n",
295 __func__, regDmn);
296 return false;
297 }
298 if (!(channelFlag & CHANNEL_2GHZ)) {
299 regDmn = regPair->regDmn5GHz;
300 flags = regPair->flags5GHz;
301 }
302 if (channelFlag & CHANNEL_2GHZ) {
303 regDmn = regPair->regDmn2GHz;
304 flags = regPair->flags2GHz;
305 }
306 }
307
308 found = ath9k_regd_is_valid_reg_domain(regDmn, rd);
309 if (!found) {
310 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
311 "%s: Failed to find unitary reg domain %u\n",
312 __func__, regDmn);
313 return false;
314 } else {
315 rd->pscan &= regPair->pscanMask;
316 if (((regOrg & MULTI_DOMAIN_MASK) == 0) &&
317 (flags != NO_REQ)) {
318 rd->flags = flags;
319 }
320
321 rd->flags &= (channelFlag & CHANNEL_2GHZ) ?
322 REG_DOMAIN_2GHZ_MASK : REG_DOMAIN_5GHZ_MASK;
323 return true;
324 }
325}
326
327static bool ath9k_regd_is_bit_set(int bit, u64 *bitmask)
328{
329 int byteOffset, bitnum;
330 u64 val;
331
332 byteOffset = bit / 64;
333 bitnum = bit - byteOffset * 64;
334 val = ((u64) 1) << bitnum;
335 if (bitmask[byteOffset] & val)
336 return true;
337 else
338 return false;
339}
340
341static void
342ath9k_regd_add_reg_classid(u8 *regclassids, u32 maxregids,
343 u32 *nregids, u8 regclassid)
344{
345 int i;
346
347 if (regclassid == 0)
348 return;
349
350 for (i = 0; i < maxregids; i++) {
351 if (regclassids[i] == regclassid)
352 return;
353 if (regclassids[i] == 0)
354 break;
355 }
356
357 if (i == maxregids)
358 return;
359 else {
360 regclassids[i] = regclassid;
361 *nregids += 1;
362 }
363
364 return;
365}
366
367static bool
368ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal *ah,
369 enum reg_ext_bitmap bit)
370{
371 return (ah->ah_currentRDExt & (1 << bit)) ? true : false;
372}
373
374#ifdef ATH_NF_PER_CHAN
375
376static void ath9k_regd_init_rf_buffer(struct ath9k_channel *ichans,
377 int nchans)
378{
379 int i, j, next;
380
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;
391 }
392 }
393 }
394}
395#endif
396
397static int ath9k_regd_is_chan_present(struct ath_hal *ah,
398 u16 c)
399{
400 int i;
401
402 for (i = 0; i < 150; i++) {
403 if (!ah->ah_channels[i].channel)
404 return -1;
405 else if (ah->ah_channels[i].channel == c)
406 return i;
407 }
408
409 return -1;
410}
411
412static bool
413ath9k_regd_add_channel(struct ath_hal *ah,
414 u16 c,
415 u16 c_lo,
416 u16 c_hi,
417 u16 maxChan,
418 u8 ctl,
419 int pos,
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)
426{
427 struct ath9k_channel *chan;
428 int ret;
429 u32 channelFlags = 0;
430 u8 privFlags = 0;
431
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);
436 return false;
437 }
438 if ((fband->channelBW == CHANNEL_HALF_BW) &&
439 !ah->ah_caps.halChanHalfRate) {
440 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
441 "%s: Skipping %u half rate channel\n",
442 __func__, c);
443 return false;
444 }
445
446 if ((fband->channelBW == CHANNEL_QUARTER_BW) &&
447 !ah->ah_caps.halChanQuarterRate) {
448 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
449 "%s: Skipping %u quarter rate channel\n",
450 __func__, c);
451 return false;
452 }
453
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);
458 return false;
459 }
460
461 if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) {
462 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
463 "Skipping ecm channel\n");
464 return false;
465 }
466
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");
470 return false;
471 }
472
473 if (IS_HT40_MODE(cm->mode) &&
474 !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_FCC_DFS_HT40)) &&
475 (fband->useDfs) &&
476 (rd->conformanceTestLimit != MKK)) {
477 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
478 "Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n");
479 return false;
480 }
481
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");
488 return false;
489 }
490
491 if (IS_HT40_MODE(cm->mode) &&
492 !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_JAPAN_DFS_HT40)) &&
493 (fband->useDfs) &&
494 (rd->conformanceTestLimit == MKK)) {
495 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
496 "Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n");
497 return false;
498 }
499
500 /* Calculate channel flags */
501
502 channelFlags = cm->flags;
503
504 switch (fband->channelBW) {
505 case CHANNEL_HALF_BW:
506 channelFlags |= CHANNEL_HALF;
507 break;
508 case CHANNEL_QUARTER_BW:
509 channelFlags |= CHANNEL_QUARTER;
510 break;
511 }
512
513 if (fband->usePassScan & rd->pscan)
514 channelFlags |= CHANNEL_PASSIVE;
515 else
516 channelFlags &= ~CHANNEL_PASSIVE;
517 if (fband->useDfs & rd->dfsMask)
518 privFlags = CHANNEL_DFS;
519 else
520 privFlags = 0;
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;
527
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.halRegCap;
533 if (!(regcap &
534 (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN |
535 AR_EEPROM_EEREGCAP_EN_KK_U2 |
536 AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) &&
537 isUNII1OddChan(c)) {
538 channelFlags &= ~CHANNEL_PASSIVE;
539 } else {
540 privFlags |= CHANNEL_DISALLOW_ADHOC;
541 }
542 } else {
543 privFlags |= CHANNEL_DISALLOW_ADHOC;
544 }
545 }
546 }
547
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;
554 }
555
556 /* Fill in channel details */
557
558 ret = ath9k_regd_is_chan_present(ah, c);
559 if (ret == -1) {
560 chan = &ah->ah_channels[pos];
561 chan->channel = c;
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;
569 } else {
570 chan = &ah->ah_channels[ret];
571 chan->channelFlags |= channelFlags;
572 chan->privFlags |= privFlags;
573 }
574
575 /* Set CTLs */
576
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;
583
584 return (ret == -1) ? true : false;
585}
586
587static bool ath9k_regd_japan_check(struct ath_hal *ah,
588 int b,
589 struct regDomain *rd5GHz)
590{
591 bool skipband = false;
592 int i;
593 u32 regcap;
594
595 for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) {
596 if (j_bandcheck[i].freqbandbit == b) {
597 regcap = ah->ah_caps.halRegCap;
598 if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) {
599 skipband = true;
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;
604 }
605 break;
606 }
607 }
608
609 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
610 "%s: Skipping %d freq band\n",
611 __func__, j_bandcheck[i].freqbandbit);
612
613 return skipband;
614}
615
616bool
617ath9k_regd_init_channels(struct ath_hal *ah,
618 u32 maxchans,
619 u32 *nchans, u8 *regclassids,
620 u32 maxregids, u32 *nregids, u16 cc,
621 u32 modeSelect, bool enableOutdoor,
622 bool enableExtendedChannels)
623{
624 u32 modesAvail;
625 u16 maxChan = 7000;
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];
630 int next = 0, b;
631 u8 ctl;
632 int regdmn;
633 u16 chanSep;
634
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" : "");
639
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);
643 return false;
644 }
645
646 if (!ath9k_regd_is_eeprom_valid(ah)) {
647 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
648 "%s: invalid EEPROM contents\n", __func__);
649 return false;
650 }
651
652 ah->ah_countryCode = ath9k_regd_get_default_country(ah);
653
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;
659 }
660 }
661
662#ifdef AH_SUPPORT_11D
663 if (ah->ah_countryCode == CTRY_DEFAULT) {
664 regdmn = ath9k_regd_get_eepromRD(ah);
665 country = NULL;
666 } else {
667#endif
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",
672 ah->ah_countryCode);
673 return false;
674 } else {
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))
682 regdmn = FCC3_FCCA;
683 else
684 regdmn = FCC1_FCCA;
685 }
686#endif
687 }
688#ifdef AH_SUPPORT_11D
689 }
690#endif
691 if (!ath9k_regd_get_wmode_regdomain(ah,
692 regdmn,
693 ~CHANNEL_2GHZ,
694 &rd5GHz)) {
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);
699 return false;
700 }
701 if (!ath9k_regd_get_wmode_regdomain(ah,
702 regdmn,
703 CHANNEL_2GHZ,
704 &rd2GHz)) {
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);
709 return false;
710 }
711
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,
716 FCC3_FCCA,
717 ~CHANNEL_2GHZ,
718 &rd5GHz)) {
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);
723 return false;
724 }
725 }
726 }
727
728 if (country == NULL) {
729 modesAvail = ah->ah_caps.halWirelessModes;
730 } else {
731 modesAvail = ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz);
732 if (!enableOutdoor)
733 maxChan = country->outdoorChanStart;
734 }
735
736 next = 0;
737
738 if (maxchans > ARRAY_SIZE(ah->ah_channels))
739 maxchans = ARRAY_SIZE(ah->ah_channels);
740
741 for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) {
742 u16 c, c_hi, c_lo;
743 u64 *channelBM = NULL;
744 struct regDomain *rd = NULL;
745 struct RegDmnFreqBand *fband = NULL, *freqs;
746 int8_t low_adj = 0, hi_adj = 0;
747
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);
752 continue;
753 }
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,
758 cm->flags);
759 continue;
760 }
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 "
764 "by hardware\n",
765 __func__, cm->flags);
766 continue;
767 }
768
769 switch (cm->mode) {
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:
774 rd = &rd5GHz;
775 channelBM = rd->chan11a;
776 freqs = &regDmn5GhzFreq[0];
777 ctl = rd->conformanceTestLimit;
778 break;
779 case ATH9K_MODE_SEL_11B:
780 rd = &rd2GHz;
781 channelBM = rd->chan11b;
782 freqs = &regDmn2GhzFreq[0];
783 ctl = rd->conformanceTestLimit | CTL_11B;
784 break;
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:
789 rd = &rd2GHz;
790 channelBM = rd->chan11g;
791 freqs = &regDmn2Ghz11gFreq[0];
792 ctl = rd->conformanceTestLimit | CTL_11G;
793 break;
794 default:
795 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
796 "%s: Unknown HAL mode 0x%x\n", __func__,
797 cm->mode);
798 continue;
799 }
800
801 if (ath9k_regd_is_chan_bm_zero(channelBM))
802 continue;
803
804 if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40PLUS) ||
805 (cm->mode == ATH9K_MODE_SEL_11NG_HT40PLUS)) {
806 hi_adj = -20;
807 }
808
809 if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40MINUS) ||
810 (cm->mode == ATH9K_MODE_SEL_11NG_HT40MINUS)) {
811 low_adj = 20;
812 }
813
814 /* XXX: Add a helper here instead */
815 for (b = 0; b < 64 * BMLEN; b++) {
816 if (ath9k_regd_is_bit_set(b, channelBM)) {
817 fband = &freqs[b];
818 if (rd5GHz.regDmnEnum == MKK1
819 || rd5GHz.regDmnEnum == MKK2) {
820 if (ath9k_regd_japan_check(ah,
821 b,
822 &rd5GHz))
823 continue;
824 }
825
826 ath9k_regd_add_reg_classid(regclassids,
827 maxregids,
828 nregids,
829 fband->
830 regClassId);
831
832 if (IS_HT40_MODE(cm->mode) && (rd == &rd5GHz)) {
833 chanSep = 40;
834 if (fband->lowChannel == 5280)
835 low_adj += 20;
836
837 if (fband->lowChannel == 5170)
838 continue;
839 } else
840 chanSep = fband->channelSep;
841
842 for (c = fband->lowChannel + low_adj;
843 ((c <= (fband->highChannel + hi_adj)) &&
844 (c >= (fband->lowChannel + low_adj)));
845 c += chanSep) {
846 if (next >= maxchans) {
847 DPRINTF(ah->ah_sc,
848 ATH_DBG_REGULATORY,
849 "%s: too many channels "
850 "for channel table\n",
851 __func__);
852 goto done;
853 }
854 if (ath9k_regd_add_channel(ah,
855 c, c_lo, c_hi,
856 maxChan, ctl,
857 next,
858 rd5GHz,
859 fband, rd, cm,
860 ichans,
861 enableExtendedChannels))
862 next++;
863 }
864 if (IS_HT40_MODE(cm->mode) &&
865 (fband->lowChannel == 5280)) {
866 low_adj -= 20;
867 }
868 }
869 }
870 }
871done:
872 if (next != 0) {
873 int i;
874
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",
878 __func__, next,
879 (int) ARRAY_SIZE(ah->ah_channels));
880 next = ARRAY_SIZE(ah->ah_channels);
881 }
882#ifdef ATH_NF_PER_CHAN
883 ath9k_regd_init_rf_buffer(ichans, next);
884#endif
885 ath9k_regd_sort(ichans, next,
886 sizeof(struct ath9k_channel),
887 ath9k_regd_chansort);
888
889 ah->ah_nchan = next;
890
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);
897 }
898 }
899 *nchans = next;
900
901 ah->ah_countryCode = ah->ah_countryCode;
902
903 ah->ah_currentRDInUse = regdmn;
904 ah->ah_currentRD5G = rd5GHz.regDmnEnum;
905 ah->ah_currentRD2G = rd2GHz.regDmnEnum;
906 if (country == NULL) {
907 ah->ah_iso[0] = 0;
908 ah->ah_iso[1] = 0;
909 } else {
910 ah->ah_iso[0] = country->isoName[0];
911 ah->ah_iso[1] = country->isoName[1];
912 }
913
914 return next != 0;
915}
916
917struct ath9k_channel*
918ath9k_regd_check_channel(struct ath_hal *ah,
919 const struct ath9k_channel *c)
920{
921 struct ath9k_channel *base, *cc;
922
923 int flags = c->channelFlags & CHAN_FLAGS;
924 int n, lim;
925
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);
929
930 cc = ah->ah_curchan;
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))
935 return NULL;
936 else
937 return cc;
938 }
939
940 base = ah->ah_channels;
941 n = ah->ah_nchan;
942
943 for (lim = n; lim != 0; lim >>= 1) {
944 int d;
945 cc = &base[lim >> 1];
946 d = c->channel - cc->channel;
947 if (d == 0) {
948 if ((cc->channelFlags & CHAN_FLAGS) == flags) {
949 if ((cc->privFlags & CHANNEL_INTERFERENCE) &&
950 (cc->privFlags & CHANNEL_DFS))
951 return NULL;
952 else
953 return cc;
954 }
955 d = flags - (cc->channelFlags & CHAN_FLAGS);
956 }
957 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
958 "%s: channel %u/0x%x d %d\n", __func__,
959 cc->channel, cc->channelFlags, d);
960 if (d > 0) {
961 base = cc + 1;
962 lim--;
963 }
964 }
965 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: no match for %u/0x%x\n",
966 __func__, c->channel, c->channelFlags);
967 return NULL;
968}
969
970u32
971ath9k_regd_get_antenna_allowed(struct ath_hal *ah,
972 struct ath9k_channel *chan)
973{
974 struct ath9k_channel *ichan = NULL;
975
976 ichan = ath9k_regd_check_channel(ah, chan);
977 if (!ichan)
978 return 0;
979
980 return ichan->antennaMax;
981}
982
983u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan)
984{
985 u32 ctl = NO_CTL;
986 struct ath9k_channel *ichan;
987
988 if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) {
989 if (IS_CHAN_B(chan))
990 ctl = SD_NO_CTL | CTL_11B;
991 else if (IS_CHAN_G(chan))
992 ctl = SD_NO_CTL | CTL_11G;
993 else
994 ctl = SD_NO_CTL | CTL_11A;
995 } else {
996 ichan = ath9k_regd_check_channel(ah, chan);
997 if (ichan != NULL) {
998 /* FIXME */
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];
1005
1006 if (IS_CHAN_G(chan) && (ctl & 0xf) == CTL_11B)
1007 ctl = (ctl & ~0xf) | CTL_11G;
1008 }
1009 }
1010 return ctl;
1011}
1012
1013void ath9k_regd_get_current_country(struct ath_hal *ah,
1014 struct ath9k_country_entry *ctry)
1015{
1016 u16 rd = ath9k_regd_get_eepromRD(ah);
1017
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);
1023
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];
1031}