1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/kernel.h>
18#include <linux/slab.h>
19#include <net/cfg80211.h>
20#include <net/mac80211.h>
21#include "regd.h"
22#include "regd_common.h"
23
24
25
26
27
28
29
30
31
32#define ATH9K_2GHZ_CH01_11 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
33
34
35#define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\
36 NL80211_RRF_PASSIVE_SCAN)
37#define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\
38 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM)
39
40
41#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 40, 0, 30,\
42 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
43#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 40, 0, 30,\
44 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
45#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 40, 0, 30,\
46 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
47
48#define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \
49 ATH9K_2GHZ_CH12_13, \
50 ATH9K_2GHZ_CH14
51
52#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \
53 ATH9K_5GHZ_5470_5850
54
55#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \
56 ATH9K_5GHZ_5725_5850
57
58
59
60static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
61 .n_reg_rules = 5,
62 .alpha2 = "99",
63 .reg_rules = {
64 ATH9K_2GHZ_ALL,
65 ATH9K_5GHZ_ALL,
66 }
67};
68
69
70static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
71 .n_reg_rules = 4,
72 .alpha2 = "99",
73 .reg_rules = {
74 ATH9K_2GHZ_CH01_11,
75 ATH9K_2GHZ_CH12_13,
76 ATH9K_5GHZ_NO_MIDBAND,
77 }
78};
79
80
81static const struct ieee80211_regdomain ath_world_regdom_64 = {
82 .n_reg_rules = 3,
83 .alpha2 = "99",
84 .reg_rules = {
85 ATH9K_2GHZ_CH01_11,
86 ATH9K_5GHZ_NO_MIDBAND,
87 }
88};
89
90
91static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
92 .n_reg_rules = 3,
93 .alpha2 = "99",
94 .reg_rules = {
95 ATH9K_2GHZ_CH01_11,
96 ATH9K_5GHZ_ALL,
97 }
98};
99
100
101static const struct ieee80211_regdomain ath_world_regdom_67_68_6A = {
102 .n_reg_rules = 4,
103 .alpha2 = "99",
104 .reg_rules = {
105 ATH9K_2GHZ_CH01_11,
106 ATH9K_2GHZ_CH12_13,
107 ATH9K_5GHZ_ALL,
108 }
109};
110
111static inline bool is_wwr_sku(u16 regd)
112{
113 return ((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
114 (regd == WORLD);
115}
116
117static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
118{
119 return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
120}
121
122bool ath_is_world_regd(struct ath_regulatory *reg)
123{
124 return is_wwr_sku(ath_regd_get_eepromRD(reg));
125}
126EXPORT_SYMBOL(ath_is_world_regd);
127
128static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
129{
130
131 return &ath_world_regdom_64;
132}
133
134static const struct
135ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
136{
137 switch (reg->regpair->regDmnEnum) {
138 case 0x60:
139 case 0x61:
140 case 0x62:
141 return &ath_world_regdom_60_61_62;
142 case 0x63:
143 case 0x65:
144 return &ath_world_regdom_63_65;
145 case 0x64:
146 return &ath_world_regdom_64;
147 case 0x66:
148 case 0x69:
149 return &ath_world_regdom_66_69;
150 case 0x67:
151 case 0x68:
152 case 0x6A:
153 return &ath_world_regdom_67_68_6A;
154 default:
155 WARN_ON(1);
156 return ath_default_world_regdomain();
157 }
158}
159
160
161static bool ath_is_radar_freq(u16 center_freq)
162{
163 return (center_freq >= 5260 && center_freq <= 5700);
164}
165
166
167
168
169
170
171
172
173
174
175static void
176ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
177 enum nl80211_reg_initiator initiator)
178{
179 enum ieee80211_band band;
180 struct ieee80211_supported_band *sband;
181 const struct ieee80211_reg_rule *reg_rule;
182 struct ieee80211_channel *ch;
183 unsigned int i;
184 u32 bandwidth = 0;
185 int r;
186
187 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
188
189 if (!wiphy->bands[band])
190 continue;
191
192 sband = wiphy->bands[band];
193
194 for (i = 0; i < sband->n_channels; i++) {
195
196 ch = &sband->channels[i];
197
198 if (ath_is_radar_freq(ch->center_freq) ||
199 (ch->flags & IEEE80211_CHAN_RADAR))
200 continue;
201
202 if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
203 r = freq_reg_info(wiphy,
204 ch->center_freq,
205 bandwidth,
206 ®_rule);
207 if (r)
208 continue;
209
210
211
212
213
214
215
216
217 if (!(reg_rule->flags &
218 NL80211_RRF_NO_IBSS))
219 ch->flags &=
220 ~IEEE80211_CHAN_NO_IBSS;
221 if (!(reg_rule->flags &
222 NL80211_RRF_PASSIVE_SCAN))
223 ch->flags &=
224 ~IEEE80211_CHAN_PASSIVE_SCAN;
225 } else {
226 if (ch->beacon_found)
227 ch->flags &= ~(IEEE80211_CHAN_NO_IBSS |
228 IEEE80211_CHAN_PASSIVE_SCAN);
229 }
230 }
231 }
232
233}
234
235
236static void
237ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
238 enum nl80211_reg_initiator initiator)
239{
240 struct ieee80211_supported_band *sband;
241 struct ieee80211_channel *ch;
242 const struct ieee80211_reg_rule *reg_rule;
243 u32 bandwidth = 0;
244 int r;
245
246 sband = wiphy->bands[IEEE80211_BAND_2GHZ];
247
248
249
250
251
252 if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
253 ch = &sband->channels[11];
254 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
255 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
256 ch = &sband->channels[12];
257 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
258 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
259 return;
260 }
261
262
263
264
265
266
267
268
269 ch = &sband->channels[11];
270 r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule);
271 if (!r) {
272 if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
273 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
274 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
275 }
276
277 ch = &sband->channels[12];
278 r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule);
279 if (!r) {
280 if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
281 if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
282 ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
283 }
284}
285
286
287static void ath_reg_apply_radar_flags(struct wiphy *wiphy)
288{
289 struct ieee80211_supported_band *sband;
290 struct ieee80211_channel *ch;
291 unsigned int i;
292
293 if (!wiphy->bands[IEEE80211_BAND_5GHZ])
294 return;
295
296 sband = wiphy->bands[IEEE80211_BAND_5GHZ];
297
298 for (i = 0; i < sband->n_channels; i++) {
299 ch = &sband->channels[i];
300 if (!ath_is_radar_freq(ch->center_freq))
301 continue;
302
303
304
305
306
307
308
309
310
311
312 if (!(ch->flags & IEEE80211_CHAN_DISABLED))
313 ch->flags |= IEEE80211_CHAN_RADAR |
314 IEEE80211_CHAN_NO_IBSS |
315 IEEE80211_CHAN_PASSIVE_SCAN;
316 }
317}
318
319static void ath_reg_apply_world_flags(struct wiphy *wiphy,
320 enum nl80211_reg_initiator initiator,
321 struct ath_regulatory *reg)
322{
323 switch (reg->regpair->regDmnEnum) {
324 case 0x60:
325 case 0x63:
326 case 0x66:
327 case 0x67:
328 ath_reg_apply_beaconing_flags(wiphy, initiator);
329 break;
330 case 0x68:
331 ath_reg_apply_beaconing_flags(wiphy, initiator);
332 ath_reg_apply_active_scan_flags(wiphy, initiator);
333 break;
334 }
335 return;
336}
337
338int ath_reg_notifier_apply(struct wiphy *wiphy,
339 struct regulatory_request *request,
340 struct ath_regulatory *reg)
341{
342
343 ath_reg_apply_radar_flags(wiphy);
344
345 switch (request->initiator) {
346 case NL80211_REGDOM_SET_BY_DRIVER:
347 case NL80211_REGDOM_SET_BY_CORE:
348 case NL80211_REGDOM_SET_BY_USER:
349 break;
350 case NL80211_REGDOM_SET_BY_COUNTRY_IE:
351 if (ath_is_world_regd(reg))
352 ath_reg_apply_world_flags(wiphy, request->initiator,
353 reg);
354 break;
355 }
356
357 return 0;
358}
359EXPORT_SYMBOL(ath_reg_notifier_apply);
360
361static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
362{
363 u16 rd = ath_regd_get_eepromRD(reg);
364 int i;
365
366 if (rd & COUNTRY_ERD_FLAG) {
367
368 u16 cc = rd & ~COUNTRY_ERD_FLAG;
369 printk(KERN_DEBUG
370 "ath: EEPROM indicates we should expect "
371 "a country code\n");
372 for (i = 0; i < ARRAY_SIZE(allCountries); i++)
373 if (allCountries[i].countryCode == cc)
374 return true;
375 } else {
376
377 if (rd != CTRY_DEFAULT)
378 printk(KERN_DEBUG "ath: EEPROM indicates we "
379 "should expect a direct regpair map\n");
380 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
381 if (regDomainPairs[i].regDmnEnum == rd)
382 return true;
383 }
384 printk(KERN_DEBUG
385 "ath: invalid regulatory domain/country code 0x%x\n", rd);
386 return false;
387}
388
389
390static struct country_code_to_enum_rd*
391ath_regd_find_country(u16 countryCode)
392{
393 int i;
394
395 for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
396 if (allCountries[i].countryCode == countryCode)
397 return &allCountries[i];
398 }
399 return NULL;
400}
401
402
403static struct country_code_to_enum_rd*
404ath_regd_find_country_by_rd(int regdmn)
405{
406 int i;
407
408 for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
409 if (allCountries[i].regDmnEnum == regdmn)
410 return &allCountries[i];
411 }
412 return NULL;
413}
414
415
416static u16 ath_regd_get_default_country(u16 rd)
417{
418 if (rd & COUNTRY_ERD_FLAG) {
419 struct country_code_to_enum_rd *country = NULL;
420 u16 cc = rd & ~COUNTRY_ERD_FLAG;
421
422 country = ath_regd_find_country(cc);
423 if (country != NULL)
424 return cc;
425 }
426
427 return CTRY_DEFAULT;
428}
429
430static struct reg_dmn_pair_mapping*
431ath_get_regpair(int regdmn)
432{
433 int i;
434
435 if (regdmn == NO_ENUMRD)
436 return NULL;
437 for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
438 if (regDomainPairs[i].regDmnEnum == regdmn)
439 return ®DomainPairs[i];
440 }
441 return NULL;
442}
443
444static int
445ath_regd_init_wiphy(struct ath_regulatory *reg,
446 struct wiphy *wiphy,
447 int (*reg_notifier)(struct wiphy *wiphy,
448 struct regulatory_request *request))
449{
450 const struct ieee80211_regdomain *regd;
451
452 wiphy->reg_notifier = reg_notifier;
453 wiphy->strict_regulatory = true;
454
455 if (ath_is_world_regd(reg)) {
456
457
458
459
460 regd = ath_world_regdomain(reg);
461 wiphy->custom_regulatory = true;
462 wiphy->strict_regulatory = false;
463 } else {
464
465
466
467
468
469 regd = ath_default_world_regdomain();
470 }
471 wiphy_apply_custom_regulatory(wiphy, regd);
472 ath_reg_apply_radar_flags(wiphy);
473 ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg);
474 return 0;
475}
476
477
478
479
480
481
482
483
484static void ath_regd_sanitize(struct ath_regulatory *reg)
485{
486 if (reg->current_rd != COUNTRY_ERD_FLAG)
487 return;
488 printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n");
489 reg->current_rd = 0x64;
490}
491
492int
493ath_regd_init(struct ath_regulatory *reg,
494 struct wiphy *wiphy,
495 int (*reg_notifier)(struct wiphy *wiphy,
496 struct regulatory_request *request))
497{
498 struct country_code_to_enum_rd *country = NULL;
499 u16 regdmn;
500
501 if (!reg)
502 return -EINVAL;
503
504 ath_regd_sanitize(reg);
505
506 printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd);
507
508 if (!ath_regd_is_eeprom_valid(reg)) {
509 printk(KERN_ERR "ath: Invalid EEPROM contents\n");
510 return -EINVAL;
511 }
512
513 regdmn = ath_regd_get_eepromRD(reg);
514 reg->country_code = ath_regd_get_default_country(regdmn);
515
516 if (reg->country_code == CTRY_DEFAULT &&
517 regdmn == CTRY_DEFAULT) {
518 printk(KERN_DEBUG "ath: EEPROM indicates default "
519 "country code should be used\n");
520 reg->country_code = CTRY_UNITED_STATES;
521 }
522
523 if (reg->country_code == CTRY_DEFAULT) {
524 country = NULL;
525 } else {
526 printk(KERN_DEBUG "ath: doing EEPROM country->regdmn "
527 "map search\n");
528 country = ath_regd_find_country(reg->country_code);
529 if (country == NULL) {
530 printk(KERN_DEBUG
531 "ath: no valid country maps found for "
532 "country code: 0x%0x\n",
533 reg->country_code);
534 return -EINVAL;
535 } else {
536 regdmn = country->regDmnEnum;
537 printk(KERN_DEBUG "ath: country maps to "
538 "regdmn code: 0x%0x\n",
539 regdmn);
540 }
541 }
542
543 reg->regpair = ath_get_regpair(regdmn);
544
545 if (!reg->regpair) {
546 printk(KERN_DEBUG "ath: "
547 "No regulatory domain pair found, cannot continue\n");
548 return -EINVAL;
549 }
550
551 if (!country)
552 country = ath_regd_find_country_by_rd(regdmn);
553
554 if (country) {
555 reg->alpha2[0] = country->isoName[0];
556 reg->alpha2[1] = country->isoName[1];
557 } else {
558 reg->alpha2[0] = '0';
559 reg->alpha2[1] = '0';
560 }
561
562 printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
563 reg->alpha2[0], reg->alpha2[1]);
564 printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
565 reg->regpair->regDmnEnum);
566
567 ath_regd_init_wiphy(reg, wiphy, reg_notifier);
568 return 0;
569}
570EXPORT_SYMBOL(ath_regd_init);
571
572u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
573 enum ieee80211_band band)
574{
575 if (!reg->regpair ||
576 (reg->country_code == CTRY_DEFAULT &&
577 is_wwr_sku(ath_regd_get_eepromRD(reg)))) {
578 return SD_NO_CTL;
579 }
580
581 switch (band) {
582 case IEEE80211_BAND_2GHZ:
583 return reg->regpair->reg_2ghz_ctl;
584 case IEEE80211_BAND_5GHZ:
585 return reg->regpair->reg_5ghz_ctl;
586 default:
587 return NO_CTL;
588 }
589}
590EXPORT_SYMBOL(ath_regd_get_band_ctl);
591