aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/regd.c
diff options
context:
space:
mode:
authorBob Copeland <me@bobcopeland.com>2009-03-30 22:30:29 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-04-22 16:54:37 -0400
commit3a702e49c03ba959e3f5bb2b74ec9921a81c8c98 (patch)
tree721e8a222648284660f5bf6c5edfba5a6e4d4527 /drivers/net/wireless/ath/regd.c
parentc02cf3738c9dbc446c160b9d49a001eb2be316c8 (diff)
atheros: introduce ath module containing common ath5k/ath9k/ar9170 code
This change creates a new module, ath.ko, which includes code that can be shared between ath5k, ath9k and ar9170. For now, extract most of the ath9k regulatory code so it can also be used in ath5k. Signed-off-by: Bob Copeland <me@bobcopeland.com> Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/regd.c')
-rw-r--r--drivers/net/wireless/ath/regd.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
new file mode 100644
index 000000000000..4d3935b6fbdd
--- /dev/null
+++ b/drivers/net/wireless/ath/regd.c
@@ -0,0 +1,519 @@
1/*
2 * Copyright (c) 2008-2009 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 <net/cfg80211.h>
20#include <net/mac80211.h>
21#include <net/wireless.h>
22#include "regd.h"
23#include "regd_common.h"
24
25/*
26 * This is a set of common rules used by our world regulatory domains.
27 * We have 12 world regulatory domains. To save space we consolidate
28 * the regulatory domains in 5 structures by frequency and change
29 * the flags on our reg_notifier() on a case by case basis.
30 */
31
32/* Only these channels all allow active scan on all world regulatory domains */
33#define ATH9K_2GHZ_CH01_11 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
34
35/* We enable active scan on these a case by case basis by regulatory domain */
36#define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\
37 NL80211_RRF_PASSIVE_SCAN)
38#define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\
39 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
40
41/* We allow IBSS on these on a case by case basis by regulatory domain */
42#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 40, 0, 30,\
43 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
44#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 40, 0, 30,\
45 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
46#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 40, 0, 30,\
47 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
48
49#define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \
50 ATH9K_2GHZ_CH12_13, \
51 ATH9K_2GHZ_CH14
52
53#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \
54 ATH9K_5GHZ_5470_5850
55/* This one skips what we call "mid band" */
56#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \
57 ATH9K_5GHZ_5725_5850
58
59/* Can be used for:
60 * 0x60, 0x61, 0x62 */
61static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
62 .n_reg_rules = 5,
63 .alpha2 = "99",
64 .reg_rules = {
65 ATH9K_2GHZ_ALL,
66 ATH9K_5GHZ_ALL,
67 }
68};
69
70/* Can be used by 0x63 and 0x65 */
71static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
72 .n_reg_rules = 4,
73 .alpha2 = "99",
74 .reg_rules = {
75 ATH9K_2GHZ_CH01_11,
76 ATH9K_2GHZ_CH12_13,
77 ATH9K_5GHZ_NO_MIDBAND,
78 }
79};
80
81/* Can be used by 0x64 only */
82static const struct ieee80211_regdomain ath_world_regdom_64 = {
83 .n_reg_rules = 3,
84 .alpha2 = "99",
85 .reg_rules = {
86 ATH9K_2GHZ_CH01_11,
87 ATH9K_5GHZ_NO_MIDBAND,
88 }
89};
90
91/* Can be used by 0x66 and 0x69 */
92static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
93 .n_reg_rules = 3,
94 .alpha2 = "99",
95 .reg_rules = {
96 ATH9K_2GHZ_CH01_11,
97 ATH9K_5GHZ_ALL,
98 }
99};
100
101/* Can be used by 0x67, 0x6A and 0x68 */
102static const struct ieee80211_regdomain ath_world_regdom_67_68_6A = {
103 .n_reg_rules = 4,
104 .alpha2 = "99",
105 .reg_rules = {
106 ATH9K_2GHZ_CH01_11,
107 ATH9K_2GHZ_CH12_13,
108 ATH9K_5GHZ_ALL,
109 }
110};
111
112static inline bool is_wwr_sku(u16 regd)
113{
114 return ((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
115 (regd == WORLD);
116}
117
118static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
119{
120 return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
121}
122
123bool ath_is_world_regd(struct ath_regulatory *reg)
124{
125 return is_wwr_sku(ath_regd_get_eepromRD(reg));
126}
127EXPORT_SYMBOL(ath_is_world_regd);
128
129const struct ieee80211_regdomain *ath_default_world_regdomain(void)
130{
131 /* this is the most restrictive */
132 return &ath_world_regdom_64;
133}
134EXPORT_SYMBOL(ath_default_world_regdomain);
135
136const struct
137ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
138{
139 switch (reg->regpair->regDmnEnum) {
140 case 0x60:
141 case 0x61:
142 case 0x62:
143 return &ath_world_regdom_60_61_62;
144 case 0x63:
145 case 0x65:
146 return &ath_world_regdom_63_65;
147 case 0x64:
148 return &ath_world_regdom_64;
149 case 0x66:
150 case 0x69:
151 return &ath_world_regdom_66_69;
152 case 0x67:
153 case 0x68:
154 case 0x6A:
155 return &ath_world_regdom_67_68_6A;
156 default:
157 WARN_ON(1);
158 return ath_default_world_regdomain();
159 }
160}
161EXPORT_SYMBOL(ath_world_regdomain);
162
163/* Frequency is one where radar detection is required */
164static bool ath_is_radar_freq(u16 center_freq)
165{
166 return (center_freq >= 5260 && center_freq <= 5700);
167}
168
169/*
170 * N.B: These exception rules do not apply radar freqs.
171 *
172 * - We enable adhoc (or beaconing) if allowed by 11d
173 * - We enable active scan if the channel is allowed by 11d
174 * - If no country IE has been processed and a we determine we have
175 * received a beacon on a channel we can enable active scan and
176 * adhoc (or beaconing).
177 */
178static void ath_reg_apply_beaconing_flags(
179 struct wiphy *wiphy,
180 enum nl80211_reg_initiator initiator)
181{
182 enum ieee80211_band band;
183 struct ieee80211_supported_band *sband;
184 const struct ieee80211_reg_rule *reg_rule;
185 struct ieee80211_channel *ch;
186 unsigned int i;
187 u32 bandwidth = 0;
188 int r;
189
190 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
191
192 if (!wiphy->bands[band])
193 continue;
194
195 sband = wiphy->bands[band];
196
197 for (i = 0; i < sband->n_channels; i++) {
198
199 ch = &sband->channels[i];
200
201 if (ath_is_radar_freq(ch->center_freq) ||
202 (ch->flags & IEEE80211_CHAN_RADAR))
203 continue;
204
205 if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
206 r = freq_reg_info(wiphy, ch->center_freq,
207 &bandwidth, &reg_rule);
208 if (r)
209 continue;
210 /*
211 * If 11d had a rule for this channel ensure
212 * we enable adhoc/beaconing if it allows us to
213 * use it. Note that we would have disabled it
214 * by applying our static world regdomain by
215 * default during init, prior to calling our
216 * regulatory_hint().
217 */
218 if (!(reg_rule->flags &
219 NL80211_RRF_NO_IBSS))
220 ch->flags &=
221 ~IEEE80211_CHAN_NO_IBSS;
222 if (!(reg_rule->flags &
223 NL80211_RRF_PASSIVE_SCAN))
224 ch->flags &=
225 ~IEEE80211_CHAN_PASSIVE_SCAN;
226 } else {
227 if (ch->beacon_found)
228 ch->flags &= ~(IEEE80211_CHAN_NO_IBSS |
229 IEEE80211_CHAN_PASSIVE_SCAN);
230 }
231 }
232 }
233
234}
235
236/* Allows active scan scan on Ch 12 and 13 */
237static void ath_reg_apply_active_scan_flags(
238 struct wiphy *wiphy,
239 enum nl80211_reg_initiator initiator)
240{
241 struct ieee80211_supported_band *sband;
242 struct ieee80211_channel *ch;
243 const struct ieee80211_reg_rule *reg_rule;
244 u32 bandwidth = 0;
245 int r;
246
247 sband = wiphy->bands[IEEE80211_BAND_2GHZ];
248
249 /*
250 * If no country IE has been received always enable active scan
251 * on these channels. This is only done for specific regulatory SKUs
252 */
253 if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
254 ch = &sband->channels[11]; /* CH 12 */
255 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
256 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
257 ch = &sband->channels[12]; /* CH 13 */
258 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
259 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
260 return;
261 }
262
263 /*
264 * If a country IE has been recieved check its rule for this
265 * channel first before enabling active scan. The passive scan
266 * would have been enforced by the initial processing of our
267 * custom regulatory domain.
268 */
269
270 ch = &sband->channels[11]; /* CH 12 */
271 r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, &reg_rule);
272 if (!r) {
273 if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
274 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
275 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
276 }
277
278 ch = &sband->channels[12]; /* CH 13 */
279 r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, &reg_rule);
280 if (!r) {
281 if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
282 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
283 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
284 }
285}
286
287/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */
288void ath_reg_apply_radar_flags(struct wiphy *wiphy)
289{
290 struct ieee80211_supported_band *sband;
291 struct ieee80211_channel *ch;
292 unsigned int i;
293
294 if (!wiphy->bands[IEEE80211_BAND_5GHZ])
295 return;
296
297 sband = wiphy->bands[IEEE80211_BAND_5GHZ];
298
299 for (i = 0; i < sband->n_channels; i++) {
300 ch = &sband->channels[i];
301 if (!ath_is_radar_freq(ch->center_freq))
302 continue;
303 /* We always enable radar detection/DFS on this
304 * frequency range. Additionally we also apply on
305 * this frequency range:
306 * - If STA mode does not yet have DFS supports disable
307 * active scanning
308 * - If adhoc mode does not support DFS yet then
309 * disable adhoc in the frequency.
310 * - If AP mode does not yet support radar detection/DFS
311 * do not allow AP mode
312 */
313 if (!(ch->flags & IEEE80211_CHAN_DISABLED))
314 ch->flags |= IEEE80211_CHAN_RADAR |
315 IEEE80211_CHAN_NO_IBSS |
316 IEEE80211_CHAN_PASSIVE_SCAN;
317 }
318}
319EXPORT_SYMBOL(ath_reg_apply_radar_flags);
320
321void ath_reg_apply_world_flags(struct wiphy *wiphy,
322 enum nl80211_reg_initiator initiator,
323 struct ath_regulatory *reg)
324{
325 switch (reg->regpair->regDmnEnum) {
326 case 0x60:
327 case 0x63:
328 case 0x66:
329 case 0x67:
330 ath_reg_apply_beaconing_flags(wiphy, initiator);
331 break;
332 case 0x68:
333 ath_reg_apply_beaconing_flags(wiphy, initiator);
334 ath_reg_apply_active_scan_flags(wiphy, initiator);
335 break;
336 }
337 return;
338}
339EXPORT_SYMBOL(ath_reg_apply_world_flags);
340
341int ath_reg_notifier_apply(struct wiphy *wiphy,
342 struct regulatory_request *request, struct ath_regulatory *reg)
343{
344 /* We always apply this */
345 ath_reg_apply_radar_flags(wiphy);
346
347 switch (request->initiator) {
348 case NL80211_REGDOM_SET_BY_DRIVER:
349 case NL80211_REGDOM_SET_BY_CORE:
350 case NL80211_REGDOM_SET_BY_USER:
351 break;
352 case NL80211_REGDOM_SET_BY_COUNTRY_IE:
353 if (ath_is_world_regd(reg))
354 ath_reg_apply_world_flags(wiphy, request->initiator,
355 reg);
356 break;
357 }
358
359 return 0;
360}
361EXPORT_SYMBOL(ath_reg_notifier_apply);
362
363bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
364{
365 u16 rd = ath_regd_get_eepromRD(reg);
366 int i;
367
368 if (rd & COUNTRY_ERD_FLAG) {
369 /* EEPROM value is a country code */
370 u16 cc = rd & ~COUNTRY_ERD_FLAG;
371 for (i = 0; i < ARRAY_SIZE(allCountries); i++)
372 if (allCountries[i].countryCode == cc)
373 return true;
374 } else {
375 /* EEPROM value is a regpair value */
376 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
377 if (regDomainPairs[i].regDmnEnum == rd)
378 return true;
379 }
380 printk(KERN_DEBUG
381 "ath: invalid regulatory domain/country code 0x%x\n", rd);
382 return false;
383}
384EXPORT_SYMBOL(ath_regd_is_eeprom_valid);
385
386/* EEPROM country code to regpair mapping */
387static struct country_code_to_enum_rd*
388ath_regd_find_country(u16 countryCode)
389{
390 int i;
391
392 for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
393 if (allCountries[i].countryCode == countryCode)
394 return &allCountries[i];
395 }
396 return NULL;
397}
398
399/* EEPROM rd code to regpair mapping */
400static struct country_code_to_enum_rd*
401ath_regd_find_country_by_rd(int regdmn)
402{
403 int i;
404
405 for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
406 if (allCountries[i].regDmnEnum == regdmn)
407 return &allCountries[i];
408 }
409 return NULL;
410}
411
412/* Returns the map of the EEPROM set RD to a country code */
413static u16 ath_regd_get_default_country(u16 rd)
414{
415 if (rd & COUNTRY_ERD_FLAG) {
416 struct country_code_to_enum_rd *country = NULL;
417 u16 cc = rd & ~COUNTRY_ERD_FLAG;
418
419 country = ath_regd_find_country(cc);
420 if (country != NULL)
421 return cc;
422 }
423
424 return CTRY_DEFAULT;
425}
426
427static struct reg_dmn_pair_mapping*
428ath_get_regpair(int regdmn)
429{
430 int i;
431
432 if (regdmn == NO_ENUMRD)
433 return NULL;
434 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
435 if (regDomainPairs[i].regDmnEnum == regdmn)
436 return &regDomainPairs[i];
437 }
438 return NULL;
439}
440
441int ath_regd_init(struct ath_regulatory *reg)
442{
443 struct country_code_to_enum_rd *country = NULL;
444 u16 regdmn;
445
446 if (!ath_regd_is_eeprom_valid(reg)) {
447 printk(KERN_DEBUG "ath: Invalid EEPROM contents\n");
448 return -EINVAL;
449 }
450
451 regdmn = ath_regd_get_eepromRD(reg);
452 reg->country_code = ath_regd_get_default_country(regdmn);
453
454 if (reg->country_code == CTRY_DEFAULT &&
455 regdmn == CTRY_DEFAULT)
456 reg->country_code = CTRY_UNITED_STATES;
457
458 if (reg->country_code == CTRY_DEFAULT) {
459 country = NULL;
460 } else {
461 country = ath_regd_find_country(reg->country_code);
462 if (country == NULL) {
463 printk(KERN_DEBUG
464 "ath: Country is NULL!!!!, cc= %d\n",
465 reg->country_code);
466 return -EINVAL;
467 } else
468 regdmn = country->regDmnEnum;
469 }
470
471 reg->regpair = ath_get_regpair(regdmn);
472
473 if (!reg->regpair) {
474 printk(KERN_DEBUG "ath: "
475 "No regulatory domain pair found, cannot continue\n");
476 return -EINVAL;
477 }
478
479 if (!country)
480 country = ath_regd_find_country_by_rd(regdmn);
481
482 if (country) {
483 reg->alpha2[0] = country->isoName[0];
484 reg->alpha2[1] = country->isoName[1];
485 } else {
486 reg->alpha2[0] = '0';
487 reg->alpha2[1] = '0';
488 }
489
490 printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
491 reg->alpha2[0], reg->alpha2[1]);
492 printk(KERN_DEBUG "ath: Regpair detected: 0x%0x\n",
493 reg->regpair->regDmnEnum);
494
495 return 0;
496}
497EXPORT_SYMBOL(ath_regd_init);
498
499u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
500 enum ieee80211_band band)
501{
502 if (!reg->regpair ||
503 (reg->country_code == CTRY_DEFAULT &&
504 is_wwr_sku(ath_regd_get_eepromRD(reg)))) {
505 return SD_NO_CTL;
506 }
507
508 switch (band) {
509 case IEEE80211_BAND_2GHZ:
510 return reg->regpair->reg_2ghz_ctl;
511 case IEEE80211_BAND_5GHZ:
512 return reg->regpair->reg_5ghz_ctl;
513 default:
514 return NO_CTL;
515 }
516
517 return NO_CTL;
518}
519EXPORT_SYMBOL(ath_regd_get_band_ctl);