ah_regdomain.c revision 303975
1/*
2 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3 * Copyright (c) 2005-2006 Atheros Communications, Inc.
4 * All rights reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $FreeBSD: releng/11.0/sys/dev/ath/ath_hal/ah_regdomain.c 300267 2016-05-20 06:06:21Z adrian $
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
101static 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
130static void ath_hal_update_dfsdomain(struct ath_hal *ah);
131
132static OS_INLINE uint16_t
133getEepromRD(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 */
141static HAL_BOOL
142isChanBitMaskZero(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 */
158static HAL_BOOL
159isEepromValid(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 */
188static COUNTRY_CODE_TO_ENUM_RD*
189findCountry(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
200static REG_DOMAIN *
201findRegDmn(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
212static REG_DMN_PAIR_MAPPING *
213findRegDmnPair(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 */
229static HAL_CTRY_CODE
230getDefaultCountry(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
250static HAL_BOOL
251IS_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
262static HAL_STATUS
263getregstate(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
363static uint64_t *
364getchannelBM(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
398static void
399setchannelflags(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
419static int
420addchan(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	c = &chans[(*nchans)++];
430	c->ic_freq = freq;
431	c->ic_flags = flags;
432	setchannelflags(c, fband, rd);
433	c->ic_maxregpower = fband->powerDfs;
434	ath_hal_getpowerlimits(ah, c);
435	c->ic_maxantgain = fband->antennaMax;
436
437	return (0);
438}
439
440static int
441copychan_prev(struct ath_hal *ah, struct ieee80211_channel chans[],
442    u_int maxchans, int *nchans, uint16_t freq)
443{
444	struct ieee80211_channel *c;
445
446	if (*nchans == 0)
447		return (HAL_EINVAL);
448
449	if (*nchans >= maxchans)
450		return (HAL_ENOMEM);
451
452	c = &chans[(*nchans)++];
453	c[0] = c[-1];
454	c->ic_freq = freq;
455	/* XXX is it needed here? */
456	ath_hal_getpowerlimits(ah, c);
457
458	return (0);
459}
460
461static int
462add_chanlist_band(struct ath_hal *ah, struct ieee80211_channel chans[],
463    int maxchans, int *nchans, uint16_t freq_lo, uint16_t freq_hi, int step,
464    uint32_t flags, REG_DMN_FREQ_BAND *fband, REG_DOMAIN *rd)
465{
466	uint16_t freq = freq_lo;
467	int error;
468
469	if (freq_hi < freq_lo)
470		return (0);
471
472	error = addchan(ah, chans, maxchans, nchans, freq, flags, fband, rd);
473	for (freq += step; freq <= freq_hi && error == 0; freq += step)
474		error = copychan_prev(ah, chans, maxchans, nchans, freq);
475
476	return (error);
477}
478
479static void
480adj_freq_ht40(u_int mode, int *low_adj, int *hi_adj, int *channelSep)
481{
482
483	*low_adj = *hi_adj = *channelSep = 0;
484	switch (mode) {
485	case HAL_MODE_11NA_HT40PLUS:
486		*channelSep = 40;
487		/* FALLTHROUGH */
488	case HAL_MODE_11NG_HT40PLUS:
489		*hi_adj = -20;
490		break;
491	case HAL_MODE_11NA_HT40MINUS:
492		*channelSep = 40;
493		/* FALLTHROUGH */
494	case HAL_MODE_11NG_HT40MINUS:
495		*low_adj = 20;
496		break;
497	}
498}
499
500static void
501add_chanlist_mode(struct ath_hal *ah, struct ieee80211_channel chans[],
502    u_int maxchans, int *nchans, const struct cmode *cm, REG_DOMAIN *rd,
503    HAL_BOOL enableExtendedChannels)
504{
505	uint64_t *channelBM;
506	uint16_t freq_lo, freq_hi;
507	int b, error, low_adj, hi_adj, channelSep;
508
509	if (!ath_hal_getChannelEdges(ah, cm->flags, &freq_lo, &freq_hi)) {
510		/* channel not supported by hardware, skip it */
511		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
512		    "%s: channels 0x%x not supported by hardware\n",
513		    __func__, cm->flags);
514		return;
515	}
516
517	channelBM = getchannelBM(cm->mode, rd);
518	if (isChanBitMaskZero(channelBM))
519		return;
520
521	/*
522	 * Setup special handling for HT40 channels; e.g.
523	 * 5G HT40 channels require 40Mhz channel separation.
524	 */
525	adj_freq_ht40(cm->mode, &low_adj, &hi_adj, &channelSep);
526
527	for (b = 0; b < 64*BMLEN; b++) {
528		REG_DMN_FREQ_BAND *fband;
529		uint16_t bfreq_lo, bfreq_hi;
530		int step;
531
532		if (!IS_BIT_SET(b, channelBM))
533			continue;
534		fband = &cm->freqs[b];
535
536		if ((fband->usePassScan & IS_ECM_CHAN) &&
537		    !enableExtendedChannels) {
538			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
539			    "skip ecm channels\n");
540			continue;
541		}
542#if 0
543		if ((fband->useDfs & rd->dfsMask) &&
544		    (cm->flags & IEEE80211_CHAN_HT40)) {
545			/* NB: DFS and HT40 don't mix */
546			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
547			    "skip HT40 chan, DFS required\n");
548			continue;
549		}
550#endif
551		bfreq_lo = MAX(fband->lowChannel + low_adj, freq_lo);
552		bfreq_hi = MIN(fband->highChannel + hi_adj, freq_hi);
553		if (fband->channelSep >= channelSep)
554			step = fband->channelSep;
555		else
556			step = roundup(channelSep, fband->channelSep);
557
558		error = add_chanlist_band(ah, chans, maxchans, nchans,
559		    bfreq_lo, bfreq_hi, step, cm->flags, fband, rd);
560		if (error != 0)	{
561			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
562			    "%s: too many channels for channel table\n",
563			    __func__);
564			return;
565		}
566	}
567}
568
569static u_int
570getmodesmask(struct ath_hal *ah, REG_DOMAIN *rd5GHz, u_int modeSelect)
571{
572#define	HAL_MODE_11A_ALL \
573	(HAL_MODE_11A | HAL_MODE_11A_TURBO | HAL_MODE_TURBO | \
574	 HAL_MODE_11A_QUARTER_RATE | HAL_MODE_11A_HALF_RATE)
575	u_int modesMask;
576
577	/* get modes that HW is capable of */
578	modesMask = ath_hal_getWirelessModes(ah);
579	modesMask &= modeSelect;
580	/* optimize work below if no 11a channels */
581	if (isChanBitMaskZero(rd5GHz->chan11a) &&
582	    (modesMask & HAL_MODE_11A_ALL)) {
583		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
584		    "%s: disallow all 11a\n", __func__);
585		modesMask &= ~HAL_MODE_11A_ALL;
586	}
587
588	return (modesMask);
589#undef HAL_MODE_11A_ALL
590}
591
592/*
593 * Construct the channel list for the specified regulatory config.
594 */
595static HAL_STATUS
596getchannels(struct ath_hal *ah,
597    struct ieee80211_channel chans[], u_int maxchans, int *nchans,
598    u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
599    HAL_BOOL enableExtendedChannels,
600    COUNTRY_CODE_TO_ENUM_RD **pcountry,
601    REG_DOMAIN **prd2GHz, REG_DOMAIN **prd5GHz)
602{
603	REG_DOMAIN *rd5GHz, *rd2GHz;
604	u_int modesMask;
605	const struct cmode *cm;
606	HAL_STATUS status;
607
608	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u regDmn 0x%x mode 0x%x%s\n",
609	    __func__, cc, regDmn, modeSelect,
610	    enableExtendedChannels ? " ecm" : "");
611
612	status = getregstate(ah, cc, regDmn, pcountry, &rd2GHz, &rd5GHz);
613	if (status != HAL_OK)
614		return status;
615
616	modesMask = getmodesmask(ah, rd5GHz, modeSelect);
617	/* XXX error? */
618	if (modesMask == 0)
619		goto done;
620
621	for (cm = modes; cm < &modes[N(modes)]; cm++) {
622		REG_DOMAIN *rd;
623
624		if ((cm->mode & modesMask) == 0) {
625			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
626			    "%s: skip mode 0x%x flags 0x%x\n",
627			    __func__, cm->mode, cm->flags);
628			continue;
629		}
630
631		if (cm->flags & IEEE80211_CHAN_5GHZ)
632			rd = rd5GHz;
633		else if (cm->flags & IEEE80211_CHAN_2GHZ)
634			rd = rd2GHz;
635		else {
636			ath_hal_printf(ah, "%s: Unkonwn HAL flags 0x%x\n",
637			    __func__, cm->flags);
638			return HAL_EINVAL;
639		}
640
641		add_chanlist_mode(ah, chans, maxchans, nchans, cm,
642		    rd, enableExtendedChannels);
643		if (*nchans >= maxchans)
644			goto done;
645	}
646done:
647	/* NB: pcountry set above by getregstate */
648	if (prd2GHz != AH_NULL)
649		*prd2GHz = rd2GHz;
650	if (prd5GHz != AH_NULL)
651		*prd5GHz = rd5GHz;
652	return HAL_OK;
653}
654
655/*
656 * Retrieve a channel list without affecting runtime state.
657 */
658HAL_STATUS
659ath_hal_getchannels(struct ath_hal *ah,
660    struct ieee80211_channel chans[], u_int maxchans, int *nchans,
661    u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
662    HAL_BOOL enableExtendedChannels)
663{
664	return getchannels(ah, chans, maxchans, nchans, modeSelect,
665	    cc, regDmn, enableExtendedChannels, AH_NULL, AH_NULL, AH_NULL);
666}
667
668/*
669 * Handle frequency mapping from 900Mhz range to 2.4GHz range
670 * for GSM radios.  This is done when we need the h/w frequency
671 * and the channel is marked IEEE80211_CHAN_GSM.
672 */
673static int
674ath_hal_mapgsm(int sku, int freq)
675{
676	if (sku == SKU_XR9)
677		return 1520 + freq;
678	if (sku == SKU_GZ901)
679		return 1544 + freq;
680	if (sku == SKU_SR9)
681		return 3344 - freq;
682	if (sku == SKU_XC900M)
683		return 1517 + freq;
684	HALDEBUG(AH_NULL, HAL_DEBUG_ANY,
685	    "%s: cannot map freq %u unknown gsm sku %u\n",
686	    __func__, freq, sku);
687	return freq;
688}
689
690/*
691 * Setup the internal/private channel state given a table of
692 * net80211 channels.  We collapse entries for the same frequency
693 * and record the frequency for doing noise floor processing
694 * where we don't have net80211 channel context.
695 */
696static HAL_BOOL
697assignPrivateChannels(struct ath_hal *ah,
698	struct ieee80211_channel chans[], int nchans, int sku)
699{
700	HAL_CHANNEL_INTERNAL *ic;
701	int i, j, next, freq;
702
703	next = 0;
704	for (i = 0; i < nchans; i++) {
705		struct ieee80211_channel *c = &chans[i];
706		for (j = i-1; j >= 0; j--)
707			if (chans[j].ic_freq == c->ic_freq) {
708				c->ic_devdata = chans[j].ic_devdata;
709				break;
710			}
711		if (j < 0) {
712			/* new entry, assign a private channel entry */
713			if (next >= N(AH_PRIVATE(ah)->ah_channels)) {
714				HALDEBUG(ah, HAL_DEBUG_ANY,
715				    "%s: too many channels, max %zu\n",
716				    __func__, N(AH_PRIVATE(ah)->ah_channels));
717				return AH_FALSE;
718			}
719			/*
720			 * Handle frequency mapping for 900MHz devices.
721			 * The hardware uses 2.4GHz frequencies that are
722			 * down-converted.  The 802.11 layer uses the
723			 * true frequencies.
724			 */
725			freq = IEEE80211_IS_CHAN_GSM(c) ?
726			    ath_hal_mapgsm(sku, c->ic_freq) : c->ic_freq;
727
728			HALDEBUG(ah, HAL_DEBUG_REGDOMAIN,
729			    "%s: private[%3u] %u/0x%x -> channel %u\n",
730			    __func__, next, c->ic_freq, c->ic_flags, freq);
731
732			ic = &AH_PRIVATE(ah)->ah_channels[next];
733			/*
734			 * NB: This clears privFlags which means ancillary
735			 *     code like ANI and IQ calibration will be
736			 *     restarted and re-setup any per-channel state.
737			 */
738			OS_MEMZERO(ic, sizeof(*ic));
739			ic->channel = freq;
740			c->ic_devdata = next;
741			next++;
742		}
743	}
744	AH_PRIVATE(ah)->ah_nchan = next;
745	HALDEBUG(ah, HAL_DEBUG_ANY, "%s: %u public, %u private channels\n",
746	    __func__, nchans, next);
747	return AH_TRUE;
748}
749
750/*
751 * Setup the channel list based on the information in the EEPROM.
752 */
753HAL_STATUS
754ath_hal_init_channels(struct ath_hal *ah,
755    struct ieee80211_channel chans[], u_int maxchans, int *nchans,
756    u_int modeSelect, HAL_CTRY_CODE cc, HAL_REG_DOMAIN regDmn,
757    HAL_BOOL enableExtendedChannels)
758{
759	COUNTRY_CODE_TO_ENUM_RD *country;
760	REG_DOMAIN *rd5GHz, *rd2GHz;
761	HAL_STATUS status;
762
763	status = getchannels(ah, chans, maxchans, nchans, modeSelect,
764	    cc, regDmn, enableExtendedChannels, &country, &rd2GHz, &rd5GHz);
765	if (status == HAL_OK &&
766	    assignPrivateChannels(ah, chans, *nchans, AH_PRIVATE(ah)->ah_currentRD)) {
767		AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
768		AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
769
770		ah->ah_countryCode = country->countryCode;
771		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
772		    __func__, ah->ah_countryCode);
773
774		/* Update current DFS domain */
775		ath_hal_update_dfsdomain(ah);
776	} else
777		status = HAL_EINVAL;
778
779	return status;
780}
781
782/*
783 * Set the channel list.
784 */
785HAL_STATUS
786ath_hal_set_channels(struct ath_hal *ah,
787    struct ieee80211_channel chans[], int nchans,
788    HAL_CTRY_CODE cc, HAL_REG_DOMAIN rd)
789{
790	COUNTRY_CODE_TO_ENUM_RD *country;
791	REG_DOMAIN *rd5GHz, *rd2GHz;
792	HAL_STATUS status;
793
794	switch (rd) {
795	case SKU_SR9:
796	case SKU_XR9:
797	case SKU_GZ901:
798	case SKU_XC900M:
799		/*
800		 * Map 900MHz sku's.  The frequencies will be mapped
801		 * according to the sku to compensate for the down-converter.
802		 * We use the FCC for these sku's as the mapped channel
803		 * list is known compatible (will need to change if/when
804		 * vendors do different mapping in different locales).
805		 */
806		status = getregstate(ah, CTRY_DEFAULT, SKU_FCC,
807		    &country, &rd2GHz, &rd5GHz);
808		break;
809	default:
810		status = getregstate(ah, cc, rd,
811		    &country, &rd2GHz, &rd5GHz);
812		rd = AH_PRIVATE(ah)->ah_currentRD;
813		break;
814	}
815	if (status == HAL_OK && assignPrivateChannels(ah, chans, nchans, rd)) {
816		AH_PRIVATE(ah)->ah_rd2GHz = rd2GHz;
817		AH_PRIVATE(ah)->ah_rd5GHz = rd5GHz;
818
819		ah->ah_countryCode = country->countryCode;
820		HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: cc %u\n",
821		    __func__, ah->ah_countryCode);
822	} else
823		status = HAL_EINVAL;
824
825	if (status == HAL_OK) {
826		/* Update current DFS domain */
827		(void) ath_hal_update_dfsdomain(ah);
828	}
829	return status;
830}
831
832#ifdef AH_DEBUG
833/*
834 * Return the internal channel corresponding to a public channel.
835 * NB: normally this routine is inline'd (see ah_internal.h)
836 */
837HAL_CHANNEL_INTERNAL *
838ath_hal_checkchannel(struct ath_hal *ah, const struct ieee80211_channel *c)
839{
840	HAL_CHANNEL_INTERNAL *cc = &AH_PRIVATE(ah)->ah_channels[c->ic_devdata];
841
842	if (c->ic_devdata < AH_PRIVATE(ah)->ah_nchan &&
843	    (c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c)))
844		return cc;
845	if (c->ic_devdata >= AH_PRIVATE(ah)->ah_nchan) {
846		HALDEBUG(ah, HAL_DEBUG_ANY,
847		    "%s: bad mapping, devdata %u nchans %u\n",
848		   __func__, c->ic_devdata, AH_PRIVATE(ah)->ah_nchan);
849		HALASSERT(c->ic_devdata < AH_PRIVATE(ah)->ah_nchan);
850	} else {
851		HALDEBUG(ah, HAL_DEBUG_ANY,
852		    "%s: no match for %u/0x%x devdata %u channel %u\n",
853		   __func__, c->ic_freq, c->ic_flags, c->ic_devdata,
854		   cc->channel);
855		HALASSERT(c->ic_freq == cc->channel || IEEE80211_IS_CHAN_GSM(c));
856	}
857	return AH_NULL;
858}
859#endif /* AH_DEBUG */
860
861#define isWwrSKU(_ah) \
862	((getEepromRD((_ah)) & WORLD_SKU_MASK) == WORLD_SKU_PREFIX || \
863	  getEepromRD(_ah) == WORLD)
864
865/*
866 * Return the test group for the specific channel based on
867 * the current regulatory setup.
868 */
869u_int
870ath_hal_getctl(struct ath_hal *ah, const struct ieee80211_channel *c)
871{
872	u_int ctl;
873
874	if (AH_PRIVATE(ah)->ah_rd2GHz == AH_PRIVATE(ah)->ah_rd5GHz ||
875	    (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)))
876		ctl = SD_NO_CTL;
877	else if (IEEE80211_IS_CHAN_2GHZ(c))
878		ctl = AH_PRIVATE(ah)->ah_rd2GHz->conformanceTestLimit;
879	else
880		ctl = AH_PRIVATE(ah)->ah_rd5GHz->conformanceTestLimit;
881	if (IEEE80211_IS_CHAN_B(c))
882		return ctl | CTL_11B;
883	if (IEEE80211_IS_CHAN_G(c))
884		return ctl | CTL_11G;
885	if (IEEE80211_IS_CHAN_108G(c))
886		return ctl | CTL_108G;
887	if (IEEE80211_IS_CHAN_TURBO(c))
888		return ctl | CTL_TURBO;
889	if (IEEE80211_IS_CHAN_A(c))
890		return ctl | CTL_11A;
891	return ctl;
892}
893
894
895/*
896 * Update the current dfsDomain setting based on the given
897 * country code.
898 *
899 * Since FreeBSD/net80211 allows the channel set to change
900 * after the card has been setup (via ath_hal_init_channels())
901 * this function method is needed to update ah_dfsDomain.
902 */
903void
904ath_hal_update_dfsdomain(struct ath_hal *ah)
905{
906	const REG_DOMAIN *rd5GHz = AH_PRIVATE(ah)->ah_rd5GHz;
907	HAL_DFS_DOMAIN dfsDomain = HAL_DFS_UNINIT_DOMAIN;
908
909	if (rd5GHz->dfsMask & DFS_FCC3)
910		dfsDomain = HAL_DFS_FCC_DOMAIN;
911	if (rd5GHz->dfsMask & DFS_ETSI)
912		dfsDomain = HAL_DFS_ETSI_DOMAIN;
913	if (rd5GHz->dfsMask & DFS_MKK4)
914		dfsDomain = HAL_DFS_MKK4_DOMAIN;
915	AH_PRIVATE(ah)->ah_dfsDomain = dfsDomain;
916	HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s ah_dfsDomain: %d\n",
917	    __func__, AH_PRIVATE(ah)->ah_dfsDomain);
918}
919
920
921/*
922 * Return the max allowed antenna gain and apply any regulatory
923 * domain specific changes.
924 *
925 * NOTE: a negative reduction is possible in RD's that only
926 * measure radiated power (e.g., ETSI) which would increase
927 * that actual conducted output power (though never beyond
928 * the calibrated target power).
929 */
930u_int
931ath_hal_getantennareduction(struct ath_hal *ah,
932    const struct ieee80211_channel *chan, u_int twiceGain)
933{
934	int8_t antennaMax = twiceGain - chan->ic_maxantgain*2;
935	return (antennaMax < 0) ? 0 : antennaMax;
936}
937