519cae8775ba0d7577460af42215de36a65205b7
[openwrt/svn-archive/archive.git] / package / ath9k / src / 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 "ath9k.h"
20 #include "regd.h"
21 #include "regd_common.h"
22
23 static int ath9k_regd_chansort(const void *a, const void *b)
24 {
25 const struct hal_channel_internal *ca = a;
26 const struct hal_channel_internal *cb = b;
27
28 return (ca->channel == cb->channel) ?
29 (ca->channelFlags & CHAN_FLAGS) -
30 (cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel;
31 }
32
33 static void
34 ath9k_regd_sort(void *a, u_int32_t n, u_int32_t size, ath_hal_cmp_t *cmp)
35 {
36 u_int8_t *aa = a;
37 u_int8_t *ai, *t;
38
39 for (ai = aa + size; --n >= 1; ai += size)
40 for (t = ai; t > aa; t -= size) {
41 u_int8_t *u = t - size;
42 if (cmp(u, t) <= 0)
43 break;
44 swap(u, t, size);
45 }
46 }
47
48 static u_int16_t ath9k_regd_get_eepromRD(struct ath_hal *ah)
49 {
50 return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG;
51 }
52
53 static enum hal_bool ath9k_regd_is_chan_bm_zero(u_int64_t *bitmask)
54 {
55 int i;
56
57 for (i = 0; i < BMLEN; i++) {
58 if (bitmask[i] != 0)
59 return AH_FALSE;
60 }
61 return AH_TRUE;
62 }
63
64 static enum hal_bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah)
65 {
66 u_int16_t rd = ath9k_regd_get_eepromRD(ah);
67 int i;
68
69 if (rd & COUNTRY_ERD_FLAG) {
70 u_int16_t cc = rd & ~COUNTRY_ERD_FLAG;
71 for (i = 0; i < ARRAY_SIZE(allCountries); i++)
72 if (allCountries[i].countryCode == cc)
73 return AH_TRUE;
74 } else {
75 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
76 if (regDomainPairs[i].regDmnEnum == rd)
77 return AH_TRUE;
78 }
79 HDPRINTF(ah, HAL_DBG_REGULATORY,
80 "%s: invalid regulatory domain/country code 0x%x\n",
81 __func__, rd);
82 return AH_FALSE;
83 }
84
85 static enum hal_bool ath9k_regd_is_fcc_midband_supported(struct ath_hal
86 *ah)
87 {
88 u_int32_t regcap;
89
90 regcap = ah->ah_caps.halRegCap;
91
92 if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND)
93 return AH_TRUE;
94 else
95 return AH_FALSE;
96 }
97
98 static enum hal_bool ath9k_regd_is_ccode_valid(struct ath_hal *ah,
99 u_int16_t cc)
100 {
101 u_int16_t rd;
102 int i;
103
104 if (cc == CTRY_DEFAULT)
105 return AH_TRUE;
106 #ifdef AH_DEBUG_COUNTRY
107 if (cc == CTRY_DEBUG)
108 return AH_TRUE;
109 #endif
110 rd = ath9k_regd_get_eepromRD(ah);
111 HDPRINTF(ah, HAL_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n",
112 __func__, rd);
113
114 if (rd & COUNTRY_ERD_FLAG) {
115
116 HDPRINTF(ah, HAL_DBG_REGULATORY,
117 "%s: EEPROM setting is country code %u\n",
118 __func__, rd & ~COUNTRY_ERD_FLAG);
119 return cc == (rd & ~COUNTRY_ERD_FLAG);
120 }
121
122 for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
123 if (cc == allCountries[i].countryCode) {
124 #ifdef AH_SUPPORT_11D
125 if ((rd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)
126 return AH_TRUE;
127 #endif
128 if (allCountries[i].regDmnEnum == rd ||
129 rd == DEBUG_REG_DMN || rd == NO_ENUMRD)
130 return AH_TRUE;
131 }
132 }
133 return AH_FALSE;
134 }
135
136 static u_int
137 ath9k_regd_get_wmodes_nreg(struct ath_hal *ah,
138 struct country_code_to_enum_rd *country,
139 struct regDomain *rd5GHz)
140 {
141 u_int modesAvail;
142
143 modesAvail = ah->ah_caps.halWirelessModes;
144
145 if ((modesAvail & ATH9K_MODE_SEL_11G) && (!country->allow11g))
146 modesAvail &= ~ATH9K_MODE_SEL_11G;
147 if ((modesAvail & ATH9K_MODE_SEL_11A) &&
148 (ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a)))
149 modesAvail &= ~ATH9K_MODE_SEL_11A;
150
151 if ((modesAvail & ATH9K_MODE_SEL_11NG_HT20)
152 && (!country->allow11ng20))
153 modesAvail &= ~ATH9K_MODE_SEL_11NG_HT20;
154
155 if ((modesAvail & ATH9K_MODE_SEL_11NA_HT20)
156 && (!country->allow11na20))
157 modesAvail &= ~ATH9K_MODE_SEL_11NA_HT20;
158
159 if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40PLUS) &&
160 (!country->allow11ng40))
161 modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40PLUS;
162
163 if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40MINUS) &&
164 (!country->allow11ng40))
165 modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40MINUS;
166
167 if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40PLUS) &&
168 (!country->allow11na40))
169 modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40PLUS;
170
171 if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40MINUS) &&
172 (!country->allow11na40))
173 modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40MINUS;
174
175 return modesAvail;
176 }
177
178 enum hal_bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah)
179 {
180 u_int16_t rd;
181
182 rd = ath9k_regd_get_eepromRD(ah);
183
184 switch (rd) {
185 case FCC4_FCCA:
186 case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG):
187 return AH_TRUE;
188 case DEBUG_REG_DMN:
189 case NO_ENUMRD:
190 if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49)
191 return AH_TRUE;
192 break;
193 }
194 return AH_FALSE;
195 }
196
197 static struct country_code_to_enum_rd *ath9k_regd_find_country(u_int16_t
198 countryCode)
199 {
200 int i;
201
202 for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
203 if (allCountries[i].countryCode == countryCode)
204 return &allCountries[i];
205 }
206 return NULL;
207 }
208
209 static u_int16_t ath9k_regd_get_default_country(struct ath_hal *ah)
210 {
211 u_int16_t rd;
212 int i;
213
214 rd = ath9k_regd_get_eepromRD(ah);
215 if (rd & COUNTRY_ERD_FLAG) {
216 struct country_code_to_enum_rd *country = NULL;
217 u_int16_t cc = rd & ~COUNTRY_ERD_FLAG;
218
219 country = ath9k_regd_find_country(cc);
220 if (country != NULL)
221 return cc;
222 }
223
224 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
225 if (regDomainPairs[i].regDmnEnum == rd) {
226 if (regDomainPairs[i].singleCC != 0)
227 return regDomainPairs[i].singleCC;
228 else
229 i = ARRAY_SIZE(regDomainPairs);
230 }
231 return CTRY_DEFAULT;
232 }
233
234 static enum hal_bool ath9k_regd_is_valid_reg_domain(int regDmn,
235 struct regDomain *rd)
236 {
237 int i;
238
239 for (i = 0; i < ARRAY_SIZE(regDomains); i++) {
240 if (regDomains[i].regDmnEnum == regDmn) {
241 if (rd != NULL) {
242 memcpy(rd, &regDomains[i],
243 sizeof(struct regDomain));
244 }
245 return AH_TRUE;
246 }
247 }
248 return AH_FALSE;
249 }
250
251 static enum hal_bool ath9k_regd_is_valid_reg_domainPair(int regDmnPair)
252 {
253 int i;
254
255 if (regDmnPair == NO_ENUMRD)
256 return AH_FALSE;
257 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
258 if (regDomainPairs[i].regDmnEnum == regDmnPair)
259 return AH_TRUE;
260 }
261 return AH_FALSE;
262 }
263
264 static enum hal_bool
265 ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn,
266 u_int16_t channelFlag, struct regDomain *rd)
267 {
268 int i, found;
269 u_int64_t flags = NO_REQ;
270 struct reg_dmn_pair_mapping *regPair = NULL;
271 int regOrg;
272
273 regOrg = regDmn;
274 if (regDmn == CTRY_DEFAULT) {
275 u_int16_t rdnum;
276 rdnum = ath9k_regd_get_eepromRD(ah);
277
278 if (!(rdnum & COUNTRY_ERD_FLAG)) {
279 if (ath9k_regd_is_valid_reg_domain(rdnum, NULL) ||
280 ath9k_regd_is_valid_reg_domainPair(rdnum)) {
281 regDmn = rdnum;
282 }
283 }
284 }
285
286 if ((regDmn & MULTI_DOMAIN_MASK) == 0) {
287
288 for (i = 0, found = 0;
289 (i < ARRAY_SIZE(regDomainPairs)) && (!found); i++) {
290 if (regDomainPairs[i].regDmnEnum == regDmn) {
291 regPair = &regDomainPairs[i];
292 found = 1;
293 }
294 }
295 if (!found) {
296 HDPRINTF(ah, HAL_DBG_REGULATORY,
297 "%s: Failed to find reg domain pair %u\n",
298 __func__, regDmn);
299 return AH_FALSE;
300 }
301 if (!(channelFlag & CHANNEL_2GHZ)) {
302 regDmn = regPair->regDmn5GHz;
303 flags = regPair->flags5GHz;
304 }
305 if (channelFlag & CHANNEL_2GHZ) {
306 regDmn = regPair->regDmn2GHz;
307 flags = regPair->flags2GHz;
308 }
309 }
310
311 found = ath9k_regd_is_valid_reg_domain(regDmn, rd);
312 if (!found) {
313 HDPRINTF(ah, HAL_DBG_REGULATORY,
314 "%s: Failed to find unitary reg domain %u\n",
315 __func__, regDmn);
316 return AH_FALSE;
317 } else {
318 rd->pscan &= regPair->pscanMask;
319 if (((regOrg & MULTI_DOMAIN_MASK) == 0) &&
320 (flags != NO_REQ)) {
321 rd->flags = flags;
322 }
323
324 rd->flags &= (channelFlag & CHANNEL_2GHZ) ?
325 REG_DOMAIN_2GHZ_MASK : REG_DOMAIN_5GHZ_MASK;
326 return AH_TRUE;
327 }
328 }
329
330 static enum hal_bool ath9k_regd_is_bit_set(int bit, u_int64_t *bitmask)
331 {
332 int byteOffset, bitnum;
333 u_int64_t val;
334
335 byteOffset = bit / 64;
336 bitnum = bit - byteOffset * 64;
337 val = ((u_int64_t) 1) << bitnum;
338 if (bitmask[byteOffset] & val)
339 return AH_TRUE;
340 else
341 return AH_FALSE;
342 }
343
344 static void
345 ath9k_regd_add_reg_classid(u_int8_t *regclassids, u_int maxregids,
346 u_int *nregids, u_int8_t regclassid)
347 {
348 int i;
349
350 if (regclassid == 0)
351 return;
352
353 for (i = 0; i < maxregids; i++) {
354 if (regclassids[i] == regclassid)
355 return;
356 if (regclassids[i] == 0)
357 break;
358 }
359
360 if (i == maxregids)
361 return;
362 else {
363 regclassids[i] = regclassid;
364 *nregids += 1;
365 }
366
367 return;
368 }
369
370 static enum hal_bool
371 ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal *ah,
372 enum reg_ext_bitmap bit)
373 {
374 return (ah->ah_currentRDExt & (1 << bit)) ? AH_TRUE : AH_FALSE;
375 }
376
377 #ifdef ATH_NF_PER_CHAN
378
379 static void ath9k_regd_init_rf_buffer(struct hal_channel_internal *ichans,
380 int nchans)
381 {
382 int i, j, next;
383
384 for (next = 0; next < nchans; next++) {
385 for (i = 0; i < NUM_NF_READINGS; i++) {
386 ichans[next].nfCalHist[i].currIndex = 0;
387 ichans[next].nfCalHist[i].privNF =
388 AR_PHY_CCA_MAX_GOOD_VALUE;
389 ichans[next].nfCalHist[i].invalidNFcount =
390 AR_PHY_CCA_FILTERWINDOW_LENGTH;
391 for (j = 0; j < HAL_NF_CAL_HIST_MAX; j++) {
392 ichans[next].nfCalHist[i].nfCalBuffer[j] =
393 AR_PHY_CCA_MAX_GOOD_VALUE;
394 }
395 }
396 }
397 }
398 #endif
399
400 enum hal_bool
401 ath9k_regd_init_channels(struct ath_hal *ah,
402 struct hal_channel *chans, u_int maxchans,
403 u_int *nchans, u_int8_t *regclassids,
404 u_int maxregids, u_int *nregids, u_int16_t cc,
405 u_int32_t modeSelect, enum hal_bool enableOutdoor,
406 enum hal_bool enableExtendedChannels)
407 {
408 u_int modesAvail;
409 u_int16_t maxChan = 7000;
410 struct country_code_to_enum_rd *country = NULL;
411 struct regDomain rd5GHz, rd2GHz;
412 const struct cmode *cm;
413 struct hal_channel_internal *ichans = &ah->ah_channels[0];
414 int next = 0, b;
415 u_int8_t ctl;
416 int is_quarterchan_cap, is_halfchan_cap;
417 int regdmn;
418 u_int16_t chanSep;
419
420 HDPRINTF(ah, HAL_DBG_REGULATORY, "%s: cc %u mode 0x%x%s%s\n",
421 __func__, cc, modeSelect,
422 enableOutdoor ? " Enable outdoor" : " ",
423 enableExtendedChannels ? " Enable ecm" : "");
424
425 if (!ath9k_regd_is_ccode_valid(ah, cc)) {
426
427 HDPRINTF(ah, HAL_DBG_REGULATORY,
428 "%s: invalid country code %d\n", __func__, cc);
429 return AH_FALSE;
430 }
431
432 if (!ath9k_regd_is_eeprom_valid(ah)) {
433
434 HDPRINTF(ah, HAL_DBG_REGULATORY,
435 "%s: invalid EEPROM contents\n", __func__);
436 return AH_FALSE;
437 }
438
439 ah->ah_countryCode = ath9k_regd_get_default_country(ah);
440
441 if (ah->ah_countryCode == CTRY_DEFAULT) {
442
443 ah->ah_countryCode = cc & COUNTRY_CODE_MASK;
444
445 if ((ah->ah_countryCode == CTRY_DEFAULT) &&
446 (ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) {
447
448 ah->ah_countryCode = CTRY_UNITED_STATES;
449 }
450 }
451 #ifdef AH_SUPPORT_11D
452 if (ah->ah_countryCode == CTRY_DEFAULT) {
453 regdmn = ath9k_regd_get_eepromRD(ah);
454 country = NULL;
455 } else {
456 #endif
457 country = ath9k_regd_find_country(ah->ah_countryCode);
458
459 if (country == NULL) {
460 HDPRINTF(ah, HAL_DBG_REGULATORY,
461 "Country is NULL!!!!, cc= %d\n",
462 ah->ah_countryCode);
463 return AH_FALSE;
464 } else {
465 regdmn = country->regDmnEnum;
466 #ifdef AH_SUPPORT_11D
467 if (((ath9k_regd_get_eepromRD(ah) & WORLD_SKU_MASK)
468 == WORLD_SKU_PREFIX)
469 && (cc == CTRY_UNITED_STATES)) {
470 if (!isWwrSKU_NoMidband(ah)
471 &&
472 ath9k_regd_is_fcc_midband_supported
473 (ah))
474 regdmn = FCC3_FCCA;
475 else
476 regdmn = FCC1_FCCA;
477 }
478 #endif
479 }
480 #ifdef AH_SUPPORT_11D
481 }
482 #endif
483
484 if (!ath9k_regd_get_wmode_regdomain
485 (ah, regdmn, ~CHANNEL_2GHZ, &rd5GHz)) {
486 HDPRINTF(ah, HAL_DBG_REGULATORY,
487 "%s: couldn't find unitary "
488 "5GHz reg domain for country %u\n",
489 __func__, ah->ah_countryCode);
490 return AH_FALSE;
491 }
492 if (!ath9k_regd_get_wmode_regdomain
493 (ah, regdmn, CHANNEL_2GHZ, &rd2GHz)) {
494 HDPRINTF(ah, HAL_DBG_REGULATORY,
495 "%s: couldn't find unitary 2GHz "
496 "reg domain for country %u\n",
497 __func__, ah->ah_countryCode);
498 return AH_FALSE;
499 }
500
501 if (!isWwrSKU(ah)
502 && ((rd5GHz.regDmnEnum == FCC1)
503 || (rd5GHz.regDmnEnum == FCC2))) {
504 if (ath9k_regd_is_fcc_midband_supported(ah)) {
505
506 if (!ath9k_regd_get_wmode_regdomain
507 (ah, FCC3_FCCA, ~CHANNEL_2GHZ, &rd5GHz)) {
508 HDPRINTF(ah, HAL_DBG_REGULATORY,
509 "%s: couldn't find unitary 5GHz "
510 "reg domain for country %u\n",
511 __func__, ah->ah_countryCode);
512 return AH_FALSE;
513 }
514 }
515 }
516
517 if (country == NULL) {
518 modesAvail = ah->ah_caps.halWirelessModes;
519 } else {
520 modesAvail =
521 ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz);
522
523 if (!enableOutdoor)
524 maxChan = country->outdoorChanStart;
525 }
526
527 next = 0;
528
529 if (maxchans > ARRAY_SIZE(ah->ah_channels))
530 maxchans = ARRAY_SIZE(ah->ah_channels);
531
532 is_halfchan_cap = ah->ah_caps.halChanHalfRate;
533 is_quarterchan_cap = ah->ah_caps.halChanQuarterRate;
534 for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) {
535 u_int16_t c, c_hi, c_lo;
536 u_int64_t *channelBM = NULL;
537 struct regDomain *rd = NULL;
538 struct RegDmnFreqBand *fband = NULL, *freqs;
539 int8_t low_adj = 0, hi_adj = 0;
540
541 if ((cm->mode & modeSelect) == 0) {
542 HDPRINTF(ah, HAL_DBG_REGULATORY,
543 "%s: skip mode 0x%x flags 0x%x\n",
544 __func__, cm->mode, cm->flags);
545 continue;
546 }
547 if ((cm->mode & modesAvail) == 0) {
548 HDPRINTF(ah, HAL_DBG_REGULATORY,
549 "%s: !avail mode 0x%x (0x%x) flags 0x%x\n",
550 __func__, modesAvail, cm->mode,
551 cm->flags);
552 continue;
553 }
554 if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) {
555
556 HDPRINTF(ah, HAL_DBG_REGULATORY,
557 "%s: channels 0x%x not supported "
558 "by hardware\n",
559 __func__, cm->flags);
560 continue;
561 }
562 switch (cm->mode) {
563 case ATH9K_MODE_SEL_11A:
564 case ATH9K_MODE_SEL_11NA_HT20:
565 case ATH9K_MODE_SEL_11NA_HT40PLUS:
566 case ATH9K_MODE_SEL_11NA_HT40MINUS:
567 rd = &rd5GHz;
568 channelBM = rd->chan11a;
569 freqs = &regDmn5GhzFreq[0];
570 ctl = rd->conformanceTestLimit;
571 break;
572 case ATH9K_MODE_SEL_11B:
573 rd = &rd2GHz;
574 channelBM = rd->chan11b;
575 freqs = &regDmn2GhzFreq[0];
576 ctl = rd->conformanceTestLimit | CTL_11B;
577 break;
578 case ATH9K_MODE_SEL_11G:
579 case ATH9K_MODE_SEL_11NG_HT20:
580 case ATH9K_MODE_SEL_11NG_HT40PLUS:
581 case ATH9K_MODE_SEL_11NG_HT40MINUS:
582 rd = &rd2GHz;
583 channelBM = rd->chan11g;
584 freqs = &regDmn2Ghz11gFreq[0];
585 ctl = rd->conformanceTestLimit | CTL_11G;
586 break;
587 default:
588 HDPRINTF(ah, HAL_DBG_REGULATORY,
589 "%s: Unkonwn HAL mode 0x%x\n", __func__,
590 cm->mode);
591 continue;
592 }
593 if (ath9k_regd_is_chan_bm_zero(channelBM))
594 continue;
595
596
597 if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40PLUS) ||
598 (cm->mode == ATH9K_MODE_SEL_11NG_HT40PLUS)) {
599 hi_adj = -20;
600 }
601
602 if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40MINUS) ||
603 (cm->mode == ATH9K_MODE_SEL_11NG_HT40MINUS)) {
604 low_adj = 20;
605 }
606
607 /* XXX: Add a helper here instead */
608 for (b = 0; b < 64 * BMLEN; b++) {
609 if (ath9k_regd_is_bit_set(b, channelBM)) {
610 fband = &freqs[b];
611
612
613 if (rd5GHz.regDmnEnum == MKK1
614 || rd5GHz.regDmnEnum == MKK2) {
615 int i, skipband = 0;
616 u_int32_t regcap;
617
618 for (i = 0;
619 i < ARRAY_SIZE(j_bandcheck);
620 i++) {
621 if (j_bandcheck[i].
622 freqbandbit == b) {
623 regcap =
624 ah->ah_caps.
625 halRegCap;
626 if ((j_bandcheck
627 [i].
628 eepromflagtocheck
629 & regcap) ==
630 0) {
631 skipband =
632 1;
633 } else
634 if ((regcap &
635 AR_EEPROM_EEREGCAP_EN_KK_U2)
636 || (regcap
637 &
638 AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) {
639
640 rd5GHz.
641 dfsMask
642 |=
643 DFS_MKK4;
644 rd5GHz.
645 pscan
646 |=
647 PSCAN_MKK3;
648 }
649 break;
650 }
651 }
652 if (skipband) {
653 HDPRINTF(ah,
654 HAL_DBG_REGULATORY,
655 "%s: Skipping %d "
656 "freq band.\n",
657 __func__,
658 j_bandcheck[i].
659 freqbandbit);
660 continue;
661 }
662 }
663
664 ath9k_regd_add_reg_classid(regclassids,
665 maxregids,
666 nregids,
667 fband->
668 regClassId);
669
670 if (IS_HT40_MODE(cm->mode)
671 && (rd == &rd5GHz)) {
672
673 chanSep = 40;
674
675
676 if (fband->lowChannel == 5280)
677 low_adj += 20;
678
679 if (fband->lowChannel == 5170)
680 continue;
681 } else
682 chanSep = fband->channelSep;
683
684 for (c = fband->lowChannel + low_adj;
685 ((c <= (fband->highChannel + hi_adj))
686 && (c >=
687 (fband->lowChannel + low_adj)));
688 c += chanSep) {
689 struct hal_channel_internal icv;
690
691 if (!(c_lo <= c && c <= c_hi)) {
692 HDPRINTF(ah,
693 HAL_DBG_REGULATORY,
694 "%s: c %u out of "
695 "range [%u..%u]\n",
696 __func__, c, c_lo,
697 c_hi);
698 continue;
699 }
700 if ((fband->channelBW ==
701 CHANNEL_HALF_BW) &&
702 !is_halfchan_cap) {
703 HDPRINTF(ah,
704 HAL_DBG_REGULATORY,
705 "%s: Skipping %u half "
706 "rate channel\n",
707 __func__, c);
708 continue;
709 }
710
711 if ((fband->channelBW ==
712 CHANNEL_QUARTER_BW) &&
713 !is_quarterchan_cap) {
714 HDPRINTF(ah,
715 HAL_DBG_REGULATORY,
716 "%s: Skipping %u "
717 "quarter rate "
718 "channel\n",
719 __func__, c);
720 continue;
721 }
722
723 if (((c + fband->channelSep) / 2) >
724 (maxChan + HALF_MAXCHANBW)) {
725 HDPRINTF(ah,
726 HAL_DBG_REGULATORY,
727 "%s: c %u > "
728 "maxChan %u\n",
729 __func__, c,
730 maxChan);
731 continue;
732 }
733 if (next >= maxchans) {
734 HDPRINTF(ah,
735 HAL_DBG_REGULATORY,
736 "%s: too many "
737 "channels for channel "
738 "table\n",
739 __func__);
740 goto done;
741 }
742 if ((fband->
743 usePassScan & IS_ECM_CHAN)
744 && !enableExtendedChannels) {
745 HDPRINTF(ah,
746 HAL_DBG_REGULATORY,
747 "Skipping ecm "
748 "channel\n");
749 continue;
750 }
751 if ((rd->flags & NO_HOSTAP) &&
752 (ah->ah_opmode ==
753 HAL_M_HOSTAP)) {
754 HDPRINTF(ah,
755 HAL_DBG_REGULATORY,
756 "Skipping HOSTAP "
757 "channel\n");
758 continue;
759 }
760 if (IS_HT40_MODE(cm->mode) &&
761 !
762 (ath9k_regd_get_eeprom_reg_ext_bits
763 (ah, REG_EXT_FCC_DFS_HT40))
764 && (fband->useDfs)
765 && (rd->conformanceTestLimit !=
766 MKK)) {
767
768 HDPRINTF(ah,
769 HAL_DBG_REGULATORY,
770 "Skipping HT40 "
771 "channel "
772 "(en_fcc_dfs_ht40 = "
773 "0)\n");
774 continue;
775 }
776 if (IS_HT40_MODE(cm->mode) &&
777 !
778 (ath9k_regd_get_eeprom_reg_ext_bits
779 (ah,
780 REG_EXT_JAPAN_NONDFS_HT40))
781 && !(fband->useDfs)
782 && (rd->conformanceTestLimit ==
783 MKK)) {
784 HDPRINTF(ah,
785 HAL_DBG_REGULATORY,
786 "Skipping HT40 "
787 "channel (en_jap_ht40 "
788 "= 0)\n");
789 continue;
790 }
791 if (IS_HT40_MODE(cm->mode) &&
792 !
793 (ath9k_regd_get_eeprom_reg_ext_bits
794 (ah, REG_EXT_JAPAN_DFS_HT40))
795 && (fband->useDfs)
796 && (rd->conformanceTestLimit ==
797 MKK)) {
798 HDPRINTF(ah,
799 HAL_DBG_REGULATORY,
800 "Skipping HT40 channel"
801 " (en_jap_dfs_ht40 = "
802 "0)\n");
803 continue;
804 }
805 memset(&icv, 0, sizeof(icv));
806 icv.channel = c;
807 icv.channelFlags = cm->flags;
808
809 switch (fband->channelBW) {
810 case CHANNEL_HALF_BW:
811 icv.channelFlags |=
812 CHANNEL_HALF;
813 break;
814 case CHANNEL_QUARTER_BW:
815 icv.channelFlags |=
816 CHANNEL_QUARTER;
817 break;
818 }
819
820 icv.maxRegTxPower =
821 fband->powerDfs;
822 icv.antennaMax = fband->antennaMax;
823 icv.regDmnFlags = rd->flags;
824 icv.conformanceTestLimit = ctl;
825 if (fband->usePassScan & rd->pscan)
826 icv.channelFlags |=
827 CHANNEL_PASSIVE;
828 else
829 icv.channelFlags &=
830 ~CHANNEL_PASSIVE;
831 if (fband->useDfs & rd->dfsMask)
832 icv.privFlags =
833 CHANNEL_DFS;
834 else
835 icv.privFlags = 0;
836 if (rd->flags & LIMIT_FRAME_4MS)
837 icv.privFlags |=
838 CHANNEL_4MS_LIMIT;
839
840 if (icv.privFlags & CHANNEL_DFS) {
841 icv.privFlags |=
842 CHANNEL_DISALLOW_ADHOC;
843 }
844 if (icv.
845 regDmnFlags & ADHOC_PER_11D) {
846 icv.privFlags |=
847 CHANNEL_PER_11D_ADHOC;
848 }
849 if (icv.
850 channelFlags & CHANNEL_PASSIVE) {
851
852 if ((icv.channel < 2412)
853 || (icv.channel >
854 2462)) {
855 if (rd5GHz.
856 regDmnEnum ==
857 MKK1
858 || rd5GHz.
859 regDmnEnum ==
860 MKK2) {
861 u_int32_t
862 regcap
863 =
864 ah->
865 ah_caps.
866 halRegCap;
867 if (!
868 (regcap
869 &
870 (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN
871 |
872 AR_EEPROM_EEREGCAP_EN_KK_U2
873 |
874 AR_EEPROM_EEREGCAP_EN_KK_MIDBAND))
875 && isUNII1OddChan(icv.channel)) {
876
877 icv.channelFlags &= ~CHANNEL_PASSIVE;
878 } else {
879 icv.privFlags |= CHANNEL_DISALLOW_ADHOC;
880 }
881 } else {
882 icv.privFlags |= CHANNEL_DISALLOW_ADHOC;
883 }
884 }
885 }
886 if (cm->
887 mode & (ATH9K_MODE_SEL_11A |
888 ATH9K_MODE_SEL_11NA_HT20
889 |
890 ATH9K_MODE_SEL_11NA_HT40PLUS
891 |
892 ATH9K_MODE_SEL_11NA_HT40MINUS)) {
893 if (icv.
894 regDmnFlags &
895 (ADHOC_NO_11A |
896 DISALLOW_ADHOC_11A)) {
897 icv.privFlags |=
898 CHANNEL_DISALLOW_ADHOC;
899 }
900 }
901
902 memcpy(&ichans[next++], &icv,
903 sizeof(struct
904 hal_channel_internal));
905 }
906
907 if (IS_HT40_MODE(cm->mode)
908 && (fband->lowChannel == 5280)) {
909 low_adj -= 20;
910 }
911 }
912 }
913 }
914 done:
915 if (next != 0) {
916 int i;
917
918
919 if (next > ARRAY_SIZE(ah->ah_channels)) {
920 HDPRINTF(ah, HAL_DBG_REGULATORY,
921 "%s: too many channels %u; truncating to %u\n",
922 __func__, next,
923 (int) ARRAY_SIZE(ah->ah_channels));
924 next = ARRAY_SIZE(ah->ah_channels);
925 }
926 #ifdef ATH_NF_PER_CHAN
927
928 ath9k_regd_init_rf_buffer(ichans, next);
929 #endif
930
931 ath9k_regd_sort(ichans, next,
932 sizeof(struct hal_channel_internal),
933 ath9k_regd_chansort);
934 ah->ah_nchan = next;
935
936 HDPRINTF(ah, HAL_DBG_REGULATORY, "Channel list:\n");
937 for (i = 0; i < next; i++) {
938 HDPRINTF(ah, HAL_DBG_REGULATORY,
939 "chan: %d flags: 0x%x\n",
940 ichans[i].channel,
941 ichans[i].channelFlags);
942 chans[i].channel = ichans[i].channel;
943 chans[i].channelFlags = ichans[i].channelFlags;
944 chans[i].privFlags = ichans[i].privFlags;
945 chans[i].maxRegTxPower = ichans[i].maxRegTxPower;
946 }
947
948 ath9k_hw_get_chip_power_limits(ah, chans, next);
949 for (i = 0; i < next; i++) {
950 ichans[i].maxTxPower = chans[i].maxTxPower;
951 ichans[i].minTxPower = chans[i].minTxPower;
952 }
953 }
954 *nchans = next;
955
956 ah->ah_countryCode = ah->ah_countryCode;
957
958 ah->ah_currentRDInUse = regdmn;
959 ah->ah_currentRD5G = rd5GHz.regDmnEnum;
960 ah->ah_currentRD2G = rd2GHz.regDmnEnum;
961 if (country == NULL) {
962 ah->ah_iso[0] = 0;
963 ah->ah_iso[1] = 0;
964 } else {
965 ah->ah_iso[0] = country->isoName[0];
966 ah->ah_iso[1] = country->isoName[1];
967 }
968 return next != 0;
969 }
970
971 struct hal_channel_internal *ath9k_regd_check_channel(struct ath_hal *ah,
972 const struct hal_channel *c)
973 {
974 struct hal_channel_internal *base, *cc;
975
976 int flags = c->channelFlags & CHAN_FLAGS;
977 int n, lim;
978
979 HDPRINTF(ah, HAL_DBG_REGULATORY,
980 "%s: channel %u/0x%x (0x%x) requested\n", __func__,
981 c->channel, c->channelFlags, flags);
982
983 cc = ah->ah_curchan;
984 if (cc != NULL && cc->channel == c->channel &&
985 (cc->channelFlags & CHAN_FLAGS) == flags) {
986 if ((cc->privFlags & CHANNEL_INTERFERENCE) &&
987 (cc->privFlags & CHANNEL_DFS))
988 return NULL;
989 else
990 return cc;
991 }
992
993 base = ah->ah_channels;
994 n = ah->ah_nchan;
995
996 for (lim = n; lim != 0; lim >>= 1) {
997 int d;
998 cc = &base[lim >> 1];
999 d = c->channel - cc->channel;
1000 if (d == 0) {
1001 if ((cc->channelFlags & CHAN_FLAGS) == flags) {
1002 if ((cc->privFlags & CHANNEL_INTERFERENCE)
1003 && (cc->privFlags & CHANNEL_DFS))
1004 return NULL;
1005 else
1006 return cc;
1007 }
1008 d = flags - (cc->channelFlags & CHAN_FLAGS);
1009 }
1010 HDPRINTF(ah, HAL_DBG_REGULATORY,
1011 "%s: channel %u/0x%x d %d\n", __func__,
1012 cc->channel, cc->channelFlags, d);
1013 if (d > 0) {
1014 base = cc + 1;
1015 lim--;
1016 }
1017 }
1018 HDPRINTF(ah, HAL_DBG_REGULATORY, "%s: no match for %u/0x%x\n",
1019 __func__, c->channel, c->channelFlags);
1020 return NULL;
1021 }
1022
1023 u_int
1024 ath9k_regd_get_antenna_allowed(struct ath_hal *ah,
1025 struct hal_channel *chan)
1026 {
1027 struct hal_channel_internal *ichan = NULL;
1028
1029 ichan = ath9k_regd_check_channel(ah, chan);
1030 if (!ichan)
1031 return 0;
1032
1033 return ichan->antennaMax;
1034 }
1035
1036 u_int ath9k_regd_get_ctl(struct ath_hal *ah, struct hal_channel *chan)
1037 {
1038 u_int ctl = NO_CTL;
1039 struct hal_channel_internal *ichan;
1040
1041 if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) {
1042 if (IS_CHAN_B(chan))
1043 ctl = SD_NO_CTL | CTL_11B;
1044 else if (IS_CHAN_G(chan))
1045 ctl = SD_NO_CTL | CTL_11G;
1046 else
1047 ctl = SD_NO_CTL | CTL_11A;
1048 } else {
1049 ichan = ath9k_regd_check_channel(ah, chan);
1050 if (ichan != NULL) {
1051 ctl = ichan->conformanceTestLimit;
1052
1053 if (IS_CHAN_PUREG(chan) && (ctl & 0xf) == CTL_11B)
1054 ctl = (ctl & ~0xf) | CTL_11G;
1055 }
1056 }
1057 return ctl;
1058 }
1059
1060 void ath9k_regd_get_current_country(struct ath_hal *ah,
1061 struct hal_country_entry *ctry)
1062 {
1063 u_int16_t rd = ath9k_regd_get_eepromRD(ah);
1064
1065 ctry->isMultidomain = AH_FALSE;
1066 if (rd == CTRY_DEFAULT)
1067 ctry->isMultidomain = AH_TRUE;
1068 else if (!(rd & COUNTRY_ERD_FLAG))
1069 ctry->isMultidomain = isWwrSKU(ah);
1070
1071 ctry->countryCode = ah->ah_countryCode;
1072 ctry->regDmnEnum = ah->ah_currentRD;
1073 ctry->regDmn5G = ah->ah_currentRD5G;
1074 ctry->regDmn2G = ah->ah_currentRD2G;
1075 ctry->iso[0] = ah->ah_iso[0];
1076 ctry->iso[1] = ah->ah_iso[1];
1077 ctry->iso[2] = ah->ah_iso[2];
1078 }