blob: 2c237469c234c36642bf123de12a0206cd58cca8 [file] [log] [blame]
Amar Singhale4f28ee2015-10-21 14:36:56 -07001/*
2 * Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * DOC: wlan_hdd_regulatory.c
30 *
31 * hdd regulatory implementation
32 */
33
34#include "qdf_types.h"
35#include "cds_reg_service.h"
Amar Singhal604ba6cf2016-07-27 15:29:51 -070036#include "cds_regdomain.h"
Amar Singhale4f28ee2015-10-21 14:36:56 -070037#include "qdf_trace.h"
38#include "sme_api.h"
39#include "wlan_hdd_main.h"
Amar Singhale4f28ee2015-10-21 14:36:56 -070040#include "wlan_hdd_regulatory.h"
41
42#define WORLD_SKU_MASK 0x00F0
43#define WORLD_SKU_PREFIX 0x0060
44#define REG_WAIT_TIME 50
45
46#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
47#define IEEE80211_CHAN_PASSIVE_SCAN IEEE80211_CHAN_NO_IR
48#define IEEE80211_CHAN_NO_IBSS IEEE80211_CHAN_NO_IR
49#endif
50
51#define REG_RULE_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
52
53#define REG_RULE_2467_2472 REG_RULE(2467-10, 2472+10, 40, 0, 20, \
Amar Singhale4f28ee2015-10-21 14:36:56 -070054 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
55
Amar Singhal1c944922016-03-23 18:46:49 -070056#define REG_RULE_2484 REG_RULE(2484-10, 2484+10, 20, 0, 20, \
57 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | \
58 NL80211_RRF_NO_OFDM)
59
60#define REG_RULE_5180_5320 REG_RULE(5180-10, 5320+10, 160, 0, 20, \
61 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
62
63#define REG_RULE_5500_5720 REG_RULE(5500-10, 5720+10, 160, 0, 20, \
Amar Singhale4f28ee2015-10-21 14:36:56 -070064 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
65
66#define REG_RULE_5745_5925 REG_RULE(5745-10, 5925+10, 80, 0, 20, \
67 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
68
69static bool init_by_driver;
70static bool init_by_reg_core;
71
72static const struct ieee80211_regdomain
73hdd_world_regrules_60_61_62 = {
74 .n_reg_rules = 6,
75 .alpha2 = "00",
76 .reg_rules = {
77 REG_RULE_2412_2462,
78 REG_RULE_2467_2472,
79 REG_RULE_2484,
80 REG_RULE_5180_5320,
81 REG_RULE_5500_5720,
82 REG_RULE_5745_5925,
83 }
84};
85
86static const struct ieee80211_regdomain
87hdd_world_regrules_63_65 = {
88 .n_reg_rules = 4,
89 .alpha2 = "00",
90 .reg_rules = {
91 REG_RULE_2412_2462,
92 REG_RULE_2467_2472,
93 REG_RULE_5180_5320,
94 REG_RULE_5745_5925,
95 }
96};
97
98static const struct ieee80211_regdomain
99hdd_world_regrules_64 = {
100 .n_reg_rules = 3,
101 .alpha2 = "00",
102 .reg_rules = {
103 REG_RULE_2412_2462,
104 REG_RULE_5180_5320,
105 REG_RULE_5745_5925,
106 }
107};
108
109static const struct ieee80211_regdomain
110hdd_world_regrules_66_69 = {
111 .n_reg_rules = 4,
112 .alpha2 = "00",
113 .reg_rules = {
114 REG_RULE_2412_2462,
115 REG_RULE_5180_5320,
116 REG_RULE_5500_5720,
117 REG_RULE_5745_5925,
118 }
119};
120
121static const struct ieee80211_regdomain
122hdd_world_regrules_67_68_6A_6C = {
123 .n_reg_rules = 5,
124 .alpha2 = "00",
125 .reg_rules = {
126 REG_RULE_2412_2462,
127 REG_RULE_2467_2472,
128 REG_RULE_5180_5320,
129 REG_RULE_5500_5720,
130 REG_RULE_5745_5925,
131 }
132};
133
134/**
135 * hdd_get_world_regrules() - get the appropriate world regrules
136 * @reg: regulatory data
137 *
138 * Return: regulatory rules ptr
139 */
140static const struct ieee80211_regdomain *hdd_get_world_regrules(
141struct regulatory *reg)
142{
Amar Singhal22995112016-01-22 10:42:33 -0800143 struct reg_dmn_pair *regpair =
144 (struct reg_dmn_pair *)reg->regpair;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700145
Amar Singhala7bb01b2016-01-27 11:31:59 -0800146 switch (regpair->reg_dmn_pair) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700147 case 0x60:
148 case 0x61:
149 case 0x62:
150 return &hdd_world_regrules_60_61_62;
151 case 0x63:
152 case 0x65:
153 return &hdd_world_regrules_63_65;
154 case 0x64:
155 return &hdd_world_regrules_64;
156 case 0x66:
157 case 0x69:
158 return &hdd_world_regrules_66_69;
159 case 0x67:
160 case 0x68:
161 case 0x6A:
162 case 0x6C:
163 return &hdd_world_regrules_67_68_6A_6C;
164 default:
165 hdd_warn("invalid world mode in BDF");
166 return &hdd_world_regrules_60_61_62;
167 }
168}
169
170/**
171 * hdd_is_world_regdomain() - whether world regdomain
172 * @reg_domain: integer regulatory domain
173 *
174 * Return: bool
175 */
Jeff Johnson87a24252016-10-05 16:20:52 -0700176static bool hdd_is_world_regdomain(uint32_t reg_domain)
Amar Singhale4f28ee2015-10-21 14:36:56 -0700177{
178 uint32_t temp_regd = reg_domain & ~WORLDWIDE_ROAMING_FLAG;
179
180 return ((temp_regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) &&
181 (((temp_regd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) ||
182 (temp_regd == WORLD));
183}
184
185
186/**
187 * hdd_update_regulatory_info() - update regulatory info
188 * @hdd_ctx: hdd context
189 *
190 * Return: void
191 */
192static void hdd_update_regulatory_info(hdd_context_t *hdd_ctx)
193{
194 uint32_t country_code;
195
196 country_code = cds_get_country_from_alpha2(hdd_ctx->reg.alpha2);
197
198 hdd_ctx->reg.reg_domain = COUNTRY_ERD_FLAG;
199 hdd_ctx->reg.reg_domain |= country_code;
200
201 cds_fill_some_regulatory_info(&hdd_ctx->reg);
202
203}
204
205/**
Abhishek Singh3e6172f2016-05-04 16:56:48 +0530206 * hdd_reset_global_reg_params - Reset global static reg params
207 *
208 * This function is helpful in static driver to reset
209 * the global params.
210 *
211 * Return: void
212 */
213void hdd_reset_global_reg_params(void)
214{
215 init_by_driver = false;
216 init_by_reg_core = false;
217}
218
219/**
Amar Singhale4f28ee2015-10-21 14:36:56 -0700220 * hdd_regulatory_wiphy_init() - regulatory wiphy init
221 * @hdd_ctx: hdd context
222 * @reg: regulatory data
223 * @wiphy: wiphy structure
224 *
225 * Return: void
226 */
227#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
228static void hdd_regulatory_wiphy_init(hdd_context_t *hdd_ctx,
229 struct regulatory *reg,
230 struct wiphy *wiphy)
231{
232 const struct ieee80211_regdomain *reg_rules;
Amar Singhalb7fe2612016-10-19 10:49:58 -0700233 int chan_num;
234 struct ieee80211_channel chan;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700235
236 if (hdd_is_world_regdomain(reg->reg_domain)) {
237 reg_rules = hdd_get_world_regrules(reg);
238 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
239 } else if (hdd_ctx->config->fRegChangeDefCountry) {
240 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
241 reg_rules = &hdd_world_regrules_60_61_62;
242 } else {
243 wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
244 reg_rules = &hdd_world_regrules_60_61_62;
245 }
246
247 /*
248 * save the original driver regulatory flags
249 */
250 hdd_ctx->reg.reg_flags = wiphy->regulatory_flags;
251 wiphy_apply_custom_regulatory(wiphy, reg_rules);
252
253 /*
Amar Singhalb7fe2612016-10-19 10:49:58 -0700254 * disable 2.4 Ghz channels that dont have 20 mhz bw
255 */
256 for (chan_num = 0;
257 chan_num < wiphy->bands[IEEE80211_BAND_2GHZ]->n_channels;
258 chan_num++) {
259 chan = wiphy->bands[IEEE80211_BAND_2GHZ]->channels[chan_num];
260 if (chan.flags & IEEE80211_CHAN_NO_20MHZ)
261 chan.flags |= IEEE80211_CHAN_DISABLED;
262 }
263
264 /*
Amar Singhale4f28ee2015-10-21 14:36:56 -0700265 * restore the driver regulatory flags since
266 * wiphy_apply_custom_regulatory may have
267 * changed them
268 */
269 wiphy->regulatory_flags = hdd_ctx->reg.reg_flags;
270
271}
272#else
273static void hdd_regulatory_wiphy_init(hdd_context_t *hdd_ctx,
274 struct regulatory *reg,
275 struct wiphy *wiphy)
276{
277 const struct ieee80211_regdomain *reg_rules;
278
279 if (hdd_is_world_regdomain(reg->reg_domain)) {
280 reg_rules = hdd_get_world_regrules(reg);
281 wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
282 } else if (hdd_ctx->config->fRegChangeDefCountry) {
283 wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
284 reg_rules = &hdd_world_regrules_60_61_62;
285 } else {
286 wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
287 reg_rules = &hdd_world_regrules_60_61_62;
288 }
289
290 /*
291 * save the original driver regulatory flags
292 */
293 hdd_ctx->reg.reg_flags = wiphy->flags;
294 wiphy_apply_custom_regulatory(wiphy, reg_rules);
295
296 /*
297 * restore the driver regulatory flags since
298 * wiphy_apply_custom_regulatory may have
299 * changed them
300 */
301 wiphy->flags = hdd_ctx->reg.reg_flags;
302
303}
304#endif
305
306/**
Amar Singhale4f28ee2015-10-21 14:36:56 -0700307 * is_wiphy_custom_regulatory() - is custom regulatory defined
308 * @wiphy: wiphy
309 *
310 * Return: int
311 */
312#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
313static int is_wiphy_custom_regulatory(struct wiphy *wiphy)
314{
315
316 return wiphy->regulatory_flags & REGULATORY_CUSTOM_REG;
317}
318#else
319static int is_wiphy_custom_regulatory(struct wiphy *wiphy)
320{
321 return wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY;
322}
323#endif
324
325
326/**
327 * hdd_modify_wiphy() - modify wiphy
328 * @wiphy: wiphy
329 * @chan: channel structure
330 *
331 * Return: void
332 */
333static void hdd_modify_wiphy(struct wiphy *wiphy,
334 struct ieee80211_channel *chan)
335{
336 const struct ieee80211_reg_rule *reg_rule;
337
338 if (is_wiphy_custom_regulatory(wiphy)) {
339 reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
340 if (!IS_ERR(reg_rule)) {
341 chan->flags &= ~IEEE80211_CHAN_DISABLED;
342
343 if (!(reg_rule->flags & NL80211_RRF_DFS)) {
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530344 hdd_info("Remove dfs restriction for %u",
345 chan->center_freq);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700346 chan->flags &= ~IEEE80211_CHAN_RADAR;
347 }
348
349 if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) {
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530350 hdd_info("Remove passive restriction for %u",
351 chan->center_freq);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700352 chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
353 }
354
355 if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) {
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530356 hdd_info("Remove no ibss restriction for %u",
357 chan->center_freq);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700358 chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
359 }
360
361 chan->max_power =
362 MBM_TO_DBM(reg_rule->power_rule.max_eirp);
363 }
364 }
365}
366
367/**
368 * hdd_process_regulatory_data() - process regulatory data
369 * @hdd_ctx: hdd context
370 * @wiphy: wiphy
371 * @reset: whether to reset channel data
372 *
373 * Return: void
374 */
375static void hdd_process_regulatory_data(hdd_context_t *hdd_ctx,
376 struct wiphy *wiphy,
377 bool reset)
378{
Amar Singhal388b3f02016-02-10 13:37:18 -0800379 int band_num;
380 int chan_num;
Amar Singhalb2cb3002016-08-11 16:10:36 -0700381 enum channel_enum chan_enum = CHAN_ENUM_1;
382 struct ieee80211_channel *wiphy_chan, *wiphy_chan_144 = NULL;
Amar Singhal388b3f02016-02-10 13:37:18 -0800383 struct regulatory_channel *cds_chan;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700384 uint8_t band_capability;
385
386 band_capability = hdd_ctx->config->nBandCapability;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700387 hdd_ctx->isVHT80Allowed = 0;
388
Amar Singhal388b3f02016-02-10 13:37:18 -0800389 for (band_num = 0; band_num < IEEE80211_NUM_BANDS; band_num++) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700390
Amar Singhal388b3f02016-02-10 13:37:18 -0800391 if (wiphy->bands[band_num] == NULL)
Amar Singhale4f28ee2015-10-21 14:36:56 -0700392 continue;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700393
Amar Singhal388b3f02016-02-10 13:37:18 -0800394 for (chan_num = 0;
395 chan_num < wiphy->bands[band_num]->n_channels;
396 chan_num++) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700397
Amar Singhal388b3f02016-02-10 13:37:18 -0800398 wiphy_chan =
399 &(wiphy->bands[band_num]->channels[chan_num]);
400 cds_chan = &(reg_channels[chan_enum]);
Amar Singhalb2cb3002016-08-11 16:10:36 -0700401 if (CHAN_ENUM_144 == chan_enum)
402 wiphy_chan_144 = wiphy_chan;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700403
Amar Singhal388b3f02016-02-10 13:37:18 -0800404 chan_enum++;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700405
406 if (!reset)
Amar Singhal388b3f02016-02-10 13:37:18 -0800407 hdd_modify_wiphy(wiphy, wiphy_chan);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700408
Amar Singhal388b3f02016-02-10 13:37:18 -0800409 if (wiphy_chan->flags & IEEE80211_CHAN_DISABLED) {
410 cds_chan->state = CHANNEL_STATE_DISABLE;
Amar Singhal18517882016-08-08 12:26:20 -0700411 } else if ((wiphy_chan->flags &
412 (IEEE80211_CHAN_RADAR |
413 IEEE80211_CHAN_PASSIVE_SCAN)) ||
414 ((hdd_ctx->config->indoor_channel_support
415 == false) &&
416 (wiphy_chan->flags &
417 IEEE80211_CHAN_INDOOR_ONLY))) {
418 if ((wiphy_chan->flags &
419 IEEE80211_CHAN_INDOOR_ONLY) &&
420 (false ==
421 hdd_ctx->config->indoor_channel_support))
Amar Singhal388b3f02016-02-10 13:37:18 -0800422 wiphy_chan->flags |=
Amar Singhale4f28ee2015-10-21 14:36:56 -0700423 IEEE80211_CHAN_PASSIVE_SCAN;
Amar Singhal388b3f02016-02-10 13:37:18 -0800424 cds_chan->state = CHANNEL_STATE_DFS;
425 if ((wiphy_chan->flags &
Amar Singhale4f28ee2015-10-21 14:36:56 -0700426 IEEE80211_CHAN_NO_80MHZ) == 0)
427 hdd_ctx->isVHT80Allowed = 1;
428 } else {
Amar Singhal388b3f02016-02-10 13:37:18 -0800429 cds_chan->state = CHANNEL_STATE_ENABLE;
430 if ((wiphy_chan->flags &
Amar Singhale4f28ee2015-10-21 14:36:56 -0700431 IEEE80211_CHAN_NO_80MHZ) == 0)
432 hdd_ctx->isVHT80Allowed = 1;
433 }
Amar Singhal388b3f02016-02-10 13:37:18 -0800434 cds_chan->pwr_limit = wiphy_chan->max_power;
435 cds_chan->flags = wiphy_chan->flags;
Amar Singhal1c944922016-03-23 18:46:49 -0700436
Amar Singhale4f28ee2015-10-21 14:36:56 -0700437 }
438 }
439
440 if (0 == (hdd_ctx->reg.eeprom_rd_ext &
441 (1 << WHAL_REG_EXT_FCC_CH_144))) {
Amar Singhal388b3f02016-02-10 13:37:18 -0800442 cds_chan = &(reg_channels[CHAN_ENUM_144]);
443 cds_chan->state = CHANNEL_STATE_DISABLE;
Amar Singhalb2cb3002016-08-11 16:10:36 -0700444 if (NULL != wiphy_chan_144)
445 wiphy_chan_144->flags |= IEEE80211_CHAN_DISABLED;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700446 }
Amar Singhal6842e8f2016-02-23 16:30:32 -0800447
448 wlan_hdd_cfg80211_update_band(wiphy, band_capability);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700449}
450
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700451/**
452 * hdd_set_dfs_region() - set the dfs_region
453 * @dfs_region: the dfs_region to set
454 *
455 * Return: void
456 */
457#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
458static void hdd_set_dfs_region(hdd_context_t *hdd_ctx,
459 enum dfs_region dfs_reg)
460{
461 cds_put_dfs_region(dfs_reg);
462}
463#else
464static void hdd_set_dfs_region(hdd_context_t *hdd_ctx,
465 enum dfs_region dfs_reg)
466{
467
468 /* remap the ctl code to dfs region code */
469 switch (hdd_ctx->reg.ctl_5g) {
470 case FCC:
471 cds_put_dfs_region(DFS_FCC_REGION);
472 break;
473 case ETSI:
474 cds_put_dfs_region(DFS_ETSI_REGION);
475 break;
476 case MKK:
477 cds_put_dfs_region(DFS_MKK_REGION);
478 break;
479 default:
480 /* set default dfs_region to FCC */
481 cds_put_dfs_region(DFS_FCC_REGION);
482 break;
483 }
484
485}
486#endif
487
Amar Singhale4f28ee2015-10-21 14:36:56 -0700488
489/**
490 * hdd_regulatory_init() - regulatory_init
491 * @hdd_ctx: hdd context
492 * @wiphy: wiphy
493 *
494 * Return: int
495 */
496int hdd_regulatory_init(hdd_context_t *hdd_ctx, struct wiphy *wiphy)
497{
498 int ret_val;
499 struct regulatory *reg_info;
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700500 enum dfs_region dfs_reg;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700501
502 reg_info = &hdd_ctx->reg;
503
Amar Singhale4f28ee2015-10-21 14:36:56 -0700504 ret_val = cds_fill_some_regulatory_info(reg_info);
505 if (ret_val) {
506 hdd_err("incorrect BDF regulatory data");
507 return ret_val;
508 }
509
Dustin Brown38f2b552016-10-04 13:57:46 -0700510 hdd_regulatory_wiphy_init(hdd_ctx, reg_info, wiphy);
511
512 hdd_process_regulatory_data(hdd_ctx, wiphy, true);
513
514 reg_info->cc_src = SOURCE_DRIVER;
515
Amar Singhale4f28ee2015-10-21 14:36:56 -0700516 cds_put_default_country(reg_info->alpha2);
517
518 init_completion(&hdd_ctx->reg_init);
519
520 cds_fill_and_send_ctl_to_fw(reg_info);
521
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700522 hdd_set_dfs_region(hdd_ctx, DFS_FCC_REGION);
523 cds_get_dfs_region(&dfs_reg);
524 cds_set_wma_dfs_region(dfs_reg);
525
Amar Singhale4f28ee2015-10-21 14:36:56 -0700526 return 0;
527}
528
529/**
530 * hdd_program_country_code() - process channel information from country code
531 * @hdd_ctx: hddc context
532 *
533 * Return: void
534 */
535void hdd_program_country_code(hdd_context_t *hdd_ctx)
536{
537 struct wiphy *wiphy = hdd_ctx->wiphy;
538 uint8_t *country_alpha2 = hdd_ctx->reg.alpha2;
539
540 if (false == init_by_reg_core) {
541 init_by_driver = true;
542 if (('0' != country_alpha2[0]) ||
543 ('0' != country_alpha2[1])) {
544 INIT_COMPLETION(hdd_ctx->reg_init);
545 regulatory_hint(wiphy, country_alpha2);
546 wait_for_completion_timeout(&hdd_ctx->reg_init,
547 msecs_to_jiffies(REG_WAIT_TIME));
548 }
549 }
550}
551
552
553/**
Amar Singhale4f28ee2015-10-21 14:36:56 -0700554 * hdd_restore_custom_reg_settings() - restore custom reg settings
555 * @wiphy: wiphy structure
556 * @country_alpha2: alpha2 of the country
557 * @reset: whether wiphy is reset
558 *
559 * Return: void
560 */
561#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
562static void hdd_restore_custom_reg_settings(struct wiphy *wiphy,
563 uint8_t *country_alpha2,
564 bool *reset)
565{
566}
567#else
568static void hdd_restore_custom_reg_settings(struct wiphy *wiphy,
569 uint8_t *country_alpha2,
570 bool *reset)
571{
572 struct ieee80211_supported_band *sband;
573 enum ieee80211_band band;
574 struct ieee80211_channel *chan;
575 int i;
576
577 if ((country_alpha2[0] == '0') &&
578 (country_alpha2[1] == '0') &&
579 (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) {
580
581 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
582 sband = wiphy->bands[band];
583 if (!sband)
584 continue;
585 for (i = 0; i < sband->n_channels; i++) {
586 chan = &sband->channels[i];
587 chan->flags = chan->orig_flags;
588 chan->max_antenna_gain = chan->orig_mag;
589 chan->max_power = chan->orig_mpwr;
590 }
591 }
592 *reset = true;
593 }
594}
595#endif
596
597
598/**
599 * hdd_restore_reg_flags() - restore regulatory flags
600 * @flags: regulatory flags
601 *
602 * Return: void
603 */
604#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
605static void hdd_restore_reg_flags(struct wiphy *wiphy, uint32_t flags)
606{
607 wiphy->regulatory_flags = flags;
608}
609#else
610static void hdd_restore_reg_flags(struct wiphy *wiphy, uint32_t flags)
611{
612 wiphy->flags = flags;
613}
614#endif
615
616
617/**
618 * hdd_reg_notifier() - regulatory notifier
619 * @wiphy: wiphy
620 * @request: regulatory request
621 *
622 * Return: void
623 */
624void hdd_reg_notifier(struct wiphy *wiphy,
625 struct regulatory_request *request)
626{
627 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700628 bool vht80_allowed;
629 bool reset = false;
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700630 enum dfs_region dfs_reg;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700631
632 hdd_info("country: %c%c, initiator %d, dfs_region: %d",
633 request->alpha2[0],
634 request->alpha2[1],
635 request->initiator,
636 request->dfs_region);
637
638 if (NULL == hdd_ctx) {
639 hdd_err("invalid hdd_ctx pointer");
640 return;
641 }
642
643 if (cds_is_driver_unloading() || cds_is_driver_recovering()) {
644 hdd_err("%s: unloading or ssr in progress, ignore",
645 __func__);
646 return;
647 }
648
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700649 if (('K' == request->alpha2[0]) &&
650 ('R' == request->alpha2[1]))
651 request->dfs_region = DFS_KR_REGION;
652
653 if (('C' == request->alpha2[0]) &&
654 ('N' == request->alpha2[1]))
655 request->dfs_region = DFS_CN_REGION;
656
Amar Singhale4f28ee2015-10-21 14:36:56 -0700657 /* first check if this callback is in response to the driver callback */
658
659 switch (request->initiator) {
660 case NL80211_REGDOM_SET_BY_DRIVER:
661 case NL80211_REGDOM_SET_BY_CORE:
662 case NL80211_REGDOM_SET_BY_USER:
663
664 if ((false == init_by_driver) &&
665 (false == init_by_reg_core)) {
666
667 if (NL80211_REGDOM_SET_BY_CORE == request->initiator)
668 return;
669 init_by_reg_core = true;
670 }
671
672 if ((NL80211_REGDOM_SET_BY_DRIVER == request->initiator) &&
673 (true == init_by_driver)) {
674
675 /*
676 * restore the driver regulatory flags since
677 * regulatory_hint may have
678 * changed them
679 */
680 hdd_restore_reg_flags(wiphy, hdd_ctx->reg.reg_flags);
681 }
682
683 if (NL80211_REGDOM_SET_BY_CORE == request->initiator) {
684 hdd_ctx->reg.cc_src = SOURCE_CORE;
685 if (is_wiphy_custom_regulatory(wiphy))
686 reset = true;
687 } else if (NL80211_REGDOM_SET_BY_DRIVER == request->initiator)
688 hdd_ctx->reg.cc_src = SOURCE_DRIVER;
689 else {
690 hdd_ctx->reg.cc_src = SOURCE_USERSPACE;
691 hdd_restore_custom_reg_settings(wiphy,
692 request->alpha2,
693 &reset);
694 }
695
696 hdd_ctx->reg.alpha2[0] = request->alpha2[0];
697 hdd_ctx->reg.alpha2[1] = request->alpha2[1];
698
699 hdd_update_regulatory_info(hdd_ctx);
700
701 vht80_allowed = hdd_ctx->isVHT80Allowed;
702
703 hdd_process_regulatory_data(hdd_ctx, wiphy, reset);
704
705 if (hdd_ctx->isVHT80Allowed != vht80_allowed)
706 hdd_checkandupdate_phymode(hdd_ctx);
707
708 if (NL80211_REGDOM_SET_BY_DRIVER == request->initiator)
709 complete(&hdd_ctx->reg_init);
710
711 sme_generic_change_country_code(hdd_ctx->hHal,
712 hdd_ctx->reg.alpha2);
713
714 cds_fill_and_send_ctl_to_fw(&hdd_ctx->reg);
715
716 hdd_set_dfs_region(hdd_ctx, request->dfs_region);
717
718 cds_get_dfs_region(&dfs_reg);
719 cds_set_wma_dfs_region(dfs_reg);
720 break;
721
722 default:
723 break;
724 }
725}