Merge branch 'docs-next' of git://git.lwn.net/linux-2.6
[sfrench/cifs-2.6.git] / drivers / net / wireless / ath9k / regd.c
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
24 static 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
34 static void
35 ath9k_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_array(u, t, size);
46                 }
47 }
48
49 static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah)
50 {
51         return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG;
52 }
53
54 static 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
65 static 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                  "invalid regulatory domain/country code 0x%x\n", rd);
82         return false;
83 }
84
85 static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah)
86 {
87         u32 regcap;
88
89         regcap = ah->ah_caps.reg_cap;
90
91         if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND)
92                 return true;
93         else
94                 return false;
95 }
96
97 static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah,
98                                       u16 cc)
99 {
100         u16 rd;
101         int i;
102
103         if (cc == CTRY_DEFAULT)
104                 return true;
105         if (cc == CTRY_DEBUG)
106                 return true;
107
108         rd = ath9k_regd_get_eepromRD(ah);
109         DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "EEPROM regdomain 0x%x\n", rd);
110
111         if (rd & COUNTRY_ERD_FLAG) {
112                 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
113                         "EEPROM setting is country code %u\n",
114                         rd & ~COUNTRY_ERD_FLAG);
115                 return cc == (rd & ~COUNTRY_ERD_FLAG);
116         }
117
118         for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
119                 if (cc == allCountries[i].countryCode) {
120 #ifdef AH_SUPPORT_11D
121                         if ((rd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)
122                                 return true;
123 #endif
124                         if (allCountries[i].regDmnEnum == rd ||
125                             rd == DEBUG_REG_DMN || rd == NO_ENUMRD)
126                                 return true;
127                 }
128         }
129         return false;
130 }
131
132 static void
133 ath9k_regd_get_wmodes_nreg(struct ath_hal *ah,
134                            struct country_code_to_enum_rd *country,
135                            struct regDomain *rd5GHz,
136                            unsigned long *modes_allowed)
137 {
138         bitmap_copy(modes_allowed, ah->ah_caps.wireless_modes, ATH9K_MODE_MAX);
139
140         if (test_bit(ATH9K_MODE_11G, ah->ah_caps.wireless_modes) &&
141             (!country->allow11g))
142                 clear_bit(ATH9K_MODE_11G, modes_allowed);
143
144         if (test_bit(ATH9K_MODE_11A, ah->ah_caps.wireless_modes) &&
145             (ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a)))
146                 clear_bit(ATH9K_MODE_11A, modes_allowed);
147
148         if (test_bit(ATH9K_MODE_11NG_HT20, ah->ah_caps.wireless_modes)
149             && (!country->allow11ng20))
150                 clear_bit(ATH9K_MODE_11NG_HT20, modes_allowed);
151
152         if (test_bit(ATH9K_MODE_11NA_HT20, ah->ah_caps.wireless_modes)
153             && (!country->allow11na20))
154                 clear_bit(ATH9K_MODE_11NA_HT20, modes_allowed);
155
156         if (test_bit(ATH9K_MODE_11NG_HT40PLUS, ah->ah_caps.wireless_modes) &&
157             (!country->allow11ng40))
158                 clear_bit(ATH9K_MODE_11NG_HT40PLUS, modes_allowed);
159
160         if (test_bit(ATH9K_MODE_11NG_HT40MINUS, ah->ah_caps.wireless_modes) &&
161             (!country->allow11ng40))
162                 clear_bit(ATH9K_MODE_11NG_HT40MINUS, modes_allowed);
163
164         if (test_bit(ATH9K_MODE_11NA_HT40PLUS, ah->ah_caps.wireless_modes) &&
165             (!country->allow11na40))
166                 clear_bit(ATH9K_MODE_11NA_HT40PLUS, modes_allowed);
167
168         if (test_bit(ATH9K_MODE_11NA_HT40MINUS, ah->ah_caps.wireless_modes) &&
169             (!country->allow11na40))
170                 clear_bit(ATH9K_MODE_11NA_HT40MINUS, modes_allowed);
171 }
172
173 bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah)
174 {
175         u16 rd;
176
177         rd = ath9k_regd_get_eepromRD(ah);
178
179         switch (rd) {
180         case FCC4_FCCA:
181         case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG):
182                 return true;
183         case DEBUG_REG_DMN:
184         case NO_ENUMRD:
185                 if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49)
186                         return true;
187                 break;
188         }
189         return false;
190 }
191
192 static struct country_code_to_enum_rd*
193 ath9k_regd_find_country(u16 countryCode)
194 {
195         int i;
196
197         for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
198                 if (allCountries[i].countryCode == countryCode)
199                         return &allCountries[i];
200         }
201         return NULL;
202 }
203
204 static u16 ath9k_regd_get_default_country(struct ath_hal *ah)
205 {
206         u16 rd;
207         int i;
208
209         rd = ath9k_regd_get_eepromRD(ah);
210         if (rd & COUNTRY_ERD_FLAG) {
211                 struct country_code_to_enum_rd *country = NULL;
212                 u16 cc = rd & ~COUNTRY_ERD_FLAG;
213
214                 country = ath9k_regd_find_country(cc);
215                 if (country != NULL)
216                         return cc;
217         }
218
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;
227 }
228
229 static bool ath9k_regd_is_valid_reg_domain(int regDmn,
230                                            struct regDomain *rd)
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, &regDomains[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 {
248         int i;
249
250         if (regDmnPair == NO_ENUMRD)
251                 return false;
252         for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
253                 if (regDomainPairs[i].regDmnEnum == regDmnPair)
254                         return true;
255         }
256         return false;
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 = &regDomainPairs[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 }
606
607 bool
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 {
615         u16 maxChan = 7000;
616         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;
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
637         if (!ath9k_regd_is_eeprom_valid(ah)) {
638                 DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
639                         "Invalid EEPROM contents\n");
640                 return false;
641         }
642
643         ah->ah_countryCode = ath9k_regd_get_default_country(ah);
644
645         if (ah->ah_countryCode == CTRY_DEFAULT) {
646                 ah->ah_countryCode = cc & COUNTRY_CODE_MASK;
647                 if ((ah->ah_countryCode == CTRY_DEFAULT) &&
648                     (ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) {
649                         ah->ah_countryCode = CTRY_UNITED_STATES;
650                 }
651         }
652
653 #ifdef AH_SUPPORT_11D
654         if (ah->ah_countryCode == CTRY_DEFAULT) {
655                 regdmn = ath9k_regd_get_eepromRD(ah);
656                 country = NULL;
657         } else {
658 #endif
659                 country = ath9k_regd_find_country(ah->ah_countryCode);
660                 if (country == NULL) {
661                         DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
662                                 "Country is NULL!!!!, cc= %d\n",
663                                 ah->ah_countryCode);
664                         return false;
665                 } else {
666                         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         }
702
703         if (!isWwrSKU(ah) && ((rd5GHz.regDmnEnum == FCC1) ||
704                               (rd5GHz.regDmnEnum == FCC2))) {
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 = &regDmn5GhzFreq[0];
762                         ctl = rd->conformanceTestLimit;
763                         break;
764                 case ATH9K_MODE_11B:
765                         rd = &rd2GHz;
766                         channelBM = rd->chan11b;
767                         freqs = &regDmn2GhzFreq[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 = &regDmn2Ghz11gFreq[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
858                 if (next > ARRAY_SIZE(ah->ah_channels)) {
859                         DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
860                                 "too many channels %u; truncating to %u\n",
861                                 next, (int) ARRAY_SIZE(ah->ah_channels));
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         }
881         *nchans = next;
882
883         ah->ah_countryCode = ah->ah_countryCode;
884
885         ah->ah_currentRDInUse = regdmn;
886         ah->ah_currentRD5G = rd5GHz.regDmnEnum;
887         ah->ah_currentRD2G = rd2GHz.regDmnEnum;
888         if (country == NULL) {
889                 ah->ah_iso[0] = 0;
890                 ah->ah_iso[1] = 0;
891         } else {
892                 ah->ah_iso[0] = country->isoName[0];
893                 ah->ah_iso[1] = country->isoName[1];
894         }
895
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,
909                 "channel %u/0x%x (0x%x) requested\n",
910                 c->channel, c->channelFlags, flags);
911
912         cc = ah->ah_curchan;
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
922         base = ah->ah_channels;
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 }
964
965 u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan)
966 {
967         u32 ctl = NO_CTL;
968         struct ath9k_channel *ichan;
969
970         if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) {
971                 if (IS_CHAN_B(chan))
972                         ctl = SD_NO_CTL | CTL_11B;
973                 else if (IS_CHAN_G(chan))
974                         ctl = SD_NO_CTL | CTL_11G;
975                 else
976                         ctl = SD_NO_CTL | CTL_11A;
977         } else {
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         }
992         return ctl;
993 }
994
995 void ath9k_regd_get_current_country(struct ath_hal *ah,
996                                     struct ath9k_country_entry *ctry)
997 {
998         u16 rd = ath9k_regd_get_eepromRD(ah);
999
1000         ctry->isMultidomain = false;
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 }