Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/ath/ath_hal/ah_regdomain.c
39536 views
1
/*-
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
5
* Copyright (c) 2005-2006 Atheros Communications, Inc.
6
* All rights reserved.
7
*
8
* Permission to use, copy, modify, and/or distribute this software for any
9
* purpose with or without fee is hereby granted, provided that the above
10
* copyright notice and this permission notice appear in all copies.
11
*
12
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
*/
20
#include "opt_ah.h"
21
22
#include "ah.h"
23
24
#include <net80211/_ieee80211.h>
25
#include <net80211/ieee80211_regdomain.h>
26
27
#include "ah_internal.h"
28
#include "ah_eeprom.h"
29
#include "ah_devid.h"
30
31
#include "ah_regdomain.h"
32
33
/*
34
* XXX this code needs a audit+review
35
*/
36
37
/* used throughout this file... */
38
#define N(a) nitems(a)
39
40
#define HAL_MODE_11A_TURBO HAL_MODE_108A
41
#define HAL_MODE_11G_TURBO HAL_MODE_108G
42
43
/*
44
* Mask to check whether a domain is a multidomain or a single domain
45
*/
46
#define MULTI_DOMAIN_MASK 0xFF00
47
48
/*
49
* Enumerated Regulatory Domain Information 8 bit values indicate that
50
* the regdomain is really a pair of unitary regdomains. 12 bit values
51
* are the real unitary regdomains and are the only ones which have the
52
* frequency bitmasks and flags set.
53
*/
54
#include "ah_regdomain/ah_rd_regenum.h"
55
56
#define WORLD_SKU_MASK 0x00F0
57
#define WORLD_SKU_PREFIX 0x0060
58
59
/*
60
* THE following table is the mapping of regdomain pairs specified by
61
* an 8 bit regdomain value to the individual unitary reg domains
62
*/
63
#include "ah_regdomain/ah_rd_regmap.h"
64
65
/*
66
* The following tables are the master list for all different freqeuncy
67
* bands with the complete matrix of all possible flags and settings
68
* for each band if it is used in ANY reg domain.
69
*/
70
71
#define COUNTRY_ERD_FLAG 0x8000
72
#define WORLDWIDE_ROAMING_FLAG 0x4000
73
74
/*
75
* This table maps country ISO codes from net80211 into regulatory
76
* domains which the ath regulatory domain code understands.
77
*/
78
#include "ah_regdomain/ah_rd_ctry.h"
79
80
/*
81
* The frequency band collections are a set of frequency ranges
82
* with shared properties - max tx power, max antenna gain, channel width,
83
* channel spacing, DFS requirements and passive scanning requirements.
84
*
85
* These are represented as entries in a frequency band bitmask.
86
* Each regulatory domain entry in ah_regdomain_domains.h uses one
87
* or more frequency band entries for each of the channel modes
88
* supported (11bg, 11a, half, quarter, turbo, etc.)
89
*
90
*/
91
#include "ah_regdomain/ah_rd_freqbands.h"
92
93
/*
94
* This is the main regulatory database. It defines the supported
95
* set of features and requirements for each of the defined regulatory
96
* zones. It uses combinations of frequency ranges - represented in
97
* a bitmask - to determine the requirements and limitations needed.
98
*/
99
#include "ah_regdomain/ah_rd_domains.h"
100
101
static const struct cmode modes[] = {
102
{ HAL_MODE_TURBO, IEEE80211_CHAN_ST, &regDmn5GhzTurboFreq[0] },
103
{ HAL_MODE_11A, IEEE80211_CHAN_A, &regDmn5GhzFreq[0] },
104
{ HAL_MODE_11B, IEEE80211_CHAN_B, &regDmn2GhzFreq[0] },
105
{ HAL_MODE_11G, IEEE80211_CHAN_G, &regDmn2Ghz11gFreq[0] },
106
{ HAL_MODE_11G_TURBO, IEEE80211_CHAN_108G, &regDmn2Ghz11gTurboFreq[0] },
107
{ HAL_MODE_11A_TURBO, IEEE80211_CHAN_108A, &regDmn5GhzTurboFreq[0] },
108
{ HAL_MODE_11A_QUARTER_RATE,
109
IEEE80211_CHAN_A | IEEE80211_CHAN_QUARTER, &regDmn5GhzFreq[0] },
110
{ HAL_MODE_11A_HALF_RATE,
111
IEEE80211_CHAN_A | IEEE80211_CHAN_HALF, &regDmn5GhzFreq[0] },
112
{ HAL_MODE_11G_QUARTER_RATE,
113
IEEE80211_CHAN_G | IEEE80211_CHAN_QUARTER, &regDmn2Ghz11gFreq[0] },
114
{ HAL_MODE_11G_HALF_RATE,
115
IEEE80211_CHAN_G | IEEE80211_CHAN_HALF, &regDmn2Ghz11gFreq[0] },
116
{ HAL_MODE_11NG_HT20,
117
IEEE80211_CHAN_G | IEEE80211_CHAN_HT20, &regDmn2Ghz11gFreq[0] },
118
{ HAL_MODE_11NG_HT40PLUS,
119
IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U, &regDmn2Ghz11gFreq[0] },
120
{ HAL_MODE_11NG_HT40MINUS,
121
IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D, &regDmn2Ghz11gFreq[0] },
122
{ HAL_MODE_11NA_HT20,
123
IEEE80211_CHAN_A | IEEE80211_CHAN_HT20, &regDmn5GhzFreq[0] },
124
{ HAL_MODE_11NA_HT40PLUS,
125
IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U, &regDmn5GhzFreq[0] },
126
{ HAL_MODE_11NA_HT40MINUS,
127
IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D, &regDmn5GhzFreq[0] },
128
};
129
130
static void ath_hal_update_dfsdomain(struct ath_hal *ah);
131
132
static OS_INLINE uint16_t
133
getEepromRD(struct ath_hal *ah)
134
{
135
return AH_PRIVATE(ah)->ah_currentRD &~ WORLDWIDE_ROAMING_FLAG;
136
}
137
138
/*
139
* Test to see if the bitmask array is all zeros
140
*/
141
static HAL_BOOL
142
isChanBitMaskZero(const uint64_t *bitmask)
143
{
144
#if BMLEN > 2
145
#error "add more cases"
146
#endif
147
#if BMLEN > 1
148
if (bitmask[1] != 0)
149
return AH_FALSE;
150
#endif
151
return (bitmask[0] == 0);
152
}
153
154
/*
155
* Return whether or not the regulatory domain/country in EEPROM
156
* is acceptable.
157
*/
158
static HAL_BOOL
159
isEepromValid(struct ath_hal *ah)
160
{
161
uint16_t rd = getEepromRD(ah);
162
int i;
163
164
if (rd & COUNTRY_ERD_FLAG) {
165
uint16_t cc = rd &~ COUNTRY_ERD_FLAG;
166
for (i = 0; i < N(allCountries); i++)
167
if (allCountries[i].countryCode == cc)
168
return AH_TRUE;
169
} else {
170
for (i = 0; i < N(regDomainPairs); i++)
171
if (regDomainPairs[i].regDmnEnum == rd)
172
return AH_TRUE;
173
}
174
175
if (rd == FCC_UBNT) {
176
return AH_TRUE;
177
}
178
179
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
180
"%s: invalid regulatory domain/country code 0x%x\n", __func__, rd);
181
return AH_FALSE;
182
}
183
184
/*
185
* Find the pointer to the country element in the country table
186
* corresponding to the country code
187
*/
188
static COUNTRY_CODE_TO_ENUM_RD*
189
findCountry(HAL_CTRY_CODE countryCode)
190
{
191
int i;
192
193
for (i = 0; i < N(allCountries); i++) {
194
if (allCountries[i].countryCode == countryCode)
195
return &allCountries[i];
196
}
197
return AH_NULL;
198
}
199
200
static REG_DOMAIN *
201
findRegDmn(int regDmn)
202
{
203
int i;
204
205
for (i = 0; i < N(regDomains); i++) {
206
if (regDomains[i].regDmnEnum == regDmn)
207
return &regDomains[i];
208
}
209
return AH_NULL;
210
}
211
212
static REG_DMN_PAIR_MAPPING *
213
findRegDmnPair(int regDmnPair)
214
{
215
int i;
216
217
if (regDmnPair != NO_ENUMRD) {
218
for (i = 0; i < N(regDomainPairs); i++) {
219
if (regDomainPairs[i].regDmnEnum == regDmnPair)
220
return &regDomainPairs[i];
221
}
222
}
223
return AH_NULL;
224
}
225
226
/*
227
* Calculate a default country based on the EEPROM setting.
228
*/
229
static HAL_CTRY_CODE
230
getDefaultCountry(struct ath_hal *ah)
231
{
232
REG_DMN_PAIR_MAPPING *regpair;
233
uint16_t rd;
234
235
rd = getEepromRD(ah);
236
if (rd & COUNTRY_ERD_FLAG) {
237
COUNTRY_CODE_TO_ENUM_RD *country;
238
uint16_t cc = rd & ~COUNTRY_ERD_FLAG;
239
country = findCountry(cc);
240
if (country != AH_NULL)
241
return cc;
242
}
243
/*
244
* Check reg domains that have only one country
245
*/
246
regpair = findRegDmnPair(rd);
247
return (regpair != AH_NULL) ? regpair->singleCC : CTRY_DEFAULT;
248
}
249
250
static HAL_BOOL
251
IS_BIT_SET(int bit, const uint64_t bitmask[])
252
{
253
int byteOffset, bitnum;
254
uint64_t val;
255
256
byteOffset = bit/64;
257
bitnum = bit - byteOffset*64;
258
val = ((uint64_t) 1) << bitnum;
259
return (bitmask[byteOffset] & val) != 0;
260
}
261
262
static HAL_STATUS
263
getregstate(struct ath_hal *ah, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
264
COUNTRY_CODE_TO_ENUM_RD **pcountry,
265
REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
266
{
267
COUNTRY_CODE_TO_ENUM_RD *country;
268
REG_DOMAIN *rd5GHz, *rd2GHz;
269
270
if (cc == CTRY_DEFAULT && regDmn == SKU_NONE) {
271
/*
272
* Validate the EEPROM setting and setup defaults
273
*/
274
if (!isEepromValid(ah)) {
275
/*
276
* Don't return any channels if the EEPROM has an
277
* invalid regulatory domain/country code setting.
278
*/
279
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
280
"%s: invalid EEPROM contents\n",__func__);
281
return HAL_EEBADREG;
282
}
283
284
cc = getDefaultCountry(ah);
285
country = findCountry(cc);
286
if (country == AH_NULL) {
287
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
288
"NULL Country!, cc %d\n", cc);
289
return HAL_EEBADCC;
290
}
291
regDmn = country->regDmnEnum;
292
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: EEPROM cc %u rd 0x%x\n",
293
__func__, cc, regDmn);
294
295
if (country->countryCode == CTRY_DEFAULT) {
296
/*
297
* Check EEPROM; SKU may be for a country, single
298
* domain, or multiple domains (WWR).
299
*/
300
uint16_t rdnum = getEepromRD(ah);
301
if ((rdnum & COUNTRY_ERD_FLAG) == 0 &&
302
(findRegDmn(rdnum) != AH_NULL ||
303
findRegDmnPair(rdnum) != AH_NULL)) {
304
regDmn = rdnum;
305
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
306
"%s: EEPROM rd 0x%x\n", __func__, rdnum);
307
}
308
}
309
} else {
310
country = findCountry(cc);
311
if (country == AH_NULL) {
312
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
313
"unknown country, cc %d\n", cc);
314
return HAL_EINVAL;
315
}
316
if (regDmn == SKU_NONE)
317
regDmn = country->regDmnEnum;
318
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u rd 0x%x\n",
319
__func__, cc, regDmn);
320
}
321
322
/*
323
* Setup per-band state.
324
*/
325
if ((regDmn & MULTI_DOMAIN_MASK) == 0) {
326
REG_DMN_PAIR_MAPPING *regpair = findRegDmnPair(regDmn);
327
if (regpair == AH_NULL) {
328
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
329
"%s: no reg domain pair %u for country %u\n",
330
__func__, regDmn, country->countryCode);
331
return HAL_EINVAL;
332
}
333
rd5GHz = findRegDmn(regpair->regDmn5GHz);
334
if (rd5GHz == AH_NULL) {
335
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
336
"%s: no 5GHz reg domain %u for country %u\n",
337
__func__, regpair->regDmn5GHz, country->countryCode);
338
return HAL_EINVAL;
339
}
340
rd2GHz = findRegDmn(regpair->regDmn2GHz);
341
if (rd2GHz == AH_NULL) {
342
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
343
"%s: no 2GHz reg domain %u for country %u\n",
344
__func__, regpair->regDmn2GHz, country->countryCode);
345
return HAL_EINVAL;
346
}
347
} else {
348
rd5GHz = rd2GHz = findRegDmn(regDmn);
349
if (rd2GHz == AH_NULL) {
350
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
351
"%s: no unitary reg domain %u for country %u\n",
352
__func__, regDmn, country->countryCode);
353
return HAL_EINVAL;
354
}
355
}
356
if (pcountry != AH_NULL)
357
*pcountry = country;
358
*prd2GHz = rd2GHz;
359
*prd5GHz = rd5GHz;
360
return HAL_OK;
361
}
362
363
static uint64_t *
364
getchannelBM(u_int mode, REG_DOMAIN *rd)
365
{
366
switch (mode) {
367
case HAL_MODE_11B:
368
return (rd->chan11b);
369
case HAL_MODE_11G_QUARTER_RATE:
370
return (rd->chan11g_quarter);
371
case HAL_MODE_11G_HALF_RATE:
372
return (rd->chan11g_half);
373
case HAL_MODE_11G:
374
case HAL_MODE_11NG_HT20:
375
case HAL_MODE_11NG_HT40PLUS:
376
case HAL_MODE_11NG_HT40MINUS:
377
return (rd->chan11g);
378
case HAL_MODE_11G_TURBO:
379
return (rd->chan11g_turbo);
380
case HAL_MODE_11A_QUARTER_RATE:
381
return (rd->chan11a_quarter);
382
case HAL_MODE_11A_HALF_RATE:
383
return (rd->chan11a_half);
384
case HAL_MODE_11A:
385
case HAL_MODE_11NA_HT20:
386
case HAL_MODE_11NA_HT40PLUS:
387
case HAL_MODE_11NA_HT40MINUS:
388
return (rd->chan11a);
389
case HAL_MODE_TURBO:
390
return (rd->chan11a_turbo);
391
case HAL_MODE_11A_TURBO:
392
return (rd->chan11a_dyn_turbo);
393
default:
394
return (AH_NULL);
395
}
396
}
397
398
static void
399
setchannelflags(struct ieee80211_channel *c, REG_DMN_FREQ_BAND *fband,
400
REG_DOMAIN *rd)
401
{
402
if (fband->usePassScan & rd->pscan)
403
c->ic_flags |= IEEE80211_CHAN_PASSIVE;
404
if (fband->useDfs & rd->dfsMask)
405
c->ic_flags |= IEEE80211_CHAN_DFS;
406
if (IEEE80211_IS_CHAN_5GHZ(c) && (rd->flags & DISALLOW_ADHOC_11A))
407
c->ic_flags |= IEEE80211_CHAN_NOADHOC;
408
if (IEEE80211_IS_CHAN_TURBO(c) &&
409
(rd->flags & DISALLOW_ADHOC_11A_TURB))
410
c->ic_flags |= IEEE80211_CHAN_NOADHOC;
411
if (rd->flags & NO_HOSTAP)
412
c->ic_flags |= IEEE80211_CHAN_NOHOSTAP;
413
if (rd->flags & LIMIT_FRAME_4MS)
414
c->ic_flags |= IEEE80211_CHAN_4MSXMIT;
415
if (rd->flags & NEED_NFC)
416
c->ic_flags |= CHANNEL_NFCREQUIRED;
417
}
418
419
static int
420
addchan(struct ath_hal *ah, struct ieee80211_channel chans[],
421
u_int maxchans, int *nchans, uint16_t freq, uint32_t flags,
422
REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)
423
{
424
struct ieee80211_channel *c;
425
426
if (*nchans >= maxchans)
427
return (HAL_ENOMEM);
428
429
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
430
"%s: %d: freq=%d, flags=0x%08x\n",
431
__func__, *nchans, (int) freq, flags);
432
433
c = &chans[(*nchans)++];
434
c->ic_freq = freq;
435
c->ic_flags = flags;
436
setchannelflags(c, fband, rd);
437
c->ic_maxregpower = fband->powerDfs;
438
ath_hal_getpowerlimits(ah, c);
439
c->ic_maxantgain = fband->antennaMax;
440
441
return (0);
442
}
443
444
static int
445
copychan_prev(struct ath_hal *ah, struct ieee80211_channel chans[],
446
u_int maxchans, int *nchans, uint16_t freq, uint32_t flags)
447
{
448
struct ieee80211_channel *c;
449
450
if (*nchans == 0)
451
return (HAL_EINVAL);
452
453
if (*nchans >= maxchans)
454
return (HAL_ENOMEM);
455
456
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
457
"%s: %d: freq=%d, flags=0x%08x\n",
458
__func__, *nchans, (int) freq, flags);
459
460
c = &chans[(*nchans)++];
461
c[0] = c[-1];
462
c->ic_freq = freq;
463
/* XXX is it needed here? */
464
ath_hal_getpowerlimits(ah, c);
465
466
return (0);
467
}
468
469
static int
470
add_chanlist_band(struct ath_hal *ah, struct ieee80211_channel chans[],
471
int maxchans, int *nchans, uint16_t freq_lo, uint16_t freq_hi, int step,
472
uint32_t flags, REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)
473
{
474
uint16_t freq = freq_lo;
475
int error;
476
477
if (freq_hi < freq_lo)
478
return (0);
479
480
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
481
"%s: freq=%d..%d, flags=0x%08x, step=%d\n", __func__,
482
(int) freq_lo, (int) freq_hi, flags, step);
483
484
error = addchan(ah, chans, maxchans, nchans, freq, flags, fband, rd);
485
for (freq += step; freq <= freq_hi && error == 0; freq += step)
486
error = copychan_prev(ah, chans, maxchans, nchans, freq, flags);
487
488
return (error);
489
}
490
491
static void
492
adj_freq_ht40(u_int mode, int *low_adj, int *hi_adj, int *channelSep)
493
{
494
495
*low_adj = *hi_adj = *channelSep = 0;
496
switch (mode) {
497
case HAL_MODE_11NA_HT40PLUS:
498
*channelSep = 40;
499
/* FALLTHROUGH */
500
case HAL_MODE_11NG_HT40PLUS:
501
*hi_adj = -20;
502
break;
503
case HAL_MODE_11NA_HT40MINUS:
504
*channelSep = 40;
505
/* FALLTHROUGH */
506
case HAL_MODE_11NG_HT40MINUS:
507
*low_adj = 20;
508
break;
509
}
510
}
511
512
static void
513
add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[],
514
u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd,
515
HAL_BOOL enableExtendedChannels)
516
{
517
uint64_t *channelBM;
518
uint16_t freq_lo, freq_hi;
519
int b, error, low_adj, hi_adj, channelSep;
520
521
if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) {
522
/* channel not supported by hardware, skip it */
523
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
524
"%s: channels 0x%x not supported by hardware\n",
525
__func__, cm->flags);
526
return;
527
}
528
529
channelBM = getchannelBM(cm->mode, rd);
530
if (isChanBitMaskZero(channelBM))
531
return;
532
533
/*
534
* Setup special handling for HT40 channels; e.g.
535
* 5G HT40 channels require 40Mhz channel separation.
536
*/
537
adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep);
538
539
for (b = 0; b < 64*BMLEN; b++) {
540
REG_DMN_FREQ_BAND *fband;
541
uint16_t bfreq_lo, bfreq_hi;
542
int step;
543
544
if (!IS_BIT_SET(b, channelBM))
545
continue;
546
fband = &cm->freqs[b];
547
548
if ((fband->usePassScan & IS_ECM_CHAN) &&
549
!enableExtendedChannels) {
550
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
551
"skip ecm channels\n");
552
continue;
553
}
554
#if 0
555
if ((fband->useDfs & rd->dfsMask) &&
556
(cm->flags & IEEE80211_CHAN_HT40)) {
557
/* NB: DFS and HT40 don't mix */
558
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
559
"skip HT40 chan, DFS required\n");
560
continue;
561
}
562
#endif
563
/*
564
* XXX TODO: handle REG_EXT_FCC_CH_144.
565
*
566
* Figure out which instances/uses cause us to not
567
* be allowed to use channel 144 (pri or sec overlap.)
568
*/
569
570
bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo);
571
bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi);
572
573
/*
574
* Don't start the 5GHz channel list at 5120MHz.
575
*
576
* Unfortunately (sigh) the HT40 channel creation
577
* logic will create HT40U channels at 5120, 5160, 5200.
578
* This means that 36 (5180) isn't considered as a
579
* HT40 channel, and everything goes messed up from there.
580
*/
581
if ((cm->flags & IEEE80211_CHAN_5GHZ) &&
582
(cm->flags & IEEE80211_CHAN_HT40U)) {
583
if (bfreq_lo < 5180)
584
bfreq_lo = 5180;
585
}
586
587
/*
588
* Same with HT40D - need to start at 5200 or the low
589
* channels are all wrong again.
590
*/
591
if ((cm->flags & IEEE80211_CHAN_5GHZ) &&
592
(cm->flags & IEEE80211_CHAN_HT40D)) {
593
if (bfreq_lo < 5200)
594
bfreq_lo = 5200;
595
}
596
597
if (fband->channelSep >= channelSep)
598
step = fband->channelSep;
599
else
600
step = roundup(channelSep, fband->channelSep);
601
602
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
603
"%s: freq_lo=%d, freq_hi=%d, low_adj=%d, hi_adj=%d, "
604
"bandlo=%d, bandhi=%d, bfreqlo=%d, bfreqhi=%d, step=%d, "
605
"flags=0x%08x\n",
606
__func__,
607
(int) freq_lo,
608
(int) freq_hi,
609
(int) low_adj,
610
(int) hi_adj,
611
(int) fband->lowChannel,
612
(int) fband->highChannel,
613
(int) bfreq_lo,
614
(int) bfreq_hi,
615
step,
616
(int) cm->flags);
617
618
error = add_chanlist_band(ah, chans, maxchans, nchans,
619
bfreq_lo, bfreq_hi, step, cm->flags, fband, rd);
620
if (error != 0) {
621
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
622
"%s: too many channels for channel table\n",
623
__func__);
624
return;
625
}
626
}
627
}
628
629
static u_int
630
getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect)
631
{
632
#define HAL_MODE_11A_ALL \
633
(HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \
634
HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE)
635
u_int modesMask;
636
637
/* get modes that HW is capable of */
638
modesMask = ath_hal_getWirelessModes(ah);
639
modesMask &= modeSelect;
640
/* optimize work below if no 11a channels */
641
if (isChanBitMaskZero(rd5GHz->chan11a) &&
642
(modesMask & HAL_MODE_11A_ALL)) {
643
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
644
"%s: disallow all 11a\n", __func__);
645
modesMask &= ~HAL_MODE_11A_ALL;
646
}
647
648
return (modesMask);
649
#undef HAL_MODE_11A_ALL
650
}
651
652
/*
653
* Construct the channel list for the specified regulatory config.
654
*/
655
static HAL_STATUS
656
getchannels(struct ath_hal *ah,
657
struct ieee80211_channel chans[], u_int maxchans, int *nchans,
658
u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
659
HAL_BOOL enableExtendedChannels,
660
COUNTRY_CODE_TO_ENUM_RD **pcountry,
661
REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
662
{
663
REG_DOMAIN *rd5GHz, *rd2GHz;
664
u_int modesMask;
665
const struct cmode *cm;
666
HAL_STATUS status;
667
668
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n",
669
__func__, cc, regDmn, modeSelect,
670
enableExtendedChannels ? " ecm" : "");
671
672
status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz);
673
if (status != HAL_OK)
674
return status;
675
676
modesMask = getmodesmask(ah, rd5GHz, modeSelect);
677
/* XXX error? */
678
if (modesMask == 0)
679
goto done;
680
681
for (cm = modes; cm < &modes[N(modes)]; cm++) {
682
REG_DOMAIN *rd;
683
684
if ((cm->mode & modesMask) == 0) {
685
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
686
"%s: skip mode 0x%x flags 0x%x\n",
687
__func__, cm->mode, cm->flags);
688
continue;
689
}
690
691
if (cm->flags & IEEE80211_CHAN_5GHZ)
692
rd = rd5GHz;
693
else if (cm->flags & IEEE80211_CHAN_2GHZ)
694
rd = rd2GHz;
695
else {
696
ath_hal_printf(ah, "%s: Unknown HAL flags 0x%x\n",
697
__func__, cm->flags);
698
return HAL_EINVAL;
699
}
700
701
add_chanlist_mode(ah, chans, maxchans, nchans, cm,
702
rd, enableExtendedChannels);
703
if (*nchans >= maxchans)
704
goto done;
705
}
706
done:
707
/* NB: pcountry set above by getregstate */
708
if (prd2GHz != AH_NULL)
709
*prd2GHz = rd2GHz;
710
if (prd5GHz != AH_NULL)
711
*prd5GHz = rd5GHz;
712
return HAL_OK;
713
}
714
715
/*
716
* Retrieve a channel list without affecting runtime state.
717
*/
718
HAL_STATUS
719
ath_hal_getchannels(struct ath_hal *ah,
720
struct ieee80211_channel chans[], u_int maxchans, int *nchans,
721
u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
722
HAL_BOOL enableExtendedChannels)
723
{
724
return getchannels(ah, chans, maxchans, nchans, modeSelect,
725
cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL);
726
}
727
728
/*
729
* Handle frequency mapping from 900Mhz range to 2.4GHz range
730
* for GSM radios. This is done when we need the h/w frequency
731
* and the channel is marked IEEE80211_CHAN_GSM.
732
*/
733
static int
734
ath_hal_mapgsm(int sku, int freq)
735
{
736
if (sku == SKU_XR9)
737
return 1520 + freq;
738
if (sku == SKU_GZ901)
739
return 1544 + freq;
740
if (sku == SKU_SR9)
741
return 3344 - freq;
742
if (sku == SKU_XC900M)
743
return 1517 + freq;
744
HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
745
"%s: cannot map freq %u unknown gsm sku %u\n",
746
__func__, freq, sku);
747
return freq;
748
}
749
750
/*
751
* Setup the internal/private channel state given a table of
752
* net80211 channels. We collapse entries for the same frequency
753
* and record the frequency for doing noise floor processing
754
* where we don't have net80211 channel context.
755
*/
756
static HAL_BOOL
757
assignPrivateChannels(struct ath_hal *ah,
758
struct ieee80211_channel chans[], int nchans, int sku)
759
{
760
HAL_CHANNEL_INTERNAL *ic;
761
int i, j, next, freq;
762
763
next = 0;
764
for (i = 0; i < nchans; i++) {
765
struct ieee80211_channel *c = &chans[i];
766
for (j = i-1; j >= 0; j--)
767
if (chans[j].ic_freq == c->ic_freq) {
768
c->ic_devdata = chans[j].ic_devdata;
769
break;
770
}
771
if (j < 0) {
772
/* new entry, assign a private channel entry */
773
if (next >= N(AH_PRIVATE(ah)->ah_channels)) {
774
HALDEBUG(ah, HAL_DEBUG_ANY,
775
"%s: too many channels, max %zu\n",
776
__func__, N(AH_PRIVATE(ah)->ah_channels));
777
return AH_FALSE;
778
}
779
/*
780
* Handle frequency mapping for 900MHz devices.
781
* The hardware uses 2.4GHz frequencies that are
782
* down-converted. The 802.11 layer uses the
783
* true frequencies.
784
*/
785
freq = IEEE80211_IS_CHAN_GSM(c) ?
786
ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq;
787
788
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
789
"%s: private[%3u] %u/0x%x -> channel %u\n",
790
__func__, next, c->ic_freq, c->ic_flags, freq);
791
792
ic = &AH_PRIVATE(ah)->ah_channels[next];
793
/*
794
* NB: This clears privFlags which means ancillary
795
* code like ANI and IQ calibration will be
796
* restarted and re-setup any per-channel state.
797
*/
798
OS_MEMZERO(ic, sizeof(*ic));
799
ic->channel = freq;
800
c->ic_devdata = next;
801
next++;
802
}
803
}
804
AH_PRIVATE(ah)->ah_nchan = next;
805
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n",
806
__func__, nchans, next);
807
return AH_TRUE;
808
}
809
810
/*
811
* Setup the channel list based on the information in the EEPROM.
812
*/
813
HAL_STATUS
814
ath_hal_init_channels(struct ath_hal *ah,
815
struct ieee80211_channel chans[], u_int maxchans, int *nchans,
816
u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
817
HAL_BOOL enableExtendedChannels)
818
{
819
COUNTRY_CODE_TO_ENUM_RD *country;
820
REG_DOMAIN *rd5GHz, *rd2GHz;
821
HAL_STATUS status;
822
823
status = getchannels(ah, chans, maxchans, nchans, modeSelect,
824
cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz);
825
if (status == HAL_OK &&
826
assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) {
827
AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
828
AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
829
830
ah->ah_countryCode = country->countryCode;
831
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
832
__func__, ah->ah_countryCode);
833
834
/* Update current DFS domain */
835
ath_hal_update_dfsdomain(ah);
836
} else
837
status = HAL_EINVAL;
838
839
return status;
840
}
841
842
/*
843
* Set the channel list.
844
*/
845
HAL_STATUS
846
ath_hal_set_channels(struct ath_hal *ah,
847
struct ieee80211_channel chans[], int nchans,
848
HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd)
849
{
850
COUNTRY_CODE_TO_ENUM_RD *country;
851
REG_DOMAIN *rd5GHz, *rd2GHz;
852
HAL_STATUS status;
853
854
switch (rd) {
855
case SKU_SR9:
856
case SKU_XR9:
857
case SKU_GZ901:
858
case SKU_XC900M:
859
/*
860
* Map 900MHz sku's. The frequencies will be mapped
861
* according to the sku to compensate for the down-converter.
862
* We use the FCC for these sku's as the mapped channel
863
* list is known compatible (will need to change if/when
864
* vendors do different mapping in different locales).
865
*/
866
status = getregstate(ah, CTRY_DEFAULT, SKU_FCC,
867
&country, &rd2GHz, &rd5GHz);
868
break;
869
default:
870
status = getregstate(ah, cc, rd,
871
&country, &rd2GHz, &rd5GHz);
872
rd = AH_PRIVATE(ah)->ah_currentRD;
873
break;
874
}
875
if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) {
876
AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
877
AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
878
879
ah->ah_countryCode = country->countryCode;
880
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
881
__func__, ah->ah_countryCode);
882
} else
883
status = HAL_EINVAL;
884
885
if (status == HAL_OK) {
886
/* Update current DFS domain */
887
(void) ath_hal_update_dfsdomain(ah);
888
}
889
return status;
890
}
891
892
#ifdef AH_DEBUG
893
/*
894
* Return the internal channel corresponding to a public channel.
895
* NB: normally this routine is inline'd (see ah_internal.h)
896
*/
897
HAL_CHANNEL_INTERNAL *
898
ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c)
899
{
900
HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata];
901
902
if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan &&
903
(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)))
904
return cc;
905
if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) {
906
HALDEBUG(ah, HAL_DEBUG_ANY,
907
"%s: bad mapping, devdata %u nchans %u\n",
908
__func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan);
909
HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan);
910
} else {
911
HALDEBUG(ah, HAL_DEBUG_ANY,
912
"%s: no match for %u/0x%x devdata %u channel %u\n",
913
__func__, c->ic_freq, c->ic_flags, c->ic_devdata,
914
cc->channel);
915
HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c));
916
}
917
return AH_NULL;
918
}
919
#endif /* AH_DEBUG */
920
921
#define isWwrSKU(_ah) \
922
((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \
923
getEepromRD(_ah) == WORLD)
924
925
/*
926
* Return the test group for the specific channel based on
927
* the current regulatory setup.
928
*/
929
u_int
930
ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c)
931
{
932
u_int ctl;
933
934
if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz ||
935
(ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)))
936
ctl = SD_NO_CTL;
937
else if (IEEE80211_IS_CHAN_2GHZ(c))
938
ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit;
939
else
940
ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit;
941
if (IEEE80211_IS_CHAN_B(c))
942
return ctl | CTL_11B;
943
if (IEEE80211_IS_CHAN_G(c))
944
return ctl | CTL_11G;
945
if (IEEE80211_IS_CHAN_108G(c))
946
return ctl | CTL_108G;
947
if (IEEE80211_IS_CHAN_TURBO(c))
948
return ctl | CTL_TURBO;
949
if (IEEE80211_IS_CHAN_A(c))
950
return ctl | CTL_11A;
951
return ctl;
952
}
953
954
/*
955
* Update the current dfsDomain setting based on the given
956
* country code.
957
*
958
* Since FreeBSD/net80211 allows the channel set to change
959
* after the card has been setup (via ath_hal_init_channels())
960
* this function method is needed to update ah_dfsDomain.
961
*/
962
void
963
ath_hal_update_dfsdomain(struct ath_hal *ah)
964
{
965
const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz;
966
HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN;
967
968
if (rd5GHz->dfsMask & DFS_FCC3)
969
dfsDomain = HAL_DFS_FCC_DOMAIN;
970
if (rd5GHz->dfsMask & DFS_ETSI)
971
dfsDomain = HAL_DFS_ETSI_DOMAIN;
972
if (rd5GHz->dfsMask & DFS_MKK4)
973
dfsDomain = HAL_DFS_MKK4_DOMAIN;
974
AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain;
975
HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n",
976
__func__, AH_PRIVATE(ah)->ah_dfsDomain);
977
}
978
979
/*
980
* Return the max allowed antenna gain and apply any regulatory
981
* domain specific changes.
982
*
983
* NOTE: a negative reduction is possible in RD's that only
984
* measure radiated power (e.g., ETSI) which would increase
985
* that actual conducted output power (though never beyond
986
* the calibrated target power).
987
*/
988
u_int
989
ath_hal_getantennareduction(struct ath_hal *ah,
990
const struct ieee80211_channel *chan, u_int twiceGain)
991
{
992
int8_t antennaMax = twiceGain - chan->ic_maxantgain*2;
993
return (antennaMax < 0) ? 0 : antennaMax;
994
}
995
996