Branch data Line data Source code
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 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18 : :
19 : : #include <linux/kernel.h>
20 : : #include <linux/export.h>
21 : : #include <net/cfg80211.h>
22 : : #include <net/mac80211.h>
23 : : #include "regd.h"
24 : : #include "regd_common.h"
25 : :
26 : : static int __ath_regd_init(struct ath_regulatory *reg);
27 : :
28 : : /*
29 : : * This is a set of common rules used by our world regulatory domains.
30 : : * We have 12 world regulatory domains. To save space we consolidate
31 : : * the regulatory domains in 5 structures by frequency and change
32 : : * the flags on our reg_notifier() on a case by case basis.
33 : : */
34 : :
35 : : /* Only these channels all allow active scan on all world regulatory domains */
36 : : #define ATH_2GHZ_CH01_11 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
37 : :
38 : : /* We enable active scan on these a case by case basis by regulatory domain */
39 : : #define ATH_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\
40 : : NL80211_RRF_NO_IR)
41 : : #define ATH_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\
42 : : NL80211_RRF_NO_IR | \
43 : : NL80211_RRF_NO_OFDM)
44 : :
45 : : /* We allow IBSS on these on a case by case basis by regulatory domain */
46 : : #define ATH_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\
47 : : NL80211_RRF_NO_IR)
48 : : #define ATH_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\
49 : : NL80211_RRF_NO_IR)
50 : : #define ATH_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\
51 : : NL80211_RRF_NO_IR)
52 : :
53 : : #define ATH_2GHZ_ALL ATH_2GHZ_CH01_11, \
54 : : ATH_2GHZ_CH12_13, \
55 : : ATH_2GHZ_CH14
56 : :
57 : : #define ATH_5GHZ_ALL ATH_5GHZ_5150_5350, \
58 : : ATH_5GHZ_5470_5850
59 : :
60 : : /* This one skips what we call "mid band" */
61 : : #define ATH_5GHZ_NO_MIDBAND ATH_5GHZ_5150_5350, \
62 : : ATH_5GHZ_5725_5850
63 : :
64 : : /* Can be used for:
65 : : * 0x60, 0x61, 0x62 */
66 : : static const struct ieee80211_regdomain ath_world_regdom_60_61_62 = {
67 : : .n_reg_rules = 5,
68 : : .alpha2 = "99",
69 : : .reg_rules = {
70 : : ATH_2GHZ_ALL,
71 : : ATH_5GHZ_ALL,
72 : : }
73 : : };
74 : :
75 : : /* Can be used by 0x63 and 0x65 */
76 : : static const struct ieee80211_regdomain ath_world_regdom_63_65 = {
77 : : .n_reg_rules = 4,
78 : : .alpha2 = "99",
79 : : .reg_rules = {
80 : : ATH_2GHZ_CH01_11,
81 : : ATH_2GHZ_CH12_13,
82 : : ATH_5GHZ_NO_MIDBAND,
83 : : }
84 : : };
85 : :
86 : : /* Can be used by 0x64 only */
87 : : static const struct ieee80211_regdomain ath_world_regdom_64 = {
88 : : .n_reg_rules = 3,
89 : : .alpha2 = "99",
90 : : .reg_rules = {
91 : : ATH_2GHZ_CH01_11,
92 : : ATH_5GHZ_NO_MIDBAND,
93 : : }
94 : : };
95 : :
96 : : /* Can be used by 0x66 and 0x69 */
97 : : static const struct ieee80211_regdomain ath_world_regdom_66_69 = {
98 : : .n_reg_rules = 3,
99 : : .alpha2 = "99",
100 : : .reg_rules = {
101 : : ATH_2GHZ_CH01_11,
102 : : ATH_5GHZ_ALL,
103 : : }
104 : : };
105 : :
106 : : /* Can be used by 0x67, 0x68, 0x6A and 0x6C */
107 : : static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = {
108 : : .n_reg_rules = 4,
109 : : .alpha2 = "99",
110 : : .reg_rules = {
111 : : ATH_2GHZ_CH01_11,
112 : : ATH_2GHZ_CH12_13,
113 : : ATH_5GHZ_ALL,
114 : : }
115 : : };
116 : :
117 : : static bool dynamic_country_user_possible(struct ath_regulatory *reg)
118 : : {
119 : : if (IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING))
120 : : return true;
121 : :
122 : : switch (reg->country_code) {
123 : : case CTRY_UNITED_STATES:
124 : : case CTRY_JAPAN1:
125 : : case CTRY_JAPAN2:
126 : : case CTRY_JAPAN3:
127 : : case CTRY_JAPAN4:
128 : : case CTRY_JAPAN5:
129 : : case CTRY_JAPAN6:
130 : : case CTRY_JAPAN7:
131 : : case CTRY_JAPAN8:
132 : : case CTRY_JAPAN9:
133 : : case CTRY_JAPAN10:
134 : : case CTRY_JAPAN11:
135 : : case CTRY_JAPAN12:
136 : : case CTRY_JAPAN13:
137 : : case CTRY_JAPAN14:
138 : : case CTRY_JAPAN15:
139 : : case CTRY_JAPAN16:
140 : : case CTRY_JAPAN17:
141 : : case CTRY_JAPAN18:
142 : : case CTRY_JAPAN19:
143 : : case CTRY_JAPAN20:
144 : : case CTRY_JAPAN21:
145 : : case CTRY_JAPAN22:
146 : : case CTRY_JAPAN23:
147 : : case CTRY_JAPAN24:
148 : : case CTRY_JAPAN25:
149 : : case CTRY_JAPAN26:
150 : : case CTRY_JAPAN27:
151 : : case CTRY_JAPAN28:
152 : : case CTRY_JAPAN29:
153 : : case CTRY_JAPAN30:
154 : : case CTRY_JAPAN31:
155 : : case CTRY_JAPAN32:
156 : : case CTRY_JAPAN33:
157 : : case CTRY_JAPAN34:
158 : : case CTRY_JAPAN35:
159 : : case CTRY_JAPAN36:
160 : : case CTRY_JAPAN37:
161 : : case CTRY_JAPAN38:
162 : : case CTRY_JAPAN39:
163 : : case CTRY_JAPAN40:
164 : : case CTRY_JAPAN41:
165 : : case CTRY_JAPAN42:
166 : : case CTRY_JAPAN43:
167 : : case CTRY_JAPAN44:
168 : : case CTRY_JAPAN45:
169 : : case CTRY_JAPAN46:
170 : : case CTRY_JAPAN47:
171 : : case CTRY_JAPAN48:
172 : : case CTRY_JAPAN49:
173 : : case CTRY_JAPAN50:
174 : : case CTRY_JAPAN51:
175 : : case CTRY_JAPAN52:
176 : : case CTRY_JAPAN53:
177 : : case CTRY_JAPAN54:
178 : : case CTRY_JAPAN55:
179 : : case CTRY_JAPAN56:
180 : : case CTRY_JAPAN57:
181 : : case CTRY_JAPAN58:
182 : : case CTRY_JAPAN59:
183 : : return false;
184 : : }
185 : :
186 : : return true;
187 : : }
188 : :
189 : : static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg)
190 : : {
191 : : if (!IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS))
192 : : return false;
193 : : if (!dynamic_country_user_possible(reg))
194 : : return false;
195 : : return true;
196 : : }
197 : :
198 : 97 : static inline bool is_wwr_sku(u16 regd)
199 : : {
200 [ - + - + : 97 : return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) &&
- + - + -
- - + ]
201 [ # # # # : 0 : (((regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
# # # # #
# # # ]
202 : : (regd == WORLD));
203 : : }
204 : :
205 : 109 : static u16 ath_regd_get_eepromRD(struct ath_regulatory *reg)
206 : : {
207 : 109 : return reg->current_rd & ~WORLDWIDE_ROAMING_FLAG;
208 : : }
209 : :
210 : 24 : bool ath_is_world_regd(struct ath_regulatory *reg)
211 : : {
212 [ - - + - ]: 6 : return is_wwr_sku(ath_regd_get_eepromRD(reg));
213 : : }
214 : : EXPORT_SYMBOL(ath_is_world_regd);
215 : :
216 : : static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
217 : : {
218 : : /* this is the most restrictive */
219 : : return &ath_world_regdom_64;
220 : : }
221 : :
222 : : static const struct
223 : : ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
224 : : {
225 : : switch (reg->regpair->reg_domain) {
226 : : case 0x60:
227 : : case 0x61:
228 : : case 0x62:
229 : : return &ath_world_regdom_60_61_62;
230 : : case 0x63:
231 : : case 0x65:
232 : : return &ath_world_regdom_63_65;
233 : : case 0x64:
234 : : return &ath_world_regdom_64;
235 : : case 0x66:
236 : : case 0x69:
237 : : return &ath_world_regdom_66_69;
238 : : case 0x67:
239 : : case 0x68:
240 : : case 0x6A:
241 : : case 0x6C:
242 : : return &ath_world_regdom_67_68_6A_6C;
243 : : default:
244 : : WARN_ON(1);
245 : : return ath_default_world_regdomain();
246 : : }
247 : : }
248 : :
249 : 0 : bool ath_is_49ghz_allowed(u16 regdomain)
250 : : {
251 : : /* possibly more */
252 : 0 : return regdomain == MKK9_MKKC;
253 : : }
254 : : EXPORT_SYMBOL(ath_is_49ghz_allowed);
255 : :
256 : : /* Frequency is one where radar detection is required */
257 : : static bool ath_is_radar_freq(u16 center_freq,
258 : : struct ath_regulatory *reg)
259 : :
260 : : {
261 : : if (reg->country_code == CTRY_INDIA)
262 : : return (center_freq >= 5500 && center_freq <= 5700);
263 : : return (center_freq >= 5260 && center_freq <= 5700);
264 : : }
265 : :
266 : : static void ath_force_clear_no_ir_chan(struct wiphy *wiphy,
267 : : struct ieee80211_channel *ch)
268 : : {
269 : : const struct ieee80211_reg_rule *reg_rule;
270 : :
271 : : reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq));
272 : : if (IS_ERR(reg_rule))
273 : : return;
274 : :
275 : : if (!(reg_rule->flags & NL80211_RRF_NO_IR))
276 : : if (ch->flags & IEEE80211_CHAN_NO_IR)
277 : : ch->flags &= ~IEEE80211_CHAN_NO_IR;
278 : : }
279 : :
280 : 0 : static void ath_force_clear_no_ir_freq(struct wiphy *wiphy, u16 center_freq)
281 : : {
282 : 0 : struct ieee80211_channel *ch;
283 : :
284 : 0 : ch = ieee80211_get_channel(wiphy, center_freq);
285 [ # # ]: 0 : if (!ch)
286 : : return;
287 : :
288 : 0 : ath_force_clear_no_ir_chan(wiphy, ch);
289 : : }
290 : :
291 : : static void ath_force_no_ir_chan(struct ieee80211_channel *ch)
292 : : {
293 : : ch->flags |= IEEE80211_CHAN_NO_IR;
294 : : }
295 : :
296 : : static void ath_force_no_ir_freq(struct wiphy *wiphy, u16 center_freq)
297 : : {
298 : : struct ieee80211_channel *ch;
299 : :
300 : : ch = ieee80211_get_channel(wiphy, center_freq);
301 : : if (!ch)
302 : : return;
303 : :
304 : : ath_force_no_ir_chan(ch);
305 : : }
306 : :
307 : : static void
308 : : __ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
309 : : struct ath_regulatory *reg,
310 : : enum nl80211_reg_initiator initiator,
311 : : struct ieee80211_channel *ch)
312 : : {
313 : : if (ath_is_radar_freq(ch->center_freq, reg) ||
314 : : (ch->flags & IEEE80211_CHAN_RADAR))
315 : : return;
316 : :
317 : : switch (initiator) {
318 : : case NL80211_REGDOM_SET_BY_COUNTRY_IE:
319 : : ath_force_clear_no_ir_chan(wiphy, ch);
320 : : break;
321 : : case NL80211_REGDOM_SET_BY_USER:
322 : : if (ath_reg_dyn_country_user_allow(reg))
323 : : ath_force_clear_no_ir_chan(wiphy, ch);
324 : : break;
325 : : default:
326 : : if (ch->beacon_found)
327 : : ch->flags &= ~IEEE80211_CHAN_NO_IR;
328 : : }
329 : : }
330 : :
331 : : /*
332 : : * These exception rules do not apply radar frequencies.
333 : : *
334 : : * - We enable initiating radiation if the country IE says its fine:
335 : : * - If no country IE has been processed and a we determine we have
336 : : * received a beacon on a channel we can enable initiating radiation.
337 : : */
338 : : static void
339 : 0 : ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
340 : : struct ath_regulatory *reg,
341 : : enum nl80211_reg_initiator initiator)
342 : : {
343 : 0 : enum nl80211_band band;
344 : 0 : struct ieee80211_supported_band *sband;
345 : 0 : struct ieee80211_channel *ch;
346 : 0 : unsigned int i;
347 : :
348 [ # # ]: 0 : for (band = 0; band < NUM_NL80211_BANDS; band++) {
349 [ # # ]: 0 : if (!wiphy->bands[band])
350 : 0 : continue;
351 : : sband = wiphy->bands[band];
352 [ # # ]: 0 : for (i = 0; i < sband->n_channels; i++) {
353 : 0 : ch = &sband->channels[i];
354 : 0 : __ath_reg_apply_beaconing_flags(wiphy, reg,
355 : : initiator, ch);
356 : : }
357 : : }
358 : 0 : }
359 : :
360 : : /**
361 : : * ath_reg_apply_ir_flags()
362 : : * @wiphy: the wiphy to use
363 : : * @initiator: the regulatory hint initiator
364 : : *
365 : : * If no country IE has been received always enable passive scan
366 : : * and no-ibss on these channels. This is only done for specific
367 : : * regulatory SKUs.
368 : : *
369 : : * If a country IE has been received check its rule for this
370 : : * channel first before enabling active scan. The passive scan
371 : : * would have been enforced by the initial processing of our
372 : : * custom regulatory domain.
373 : : */
374 : : static void
375 : : ath_reg_apply_ir_flags(struct wiphy *wiphy,
376 : : struct ath_regulatory *reg,
377 : : enum nl80211_reg_initiator initiator)
378 : : {
379 : : struct ieee80211_supported_band *sband;
380 : :
381 : : sband = wiphy->bands[NL80211_BAND_2GHZ];
382 : : if (!sband)
383 : : return;
384 : :
385 : : switch(initiator) {
386 : : case NL80211_REGDOM_SET_BY_COUNTRY_IE:
387 : : ath_force_clear_no_ir_freq(wiphy, 2467);
388 : : ath_force_clear_no_ir_freq(wiphy, 2472);
389 : : break;
390 : : case NL80211_REGDOM_SET_BY_USER:
391 : : if (!ath_reg_dyn_country_user_allow(reg))
392 : : break;
393 : : ath_force_clear_no_ir_freq(wiphy, 2467);
394 : : ath_force_clear_no_ir_freq(wiphy, 2472);
395 : : break;
396 : : default:
397 : : ath_force_no_ir_freq(wiphy, 2467);
398 : : ath_force_no_ir_freq(wiphy, 2472);
399 : : }
400 : : }
401 : :
402 : : /* Always apply Radar/DFS rules on freq range 5500 MHz - 5700 MHz */
403 : : static void ath_reg_apply_radar_flags(struct wiphy *wiphy,
404 : : struct ath_regulatory *reg)
405 : : {
406 : : struct ieee80211_supported_band *sband;
407 : : struct ieee80211_channel *ch;
408 : : unsigned int i;
409 : :
410 : : if (!wiphy->bands[NL80211_BAND_5GHZ])
411 : : return;
412 : :
413 : : sband = wiphy->bands[NL80211_BAND_5GHZ];
414 : :
415 : : for (i = 0; i < sband->n_channels; i++) {
416 : : ch = &sband->channels[i];
417 : : if (!ath_is_radar_freq(ch->center_freq, reg))
418 : : continue;
419 : : /* We always enable radar detection/DFS on this
420 : : * frequency range. Additionally we also apply on
421 : : * this frequency range:
422 : : * - If STA mode does not yet have DFS supports disable
423 : : * active scanning
424 : : * - If adhoc mode does not support DFS yet then
425 : : * disable adhoc in the frequency.
426 : : * - If AP mode does not yet support radar detection/DFS
427 : : * do not allow AP mode
428 : : */
429 : : if (!(ch->flags & IEEE80211_CHAN_DISABLED))
430 : : ch->flags |= IEEE80211_CHAN_RADAR |
431 : : IEEE80211_CHAN_NO_IR;
432 : : }
433 : : }
434 : :
435 : 6 : static void ath_reg_apply_world_flags(struct wiphy *wiphy,
436 : : enum nl80211_reg_initiator initiator,
437 : : struct ath_regulatory *reg)
438 : : {
439 [ - - + ]: 6 : switch (reg->regpair->reg_domain) {
440 : 0 : case 0x60:
441 : : case 0x63:
442 : : case 0x66:
443 : : case 0x67:
444 : : case 0x6C:
445 : 0 : ath_reg_apply_beaconing_flags(wiphy, reg, initiator);
446 : 0 : break;
447 : 0 : case 0x68:
448 : 0 : ath_reg_apply_beaconing_flags(wiphy, reg, initiator);
449 : 0 : ath_reg_apply_ir_flags(wiphy, reg, initiator);
450 : 0 : break;
451 : : default:
452 : : if (ath_reg_dyn_country_user_allow(reg))
453 : : ath_reg_apply_beaconing_flags(wiphy, reg, initiator);
454 : : }
455 : 6 : }
456 : :
457 : 0 : u16 ath_regd_find_country_by_name(char *alpha2)
458 : : {
459 : 0 : unsigned int i;
460 : :
461 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
462 [ # # ]: 0 : if (!memcmp(allCountries[i].isoName, alpha2, 2))
463 : 0 : return allCountries[i].countryCode;
464 : : }
465 : :
466 : : return -1;
467 : : }
468 : : EXPORT_SYMBOL(ath_regd_find_country_by_name);
469 : :
470 : 0 : static int __ath_reg_dyn_country(struct wiphy *wiphy,
471 : : struct ath_regulatory *reg,
472 : : struct regulatory_request *request)
473 : : {
474 : 0 : u16 country_code;
475 : :
476 [ # # # # ]: 0 : if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
477 : : !ath_is_world_regd(reg))
478 : : return -EINVAL;
479 : :
480 : 0 : country_code = ath_regd_find_country_by_name(request->alpha2);
481 [ # # ]: 0 : if (country_code == (u16) -1)
482 : : return -EINVAL;
483 : :
484 : 0 : reg->current_rd = COUNTRY_ERD_FLAG;
485 : 0 : reg->current_rd |= country_code;
486 : :
487 : 0 : __ath_regd_init(reg);
488 : :
489 : 0 : ath_reg_apply_world_flags(wiphy, request->initiator, reg);
490 : :
491 : 0 : return 0;
492 : : }
493 : :
494 : 0 : static void ath_reg_dyn_country(struct wiphy *wiphy,
495 : : struct ath_regulatory *reg,
496 : : struct regulatory_request *request)
497 : : {
498 [ # # ]: 0 : if (__ath_reg_dyn_country(wiphy, reg, request))
499 : : return;
500 : :
501 : 0 : printk(KERN_DEBUG "ath: regdomain 0x%0x "
502 : : "dynamically updated by %s\n",
503 : 0 : reg->current_rd,
504 : : reg_initiator_name(request->initiator));
505 : : }
506 : :
507 : 6 : void ath_reg_notifier_apply(struct wiphy *wiphy,
508 : : struct regulatory_request *request,
509 : : struct ath_regulatory *reg)
510 : : {
511 : 6 : struct ath_common *common = container_of(reg, struct ath_common,
512 : : regulatory);
513 : : /* We always apply this */
514 : 6 : ath_reg_apply_radar_flags(wiphy, reg);
515 : :
516 : : /*
517 : : * This would happen when we have sent a custom regulatory request
518 : : * a world regulatory domain and the scheduler hasn't yet processed
519 : : * any pending requests in the queue.
520 : : */
521 [ + - ]: 6 : if (!request)
522 : : return;
523 : :
524 : 6 : reg->region = request->dfs_region;
525 [ + - - ]: 6 : switch (request->initiator) {
526 : 6 : case NL80211_REGDOM_SET_BY_CORE:
527 : : /*
528 : : * If common->reg_world_copy is world roaming it means we *were*
529 : : * world roaming... so we now have to restore that data.
530 : : */
531 [ + - - + ]: 12 : if (!ath_is_world_regd(&common->reg_world_copy))
532 : : break;
533 : :
534 : 6 : memcpy(reg, &common->reg_world_copy,
535 : : sizeof(struct ath_regulatory));
536 : 6 : break;
537 : : case NL80211_REGDOM_SET_BY_DRIVER:
538 : : break;
539 : : case NL80211_REGDOM_SET_BY_USER:
540 : : if (ath_reg_dyn_country_user_allow(reg))
541 : : ath_reg_dyn_country(wiphy, reg, request);
542 : : break;
543 : 0 : case NL80211_REGDOM_SET_BY_COUNTRY_IE:
544 : 0 : ath_reg_dyn_country(wiphy, reg, request);
545 : 0 : break;
546 : : }
547 : 0 : }
548 : : EXPORT_SYMBOL(ath_reg_notifier_apply);
549 : :
550 : 6 : static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
551 : : {
552 : 6 : u16 rd = ath_regd_get_eepromRD(reg);
553 : 6 : int i;
554 : :
555 [ - + ]: 6 : if (rd & COUNTRY_ERD_FLAG) {
556 : : /* EEPROM value is a country code */
557 : 0 : u16 cc = rd & ~COUNTRY_ERD_FLAG;
558 : 0 : printk(KERN_DEBUG
559 : : "ath: EEPROM indicates we should expect "
560 : : "a country code\n");
561 [ # # ]: 0 : for (i = 0; i < ARRAY_SIZE(allCountries); i++)
562 [ # # ]: 0 : if (allCountries[i].countryCode == cc)
563 : : return true;
564 : : } else {
565 : : /* EEPROM value is a regpair value */
566 [ + - ]: 6 : if (rd != CTRY_DEFAULT)
567 : 6 : printk(KERN_DEBUG "ath: EEPROM indicates we "
568 : : "should expect a direct regpair map\n");
569 [ + - ]: 624 : for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
570 [ + + ]: 624 : if (regDomainPairs[i].reg_domain == rd)
571 : : return true;
572 : : }
573 : 0 : printk(KERN_DEBUG
574 : : "ath: invalid regulatory domain/country code 0x%x\n", rd);
575 : 0 : return false;
576 : : }
577 : :
578 : : /* EEPROM country code to regpair mapping */
579 : : static struct country_code_to_enum_rd*
580 : 0 : ath_regd_find_country(u16 countryCode)
581 : : {
582 : 0 : int i;
583 : :
584 [ # # # # ]: 0 : for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
585 [ # # # # ]: 0 : if (allCountries[i].countryCode == countryCode)
586 : 0 : return &allCountries[i];
587 : : }
588 : : return NULL;
589 : : }
590 : :
591 : : /* EEPROM rd code to regpair mapping */
592 : : static struct country_code_to_enum_rd*
593 : : ath_regd_find_country_by_rd(int regdmn)
594 : : {
595 : : int i;
596 : :
597 [ + + ]: 1164 : for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
598 [ - + ]: 1158 : if (allCountries[i].regDmnEnum == regdmn)
599 : 0 : return &allCountries[i];
600 : : }
601 : : return NULL;
602 : : }
603 : :
604 : : /* Returns the map of the EEPROM set RD to a country code */
605 : 6 : static u16 ath_regd_get_default_country(u16 rd)
606 : : {
607 : 6 : if (rd & COUNTRY_ERD_FLAG) {
608 : 0 : struct country_code_to_enum_rd *country = NULL;
609 : 0 : u16 cc = rd & ~COUNTRY_ERD_FLAG;
610 : :
611 : 0 : country = ath_regd_find_country(cc);
612 [ # # ]: 0 : if (country != NULL)
613 : 0 : return cc;
614 : : }
615 : :
616 : : return CTRY_DEFAULT;
617 : : }
618 : :
619 : : static struct reg_dmn_pair_mapping*
620 : 6 : ath_get_regpair(int regdmn)
621 : : {
622 : 6 : int i;
623 : :
624 : 6 : if (regdmn == NO_ENUMRD)
625 : : return NULL;
626 [ + - ]: 624 : for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
627 [ + + ]: 624 : if (regDomainPairs[i].reg_domain == regdmn)
628 : 6 : return ®DomainPairs[i];
629 : : }
630 : : return NULL;
631 : : }
632 : :
633 : : static int
634 : 6 : ath_regd_init_wiphy(struct ath_regulatory *reg,
635 : : struct wiphy *wiphy,
636 : : void (*reg_notifier)(struct wiphy *wiphy,
637 : : struct regulatory_request *request))
638 : : {
639 : 6 : const struct ieee80211_regdomain *regd;
640 : :
641 : 6 : wiphy->reg_notifier = reg_notifier;
642 : 6 : wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
643 : : REGULATORY_CUSTOM_REG;
644 : :
645 [ + - + - ]: 12 : if (ath_is_world_regd(reg)) {
646 : : /*
647 : : * Anything applied here (prior to wiphy registration) gets
648 : : * saved on the wiphy orig_* parameters
649 : : */
650 : 6 : regd = ath_world_regdomain(reg);
651 : 6 : wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_FOLLOW_POWER;
652 : : } else {
653 : : /*
654 : : * This gets applied in the case of the absence of CRDA,
655 : : * it's our own custom world regulatory domain, similar to
656 : : * cfg80211's but we enable passive scanning.
657 : : */
658 : : regd = ath_default_world_regdomain();
659 : : }
660 : :
661 : 6 : wiphy_apply_custom_regulatory(wiphy, regd);
662 : 6 : ath_reg_apply_radar_flags(wiphy, reg);
663 : 6 : ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg);
664 : 6 : return 0;
665 : : }
666 : :
667 : : /*
668 : : * Some users have reported their EEPROM programmed with
669 : : * 0x8000 or 0x0 set, this is not a supported regulatory
670 : : * domain but since we have more than one user with it we
671 : : * need a solution for them. We default to 0x64, which is
672 : : * the default Atheros world regulatory domain.
673 : : */
674 : 6 : static void ath_regd_sanitize(struct ath_regulatory *reg)
675 : : {
676 : 6 : if (reg->current_rd != COUNTRY_ERD_FLAG && reg->current_rd != 0)
677 : : return;
678 : 6 : printk(KERN_DEBUG "ath: EEPROM regdomain sanitized\n");
679 : 6 : reg->current_rd = 0x64;
680 : : }
681 : :
682 : 6 : static int __ath_regd_init(struct ath_regulatory *reg)
683 : : {
684 : 6 : struct country_code_to_enum_rd *country = NULL;
685 : 6 : u16 regdmn;
686 : :
687 [ + - ]: 6 : if (!reg)
688 : : return -EINVAL;
689 : :
690 [ + - ]: 6 : ath_regd_sanitize(reg);
691 : :
692 : 6 : printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd);
693 : :
694 [ - + ]: 6 : if (!ath_regd_is_eeprom_valid(reg)) {
695 : 0 : pr_err("Invalid EEPROM contents\n");
696 : 0 : return -EINVAL;
697 : : }
698 : :
699 : 6 : regdmn = ath_regd_get_eepromRD(reg);
700 [ - + ]: 6 : reg->country_code = ath_regd_get_default_country(regdmn);
701 : :
702 [ - + ]: 6 : if (reg->country_code == CTRY_DEFAULT &&
703 : : regdmn == CTRY_DEFAULT) {
704 : 0 : printk(KERN_DEBUG "ath: EEPROM indicates default "
705 : : "country code should be used\n");
706 : 0 : reg->country_code = CTRY_UNITED_STATES;
707 : : }
708 : :
709 [ - + ]: 6 : if (reg->country_code == CTRY_DEFAULT) {
710 : : country = NULL;
711 : : } else {
712 : 0 : printk(KERN_DEBUG "ath: doing EEPROM country->regdmn "
713 : : "map search\n");
714 : 0 : country = ath_regd_find_country(reg->country_code);
715 [ # # ]: 0 : if (country == NULL) {
716 : 0 : printk(KERN_DEBUG
717 : : "ath: no valid country maps found for "
718 : : "country code: 0x%0x\n",
719 : : reg->country_code);
720 : 0 : return -EINVAL;
721 : : } else {
722 : 0 : regdmn = country->regDmnEnum;
723 : 0 : printk(KERN_DEBUG "ath: country maps to "
724 : : "regdmn code: 0x%0x\n",
725 : : regdmn);
726 : : }
727 : : }
728 : :
729 [ + - ]: 6 : reg->regpair = ath_get_regpair(regdmn);
730 : :
731 [ - + ]: 6 : if (!reg->regpair) {
732 : 0 : printk(KERN_DEBUG "ath: "
733 : : "No regulatory domain pair found, cannot continue\n");
734 : 0 : return -EINVAL;
735 : : }
736 : :
737 [ + - ]: 6 : if (!country)
738 : : country = ath_regd_find_country_by_rd(regdmn);
739 : :
740 [ - + ]: 6 : if (country) {
741 : 0 : reg->alpha2[0] = country->isoName[0];
742 : 0 : reg->alpha2[1] = country->isoName[1];
743 : : } else {
744 : 6 : reg->alpha2[0] = '0';
745 : 6 : reg->alpha2[1] = '0';
746 : : }
747 : :
748 : 6 : printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
749 : 6 : reg->alpha2[0], reg->alpha2[1]);
750 : 6 : printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
751 : 6 : reg->regpair->reg_domain);
752 : :
753 : 6 : return 0;
754 : : }
755 : :
756 : : int
757 : 6 : ath_regd_init(struct ath_regulatory *reg,
758 : : struct wiphy *wiphy,
759 : : void (*reg_notifier)(struct wiphy *wiphy,
760 : : struct regulatory_request *request))
761 : : {
762 : 6 : struct ath_common *common = container_of(reg, struct ath_common,
763 : : regulatory);
764 : 6 : int r;
765 : :
766 : 6 : r = __ath_regd_init(reg);
767 [ + - ]: 6 : if (r)
768 : : return r;
769 : :
770 [ + - + - ]: 12 : if (ath_is_world_regd(reg))
771 : 6 : memcpy(&common->reg_world_copy, reg,
772 : : sizeof(struct ath_regulatory));
773 : :
774 : 6 : ath_regd_init_wiphy(reg, wiphy, reg_notifier);
775 : :
776 : 6 : return 0;
777 : : }
778 : : EXPORT_SYMBOL(ath_regd_init);
779 : :
780 : 73 : u32 ath_regd_get_band_ctl(struct ath_regulatory *reg,
781 : : enum nl80211_band band)
782 : : {
783 [ + - ]: 73 : if (!reg->regpair ||
784 [ + - - + ]: 146 : (reg->country_code == CTRY_DEFAULT &&
785 [ + - ]: 73 : is_wwr_sku(ath_regd_get_eepromRD(reg)))) {
786 : : return SD_NO_CTL;
787 : : }
788 : :
789 [ # # ]: 0 : if (ath_regd_get_eepromRD(reg) == CTRY_DEFAULT) {
790 [ # # ]: 0 : switch (reg->region) {
791 : : case NL80211_DFS_FCC:
792 : : return CTL_FCC;
793 : : case NL80211_DFS_ETSI:
794 : : return CTL_ETSI;
795 : : case NL80211_DFS_JP:
796 : : return CTL_MKK;
797 : : default:
798 : : break;
799 : : }
800 : 0 : }
801 : :
802 [ # # # ]: 0 : switch (band) {
803 : 0 : case NL80211_BAND_2GHZ:
804 : 0 : return reg->regpair->reg_2ghz_ctl;
805 : 0 : case NL80211_BAND_5GHZ:
806 : 0 : return reg->regpair->reg_5ghz_ctl;
807 : : default:
808 : : return NO_CTL;
809 : : }
810 : : }
811 : : EXPORT_SYMBOL(ath_regd_get_band_ctl);
|