blob: daa6c7b34ad341d02f4e3afefd33c3baa0aeb690 [file] [log] [blame]
Amar Singhale4f28ee2015-10-21 14:36:56 -07001/*
Sandeep Puligilla985d1ee2017-01-04 16:41:39 -08002 * Copyright (c) 2014-2017 The Linux Foundation. All rights reserved.
Amar Singhale4f28ee2015-10-21 14:36:56 -07003 *
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"
Amar Singhal5f997862016-08-24 13:17:50 -070035#include "qdf_trace.h"
Amar Singhale4f28ee2015-10-21 14:36:56 -070036#include "wlan_hdd_main.h"
Amar Singhal6f8592b2017-04-26 14:31:58 -070037#include <wlan_osif_priv.h>
Amar Singhale4f28ee2015-10-21 14:36:56 -070038#include "wlan_hdd_regulatory.h"
Amar Singhal5cccafe2017-02-15 12:42:58 -080039#include <wlan_reg_ucfg_api.h>
Kiran Kumar Lokerea3de2262017-04-12 12:15:04 -070040#include "cds_regdomain.h"
Amar Singhale4f28ee2015-10-21 14:36:56 -070041
42#define REG_RULE_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 20, 0)
43
44#define REG_RULE_2467_2472 REG_RULE(2467-10, 2472+10, 40, 0, 20, \
Amar Singhale4f28ee2015-10-21 14:36:56 -070045 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
46
Amar Singhal1c944922016-03-23 18:46:49 -070047#define REG_RULE_2484 REG_RULE(2484-10, 2484+10, 20, 0, 20, \
48 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | \
49 NL80211_RRF_NO_OFDM)
50
51#define REG_RULE_5180_5320 REG_RULE(5180-10, 5320+10, 160, 0, 20, \
52 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
53
54#define REG_RULE_5500_5720 REG_RULE(5500-10, 5720+10, 160, 0, 20, \
Amar Singhale4f28ee2015-10-21 14:36:56 -070055 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
56
57#define REG_RULE_5745_5925 REG_RULE(5745-10, 5925+10, 80, 0, 20, \
58 NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS)
59
60static bool init_by_driver;
61static bool init_by_reg_core;
62
Kiran Kumar Lokerea3de2262017-04-12 12:15:04 -070063struct regulatory_channel reg_channels[NUM_CHANNELS];
64
Amar Singhale4f28ee2015-10-21 14:36:56 -070065static const struct ieee80211_regdomain
66hdd_world_regrules_60_61_62 = {
67 .n_reg_rules = 6,
68 .alpha2 = "00",
69 .reg_rules = {
70 REG_RULE_2412_2462,
71 REG_RULE_2467_2472,
72 REG_RULE_2484,
73 REG_RULE_5180_5320,
74 REG_RULE_5500_5720,
75 REG_RULE_5745_5925,
76 }
77};
78
79static const struct ieee80211_regdomain
80hdd_world_regrules_63_65 = {
81 .n_reg_rules = 4,
82 .alpha2 = "00",
83 .reg_rules = {
84 REG_RULE_2412_2462,
85 REG_RULE_2467_2472,
86 REG_RULE_5180_5320,
87 REG_RULE_5745_5925,
88 }
89};
90
91static const struct ieee80211_regdomain
92hdd_world_regrules_64 = {
93 .n_reg_rules = 3,
94 .alpha2 = "00",
95 .reg_rules = {
96 REG_RULE_2412_2462,
97 REG_RULE_5180_5320,
98 REG_RULE_5745_5925,
99 }
100};
101
102static const struct ieee80211_regdomain
103hdd_world_regrules_66_69 = {
104 .n_reg_rules = 4,
105 .alpha2 = "00",
106 .reg_rules = {
107 REG_RULE_2412_2462,
108 REG_RULE_5180_5320,
109 REG_RULE_5500_5720,
110 REG_RULE_5745_5925,
111 }
112};
113
114static const struct ieee80211_regdomain
115hdd_world_regrules_67_68_6A_6C = {
116 .n_reg_rules = 5,
117 .alpha2 = "00",
118 .reg_rules = {
119 REG_RULE_2412_2462,
120 REG_RULE_2467_2472,
121 REG_RULE_5180_5320,
122 REG_RULE_5500_5720,
123 REG_RULE_5745_5925,
124 }
125};
126
127/**
128 * hdd_get_world_regrules() - get the appropriate world regrules
129 * @reg: regulatory data
130 *
131 * Return: regulatory rules ptr
132 */
133static const struct ieee80211_regdomain *hdd_get_world_regrules(
Kiran Kumar Lokerea3de2262017-04-12 12:15:04 -0700134 struct regulatory *reg)
Amar Singhale4f28ee2015-10-21 14:36:56 -0700135{
Amar Singhal22995112016-01-22 10:42:33 -0800136 struct reg_dmn_pair *regpair =
137 (struct reg_dmn_pair *)reg->regpair;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700138
Amar Singhala7bb01b2016-01-27 11:31:59 -0800139 switch (regpair->reg_dmn_pair) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700140 case 0x60:
141 case 0x61:
142 case 0x62:
143 return &hdd_world_regrules_60_61_62;
144 case 0x63:
145 case 0x65:
146 return &hdd_world_regrules_63_65;
147 case 0x64:
148 return &hdd_world_regrules_64;
149 case 0x66:
150 case 0x69:
151 return &hdd_world_regrules_66_69;
152 case 0x67:
153 case 0x68:
154 case 0x6A:
155 case 0x6C:
156 return &hdd_world_regrules_67_68_6A_6C;
157 default:
158 hdd_warn("invalid world mode in BDF");
159 return &hdd_world_regrules_60_61_62;
160 }
161}
162
163/**
164 * hdd_is_world_regdomain() - whether world regdomain
165 * @reg_domain: integer regulatory domain
166 *
167 * Return: bool
168 */
Jeff Johnson87a24252016-10-05 16:20:52 -0700169static bool hdd_is_world_regdomain(uint32_t reg_domain)
Amar Singhale4f28ee2015-10-21 14:36:56 -0700170{
Amar Singhal5f997862016-08-24 13:17:50 -0700171 uint32_t temp_regd = reg_domain & ~WORLD_ROAMING_FLAG;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700172
Amar Singhal5f997862016-08-24 13:17:50 -0700173 return ((temp_regd & CTRY_FLAG) != CTRY_FLAG) &&
174 ((temp_regd & WORLD_ROAMING_MASK) ==
175 WORLD_ROAMING_PREFIX);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700176}
177
Amar Singhale4f28ee2015-10-21 14:36:56 -0700178/**
179 * hdd_update_regulatory_info() - update regulatory info
180 * @hdd_ctx: hdd context
181 *
182 * Return: void
183 */
184static void hdd_update_regulatory_info(hdd_context_t *hdd_ctx)
185{
186 uint32_t country_code;
187
188 country_code = cds_get_country_from_alpha2(hdd_ctx->reg.alpha2);
189
Amar Singhal5f997862016-08-24 13:17:50 -0700190 hdd_ctx->reg.reg_domain = CTRY_FLAG;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700191 hdd_ctx->reg.reg_domain |= country_code;
192
193 cds_fill_some_regulatory_info(&hdd_ctx->reg);
194
195}
196
197/**
Abhishek Singh3e6172f2016-05-04 16:56:48 +0530198 * hdd_reset_global_reg_params - Reset global static reg params
199 *
200 * This function is helpful in static driver to reset
201 * the global params.
202 *
203 * Return: void
204 */
205void hdd_reset_global_reg_params(void)
206{
207 init_by_driver = false;
208 init_by_reg_core = false;
209}
210
Amar Singhal5cccafe2017-02-15 12:42:58 -0800211static void reg_program_config_vars(hdd_context_t *hdd_ctx,
212 struct reg_config_vars *config_vars)
213{
214 config_vars->enable_11d_support = hdd_ctx->config->Is11dSupportEnabled;
215 config_vars->userspace_ctry_priority =
216 hdd_ctx->config->fSupplicantCountryCodeHasPriority;
217 config_vars->dfs_enabled = hdd_ctx->config->enableDFSChnlScan;
218 config_vars->indoor_chan_enabled =
219 hdd_ctx->config->indoor_channel_support;
220 config_vars->band_capability = hdd_ctx->config->nBandCapability;
221}
222
223
Abhishek Singh3e6172f2016-05-04 16:56:48 +0530224/**
Amar Singhale4f28ee2015-10-21 14:36:56 -0700225 * hdd_regulatory_wiphy_init() - regulatory wiphy init
226 * @hdd_ctx: hdd context
227 * @reg: regulatory data
228 * @wiphy: wiphy structure
229 *
230 * Return: void
231 */
232#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
233static void hdd_regulatory_wiphy_init(hdd_context_t *hdd_ctx,
234 struct regulatory *reg,
235 struct wiphy *wiphy)
236{
237 const struct ieee80211_regdomain *reg_rules;
Amar Singhalb7fe2612016-10-19 10:49:58 -0700238 int chan_num;
Amar Singhal6fc66382016-11-09 13:33:33 -0800239 struct ieee80211_channel *chan;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700240
241 if (hdd_is_world_regdomain(reg->reg_domain)) {
242 reg_rules = hdd_get_world_regrules(reg);
243 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
244 } else if (hdd_ctx->config->fRegChangeDefCountry) {
245 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
246 reg_rules = &hdd_world_regrules_60_61_62;
247 } else {
248 wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
249 reg_rules = &hdd_world_regrules_60_61_62;
250 }
251
252 /*
253 * save the original driver regulatory flags
254 */
255 hdd_ctx->reg.reg_flags = wiphy->regulatory_flags;
256 wiphy_apply_custom_regulatory(wiphy, reg_rules);
257
258 /*
Amar Singhalb7fe2612016-10-19 10:49:58 -0700259 * disable 2.4 Ghz channels that dont have 20 mhz bw
260 */
261 for (chan_num = 0;
Dustin Brown67f16952017-02-03 15:37:53 -0800262 chan_num < wiphy->bands[NL80211_BAND_2GHZ]->n_channels;
Amar Singhalb7fe2612016-10-19 10:49:58 -0700263 chan_num++) {
Dustin Brown67f16952017-02-03 15:37:53 -0800264 chan = &(wiphy->bands[NL80211_BAND_2GHZ]->channels[chan_num]);
Amar Singhal6fc66382016-11-09 13:33:33 -0800265 if (chan->flags & IEEE80211_CHAN_NO_20MHZ)
266 chan->flags |= IEEE80211_CHAN_DISABLED;
Amar Singhalb7fe2612016-10-19 10:49:58 -0700267 }
268
269 /*
Amar Singhale4f28ee2015-10-21 14:36:56 -0700270 * restore the driver regulatory flags since
271 * wiphy_apply_custom_regulatory may have
272 * changed them
273 */
274 wiphy->regulatory_flags = hdd_ctx->reg.reg_flags;
275
276}
277#else
278static void hdd_regulatory_wiphy_init(hdd_context_t *hdd_ctx,
279 struct regulatory *reg,
280 struct wiphy *wiphy)
281{
282 const struct ieee80211_regdomain *reg_rules;
283
284 if (hdd_is_world_regdomain(reg->reg_domain)) {
285 reg_rules = hdd_get_world_regrules(reg);
286 wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
287 } else if (hdd_ctx->config->fRegChangeDefCountry) {
288 wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
289 reg_rules = &hdd_world_regrules_60_61_62;
290 } else {
291 wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
292 reg_rules = &hdd_world_regrules_60_61_62;
293 }
294
295 /*
296 * save the original driver regulatory flags
297 */
298 hdd_ctx->reg.reg_flags = wiphy->flags;
299 wiphy_apply_custom_regulatory(wiphy, reg_rules);
300
301 /*
302 * restore the driver regulatory flags since
303 * wiphy_apply_custom_regulatory may have
304 * changed them
305 */
306 wiphy->flags = hdd_ctx->reg.reg_flags;
307
308}
309#endif
310
311/**
Amar Singhale4f28ee2015-10-21 14:36:56 -0700312 * is_wiphy_custom_regulatory() - is custom regulatory defined
313 * @wiphy: wiphy
314 *
315 * Return: int
316 */
317#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
318static int is_wiphy_custom_regulatory(struct wiphy *wiphy)
319{
320
321 return wiphy->regulatory_flags & REGULATORY_CUSTOM_REG;
322}
323#else
324static int is_wiphy_custom_regulatory(struct wiphy *wiphy)
325{
326 return wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY;
327}
328#endif
329
Amar Singhale4f28ee2015-10-21 14:36:56 -0700330/**
331 * hdd_modify_wiphy() - modify wiphy
332 * @wiphy: wiphy
333 * @chan: channel structure
334 *
335 * Return: void
336 */
337static void hdd_modify_wiphy(struct wiphy *wiphy,
338 struct ieee80211_channel *chan)
339{
340 const struct ieee80211_reg_rule *reg_rule;
341
342 if (is_wiphy_custom_regulatory(wiphy)) {
343 reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
344 if (!IS_ERR(reg_rule)) {
345 chan->flags &= ~IEEE80211_CHAN_DISABLED;
346
347 if (!(reg_rule->flags & NL80211_RRF_DFS)) {
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530348 hdd_info("Remove dfs restriction for %u",
349 chan->center_freq);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700350 chan->flags &= ~IEEE80211_CHAN_RADAR;
351 }
352
353 if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) {
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530354 hdd_info("Remove passive restriction for %u",
355 chan->center_freq);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700356 chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
357 }
358
359 if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) {
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530360 hdd_info("Remove no ibss restriction for %u",
361 chan->center_freq);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700362 chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
363 }
364
365 chan->max_power =
366 MBM_TO_DBM(reg_rule->power_rule.max_eirp);
367 }
368 }
369}
370
371/**
372 * hdd_process_regulatory_data() - process regulatory data
373 * @hdd_ctx: hdd context
374 * @wiphy: wiphy
375 * @reset: whether to reset channel data
376 *
377 * Return: void
378 */
379static void hdd_process_regulatory_data(hdd_context_t *hdd_ctx,
380 struct wiphy *wiphy,
381 bool reset)
382{
Amar Singhal388b3f02016-02-10 13:37:18 -0800383 int band_num;
384 int chan_num;
Amar Singhalb2cb3002016-08-11 16:10:36 -0700385 enum channel_enum chan_enum = CHAN_ENUM_1;
386 struct ieee80211_channel *wiphy_chan, *wiphy_chan_144 = NULL;
Amar Singhal388b3f02016-02-10 13:37:18 -0800387 struct regulatory_channel *cds_chan;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700388 uint8_t band_capability;
389
390 band_capability = hdd_ctx->config->nBandCapability;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700391
Dustin Browna30892e2016-10-12 17:28:36 -0700392 for (band_num = 0; band_num < NUM_NL80211_BANDS; band_num++) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700393
Amar Singhal388b3f02016-02-10 13:37:18 -0800394 if (wiphy->bands[band_num] == NULL)
Amar Singhale4f28ee2015-10-21 14:36:56 -0700395 continue;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700396
Amar Singhal388b3f02016-02-10 13:37:18 -0800397 for (chan_num = 0;
Manjeet Singhec402062016-10-27 15:58:01 +0530398 chan_num < wiphy->bands[band_num]->n_channels &&
399 chan_enum < NUM_CHANNELS;
Amar Singhal388b3f02016-02-10 13:37:18 -0800400 chan_num++) {
Amar Singhal388b3f02016-02-10 13:37:18 -0800401 wiphy_chan =
402 &(wiphy->bands[band_num]->channels[chan_num]);
403 cds_chan = &(reg_channels[chan_enum]);
Amar Singhal5cccafe2017-02-15 12:42:58 -0800404 cds_chan->chan_flags = 0;
Amar Singhalb2cb3002016-08-11 16:10:36 -0700405 if (CHAN_ENUM_144 == chan_enum)
406 wiphy_chan_144 = wiphy_chan;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700407
Amar Singhal388b3f02016-02-10 13:37:18 -0800408 chan_enum++;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700409
410 if (!reset)
Amar Singhal388b3f02016-02-10 13:37:18 -0800411 hdd_modify_wiphy(wiphy, wiphy_chan);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700412
Amar Singhal388b3f02016-02-10 13:37:18 -0800413 if (wiphy_chan->flags & IEEE80211_CHAN_DISABLED) {
414 cds_chan->state = CHANNEL_STATE_DISABLE;
Amar Singhal5cccafe2017-02-15 12:42:58 -0800415 cds_chan->chan_flags |=
416 REGULATORY_CHAN_DISABLED;
Amar Singhal6f8592b2017-04-26 14:31:58 -0700417 } else if (wiphy_chan->flags &
Amar Singhal18517882016-08-08 12:26:20 -0700418 (IEEE80211_CHAN_RADAR |
Amar Singhal6f8592b2017-04-26 14:31:58 -0700419 IEEE80211_CHAN_PASSIVE_SCAN)) {
Amar Singhal388b3f02016-02-10 13:37:18 -0800420 cds_chan->state = CHANNEL_STATE_DFS;
Amar Singhal5cccafe2017-02-15 12:42:58 -0800421 if (wiphy_chan->flags & IEEE80211_CHAN_RADAR)
422 cds_chan->chan_flags |=
423 REGULATORY_CHAN_RADAR;
424 if (wiphy_chan->flags &
425 IEEE80211_CHAN_PASSIVE_SCAN)
426 cds_chan->chan_flags |=
427 REGULATORY_CHAN_NO_IR;
Amar Singhal6f8592b2017-04-26 14:31:58 -0700428 } else if (wiphy_chan->flags &
429 IEEE80211_CHAN_INDOOR_ONLY) {
430 cds_chan->chan_flags |=
431 REGULATORY_CHAN_INDOOR_ONLY;
432 if (hdd_ctx->config->indoor_channel_support
433 == false) {
434 cds_chan->state = CHANNEL_STATE_DFS;
435 wiphy_chan->flags |=
436 IEEE80211_CHAN_PASSIVE_SCAN;
Amar Singhal5cccafe2017-02-15 12:42:58 -0800437 cds_chan->chan_flags |=
Amar Singhal6f8592b2017-04-26 14:31:58 -0700438 REGULATORY_CHAN_NO_IR;
439 } else
440 cds_chan->state = CHANNEL_STATE_ENABLE;
441 } else
Amar Singhal388b3f02016-02-10 13:37:18 -0800442 cds_chan->state = CHANNEL_STATE_ENABLE;
Amar Singhal5cccafe2017-02-15 12:42:58 -0800443 cds_chan->tx_power = wiphy_chan->max_power;
444 if (wiphy_chan->flags & IEEE80211_CHAN_NO_10MHZ)
445 cds_chan->max_bw = 5;
446 else if (wiphy_chan->flags & IEEE80211_CHAN_NO_20MHZ)
447 cds_chan->max_bw = 10;
Krunal Soni408690d2017-05-03 18:32:35 -0700448 /*
449 * IEEE80211_CHAN_NO_HT40 is defined as 0x30 in kernel
450 * 4th BIT representing IEEE80211_CHAN_NO_HT40PLUS
451 * 5th BIT representing IEEE80211_CHAN_NO_HT40MINUS
452 *
453 * In order to claim no 40Mhz support value of
454 * wiphy_chan->flags needs to be 0x30.
455 * 0x20 and 0x10 values shows that either HT40+ or
456 * HT40- is not supported based on BIT set but they
457 * can support 40Mhz Operation.
458 */
459 else if ((wiphy_chan->flags & IEEE80211_CHAN_NO_HT40) ==
460 IEEE80211_CHAN_NO_HT40)
Amar Singhal5cccafe2017-02-15 12:42:58 -0800461 cds_chan->max_bw = 20;
462 else if (wiphy_chan->flags & IEEE80211_CHAN_NO_80MHZ)
463 cds_chan->max_bw = 40;
464 else if (wiphy_chan->flags & IEEE80211_CHAN_NO_160MHZ)
465 cds_chan->max_bw = 80;
466 else
467 cds_chan->max_bw = 160;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700468 }
469 }
470
471 if (0 == (hdd_ctx->reg.eeprom_rd_ext &
Sandeep Puligilla985d1ee2017-01-04 16:41:39 -0800472 (1 << WMI_REG_EXT_FCC_CH_144))) {
Amar Singhal388b3f02016-02-10 13:37:18 -0800473 cds_chan = &(reg_channels[CHAN_ENUM_144]);
474 cds_chan->state = CHANNEL_STATE_DISABLE;
Amar Singhalb2cb3002016-08-11 16:10:36 -0700475 if (NULL != wiphy_chan_144)
476 wiphy_chan_144->flags |= IEEE80211_CHAN_DISABLED;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700477 }
Amar Singhal6842e8f2016-02-23 16:30:32 -0800478
Kiran Kumar Lokerea3de2262017-04-12 12:15:04 -0700479 wlan_hdd_cfg80211_update_band(hdd_ctx, wiphy, band_capability);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700480}
481
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700482/**
483 * hdd_set_dfs_region() - set the dfs_region
484 * @dfs_region: the dfs_region to set
485 *
486 * Return: void
487 */
488#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
489static void hdd_set_dfs_region(hdd_context_t *hdd_ctx,
Amar Singhal5cccafe2017-02-15 12:42:58 -0800490 enum dfs_reg dfs_reg)
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700491{
Amar Singhal5cccafe2017-02-15 12:42:58 -0800492 wlan_reg_set_dfs_region(hdd_ctx->hdd_psoc, dfs_reg);
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700493}
494#else
495static void hdd_set_dfs_region(hdd_context_t *hdd_ctx,
496 enum dfs_region dfs_reg)
497{
498
499 /* remap the ctl code to dfs region code */
500 switch (hdd_ctx->reg.ctl_5g) {
501 case FCC:
502 cds_put_dfs_region(DFS_FCC_REGION);
503 break;
504 case ETSI:
505 cds_put_dfs_region(DFS_ETSI_REGION);
506 break;
507 case MKK:
508 cds_put_dfs_region(DFS_MKK_REGION);
509 break;
510 default:
511 /* set default dfs_region to FCC */
512 cds_put_dfs_region(DFS_FCC_REGION);
513 break;
514 }
515
516}
517#endif
518
Amar Singhale4f28ee2015-10-21 14:36:56 -0700519/**
Amar Singhal5cccafe2017-02-15 12:42:58 -0800520 * hdd_regulatory_init_no_offload() - regulatory init
Amar Singhale4f28ee2015-10-21 14:36:56 -0700521 * @hdd_ctx: hdd context
522 * @wiphy: wiphy
523 *
524 * Return: int
525 */
Amar Singhal5cccafe2017-02-15 12:42:58 -0800526static int hdd_regulatory_init_no_offload(hdd_context_t *hdd_ctx,
527 struct wiphy *wiphy)
Amar Singhale4f28ee2015-10-21 14:36:56 -0700528{
529 int ret_val;
530 struct regulatory *reg_info;
Amar Singhal5cccafe2017-02-15 12:42:58 -0800531 enum dfs_reg dfs_reg;
532 struct reg_config_vars config_vars;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700533
534 reg_info = &hdd_ctx->reg;
535
Amar Singhale4f28ee2015-10-21 14:36:56 -0700536 ret_val = cds_fill_some_regulatory_info(reg_info);
537 if (ret_val) {
538 hdd_err("incorrect BDF regulatory data");
539 return ret_val;
540 }
541
Dustin Brown38f2b552016-10-04 13:57:46 -0700542 hdd_regulatory_wiphy_init(hdd_ctx, reg_info, wiphy);
543
544 hdd_process_regulatory_data(hdd_ctx, wiphy, true);
545
546 reg_info->cc_src = SOURCE_DRIVER;
547
Kiran Kumar Lokereb1d412e2017-04-23 17:19:43 -0700548 ucfg_reg_set_default_country(hdd_ctx->hdd_psoc, reg_info->alpha2);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700549
Amar Singhale4f28ee2015-10-21 14:36:56 -0700550 cds_fill_and_send_ctl_to_fw(reg_info);
551
Amar Singhal5cccafe2017-02-15 12:42:58 -0800552 hdd_set_dfs_region(hdd_ctx, DFS_FCC_REG);
553 wlan_reg_get_dfs_region(hdd_ctx->hdd_psoc, &dfs_reg);
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700554
Amar Singhal5cccafe2017-02-15 12:42:58 -0800555 reg_program_config_vars(hdd_ctx, &config_vars);
556 ucfg_reg_set_config_vars(hdd_ctx->hdd_psoc, config_vars);
557 ucfg_reg_program_mas_chan_list(hdd_ctx->hdd_psoc,
558 reg_channels,
559 hdd_ctx->reg.alpha2,
560 dfs_reg);
561
Amar Singhale4f28ee2015-10-21 14:36:56 -0700562 return 0;
563}
564
565/**
566 * hdd_program_country_code() - process channel information from country code
567 * @hdd_ctx: hddc context
568 *
569 * Return: void
570 */
571void hdd_program_country_code(hdd_context_t *hdd_ctx)
572{
573 struct wiphy *wiphy = hdd_ctx->wiphy;
574 uint8_t *country_alpha2 = hdd_ctx->reg.alpha2;
575
Abhishek Singh897b27a2017-03-17 11:56:46 +0530576 if (!init_by_reg_core && !init_by_driver) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700577 init_by_driver = true;
578 if (('0' != country_alpha2[0]) ||
Amar Singhal38c5eeb2016-08-22 16:50:01 -0700579 ('0' != country_alpha2[1]))
Amar Singhale4f28ee2015-10-21 14:36:56 -0700580 regulatory_hint(wiphy, country_alpha2);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700581 }
582}
583
Kiran Kumar Lokereb1d412e2017-04-23 17:19:43 -0700584int hdd_reg_set_country(hdd_context_t *hdd_ctx, char *country_code)
585{
586 int err;
Amar Singhal6f8592b2017-04-26 14:31:58 -0700587 QDF_STATUS status = QDF_STATUS_SUCCESS;
Kiran Kumar Lokereb1d412e2017-04-23 17:19:43 -0700588
589 /* validation */
590 err = wlan_hdd_validate_context(hdd_ctx);
591 if (err)
592 return err;
593
594 if (!country_code) {
595 hdd_err("country_code is null");
596 return -EINVAL;
597 }
598
Amar Singhal6f8592b2017-04-26 14:31:58 -0700599 if (hdd_ctx->reg_offload)
600 status = ucfg_reg_set_country(hdd_ctx->hdd_pdev, country_code);
601 else
602 regulatory_hint_user(country_code,
603 NL80211_USER_REG_HINT_USER);
604
Kiran Kumar Lokereb1d412e2017-04-23 17:19:43 -0700605 if (QDF_IS_STATUS_ERROR(status))
606 hdd_err("Failed to set country");
607
608 return qdf_status_to_os_return(status);
609}
Amar Singhale4f28ee2015-10-21 14:36:56 -0700610
611/**
Amar Singhale4f28ee2015-10-21 14:36:56 -0700612 * hdd_restore_custom_reg_settings() - restore custom reg settings
613 * @wiphy: wiphy structure
614 * @country_alpha2: alpha2 of the country
615 * @reset: whether wiphy is reset
616 *
617 * Return: void
618 */
619#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
620static void hdd_restore_custom_reg_settings(struct wiphy *wiphy,
621 uint8_t *country_alpha2,
622 bool *reset)
623{
624}
625#else
626static void hdd_restore_custom_reg_settings(struct wiphy *wiphy,
627 uint8_t *country_alpha2,
628 bool *reset)
629{
630 struct ieee80211_supported_band *sband;
Dustin Browna30892e2016-10-12 17:28:36 -0700631 enum nl80211_band band;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700632 struct ieee80211_channel *chan;
633 int i;
634
635 if ((country_alpha2[0] == '0') &&
636 (country_alpha2[1] == '0') &&
637 (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) {
638
Dustin Browna30892e2016-10-12 17:28:36 -0700639 for (band = 0; band < NUM_NL80211_BANDS; band++) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700640 sband = wiphy->bands[band];
641 if (!sband)
642 continue;
643 for (i = 0; i < sband->n_channels; i++) {
644 chan = &sband->channels[i];
645 chan->flags = chan->orig_flags;
646 chan->max_antenna_gain = chan->orig_mag;
647 chan->max_power = chan->orig_mpwr;
648 }
649 }
650 *reset = true;
651 }
652}
653#endif
654
Amar Singhale4f28ee2015-10-21 14:36:56 -0700655/**
656 * hdd_restore_reg_flags() - restore regulatory flags
657 * @flags: regulatory flags
658 *
659 * Return: void
660 */
661#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(WITH_BACKPORTS)
662static void hdd_restore_reg_flags(struct wiphy *wiphy, uint32_t flags)
663{
664 wiphy->regulatory_flags = flags;
665}
666#else
667static void hdd_restore_reg_flags(struct wiphy *wiphy, uint32_t flags)
668{
669 wiphy->flags = flags;
670}
671#endif
672
Amar Singhale4f28ee2015-10-21 14:36:56 -0700673/**
674 * hdd_reg_notifier() - regulatory notifier
675 * @wiphy: wiphy
676 * @request: regulatory request
677 *
678 * Return: void
679 */
680void hdd_reg_notifier(struct wiphy *wiphy,
681 struct regulatory_request *request)
682{
683 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700684 bool reset = false;
Amar Singhal5cccafe2017-02-15 12:42:58 -0800685 enum dfs_reg dfs_reg;
686 struct reg_config_vars config_vars;
Amar Singhale4f28ee2015-10-21 14:36:56 -0700687
688 hdd_info("country: %c%c, initiator %d, dfs_region: %d",
689 request->alpha2[0],
690 request->alpha2[1],
691 request->initiator,
692 request->dfs_region);
693
694 if (NULL == hdd_ctx) {
695 hdd_err("invalid hdd_ctx pointer");
696 return;
697 }
698
699 if (cds_is_driver_unloading() || cds_is_driver_recovering()) {
700 hdd_err("%s: unloading or ssr in progress, ignore",
701 __func__);
702 return;
703 }
704
Dustin Brownc0ad1ec2017-01-26 12:01:32 -0800705 if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) {
706 hdd_err("Driver module is closed; dropping request");
707 return;
708 }
709
Mukul Sharma0c854a02016-10-20 15:36:20 +0530710 if (hdd_ctx->isWiphySuspended == true) {
711 hdd_err("%s: system/cfg80211 is already suspend", __func__);
712 return;
713 }
714
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700715 if (('K' == request->alpha2[0]) &&
716 ('R' == request->alpha2[1]))
Amar Singhal5cccafe2017-02-15 12:42:58 -0800717 request->dfs_region = DFS_KR_REG;
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700718
719 if (('C' == request->alpha2[0]) &&
720 ('N' == request->alpha2[1]))
Amar Singhal5cccafe2017-02-15 12:42:58 -0800721 request->dfs_region = DFS_CN_REG;
Amar Singhal604ba6cf2016-07-27 15:29:51 -0700722
Amar Singhale4f28ee2015-10-21 14:36:56 -0700723 /* first check if this callback is in response to the driver callback */
724
725 switch (request->initiator) {
726 case NL80211_REGDOM_SET_BY_DRIVER:
727 case NL80211_REGDOM_SET_BY_CORE:
728 case NL80211_REGDOM_SET_BY_USER:
729
730 if ((false == init_by_driver) &&
731 (false == init_by_reg_core)) {
732
733 if (NL80211_REGDOM_SET_BY_CORE == request->initiator)
734 return;
735 init_by_reg_core = true;
736 }
737
738 if ((NL80211_REGDOM_SET_BY_DRIVER == request->initiator) &&
739 (true == init_by_driver)) {
740
741 /*
742 * restore the driver regulatory flags since
743 * regulatory_hint may have
744 * changed them
745 */
746 hdd_restore_reg_flags(wiphy, hdd_ctx->reg.reg_flags);
747 }
748
749 if (NL80211_REGDOM_SET_BY_CORE == request->initiator) {
750 hdd_ctx->reg.cc_src = SOURCE_CORE;
751 if (is_wiphy_custom_regulatory(wiphy))
752 reset = true;
Amar Singhal6edf9732016-11-20 21:43:40 -0800753 } else if (NL80211_REGDOM_SET_BY_DRIVER == request->initiator) {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700754 hdd_ctx->reg.cc_src = SOURCE_DRIVER;
Amar Singhal6edf9732016-11-20 21:43:40 -0800755 sme_set_cc_src(hdd_ctx->hHal, SOURCE_DRIVER);
756 } else {
Amar Singhale4f28ee2015-10-21 14:36:56 -0700757 hdd_ctx->reg.cc_src = SOURCE_USERSPACE;
758 hdd_restore_custom_reg_settings(wiphy,
759 request->alpha2,
760 &reset);
761 }
762
763 hdd_ctx->reg.alpha2[0] = request->alpha2[0];
764 hdd_ctx->reg.alpha2[1] = request->alpha2[1];
765
766 hdd_update_regulatory_info(hdd_ctx);
767
Amar Singhale4f28ee2015-10-21 14:36:56 -0700768 hdd_process_regulatory_data(hdd_ctx, wiphy, reset);
769
Amar Singhale4f28ee2015-10-21 14:36:56 -0700770 sme_generic_change_country_code(hdd_ctx->hHal,
771 hdd_ctx->reg.alpha2);
772
773 cds_fill_and_send_ctl_to_fw(&hdd_ctx->reg);
774
775 hdd_set_dfs_region(hdd_ctx, request->dfs_region);
Amar Singhal5cccafe2017-02-15 12:42:58 -0800776 wlan_reg_get_dfs_region(hdd_ctx->hdd_psoc, &dfs_reg);
Amar Singhal5cccafe2017-02-15 12:42:58 -0800777
778 reg_program_config_vars(hdd_ctx, &config_vars);
779 ucfg_reg_set_config_vars(hdd_ctx->hdd_psoc, config_vars);
780 ucfg_reg_program_mas_chan_list(hdd_ctx->hdd_psoc,
781 reg_channels,
782 hdd_ctx->reg.alpha2,
783 dfs_reg);
Amar Singhale4f28ee2015-10-21 14:36:56 -0700784 break;
785
786 default:
787 break;
788 }
789}
Amar Singhal5cccafe2017-02-15 12:42:58 -0800790
791
792#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
793static void fill_wiphy_channel(struct ieee80211_channel *wiphy_chan,
794 struct regulatory_channel *cur_chan)
795{
796
797 wiphy_chan->flags &= ~IEEE80211_CHAN_DISABLED;
798 wiphy_chan->max_power = cur_chan->tx_power;
799
800 if (cur_chan->chan_flags & REGULATORY_CHAN_DISABLED)
801 wiphy_chan->flags |= IEEE80211_CHAN_DISABLED;
802 if (cur_chan->chan_flags & REGULATORY_CHAN_NO_IR)
803 wiphy_chan->flags |= IEEE80211_CHAN_NO_IR;
804 if (cur_chan->chan_flags & REGULATORY_CHAN_RADAR)
805 wiphy_chan->flags |= IEEE80211_CHAN_RADAR;
806 if (cur_chan->chan_flags & REGULATORY_CHAN_NO_OFDM)
807 wiphy_chan->flags |= IEEE80211_CHAN_NO_OFDM;
808 if (cur_chan->chan_flags & REGULATORY_CHAN_INDOOR_ONLY)
809 wiphy_chan->flags |= IEEE80211_CHAN_INDOOR_ONLY;
810
811 if (cur_chan->max_bw < 10)
812 wiphy_chan->flags |= IEEE80211_CHAN_NO_10MHZ;
813 if (cur_chan->max_bw < 20)
814 wiphy_chan->flags |= IEEE80211_CHAN_NO_20MHZ;
815 if (cur_chan->max_bw < 40)
816 wiphy_chan->flags |= IEEE80211_CHAN_NO_HT40;
817 if (cur_chan->max_bw < 80)
818 wiphy_chan->flags |= IEEE80211_CHAN_NO_80MHZ;
819 if (cur_chan->max_bw < 160)
820 wiphy_chan->flags |= IEEE80211_CHAN_NO_160MHZ;
821}
822
823
824static void fill_wiphy_band_channels(struct wiphy *wiphy,
825 struct regulatory_channel *cur_chan_list,
826 uint8_t band_id)
827{
828 uint32_t wiphy_num_chan, wiphy_index;
829 uint32_t chan_cnt;
830 struct ieee80211_channel *wiphy_chan;
831
832 wiphy_num_chan = wiphy->bands[band_id]->n_channels;
833 wiphy_chan = wiphy->bands[band_id]->channels;
834
835 for (wiphy_index = 0; wiphy_index < wiphy_num_chan; wiphy_index++) {
836 for (chan_cnt = 0; chan_cnt < NUM_CHANNELS; chan_cnt++) {
837 if (wiphy_chan[wiphy_index].center_freq ==
838 cur_chan_list[chan_cnt].center_freq) {
839 fill_wiphy_channel(&(wiphy_chan[wiphy_index]),
840 &(cur_chan_list[chan_cnt]));
841 break;
842 }
843 }
844 }
845}
846
847
Amar Singhal6f8592b2017-04-26 14:31:58 -0700848static void hdd_regulatory_dyn_cbk(struct wlan_objmgr_psoc *psoc,
849 struct wlan_objmgr_pdev *pdev,
850 struct regulatory_channel *chan_list,
851 void *arg)
852{
853 struct wiphy *wiphy;
854 struct pdev_osif_priv *pdev_priv;
855 hdd_context_t *hdd_ctx;
856 enum country_src cc_src;
857 uint8_t alpha2[REG_ALPHA2_LEN + 1];
858
859 pdev_priv = wlan_pdev_get_ospriv(pdev);
860 wiphy = pdev_priv->wiphy;
861 hdd_ctx = wiphy_priv(wiphy);
862
863 hdd_debug("%s: process channel list update from regulatory", __func__);
864
865 fill_wiphy_band_channels(wiphy, chan_list, NL80211_BAND_2GHZ);
866 fill_wiphy_band_channels(wiphy, chan_list, NL80211_BAND_5GHZ);
867
868 cc_src = ucfg_reg_get_cc_and_src(hdd_ctx->hdd_psoc, alpha2);
869 qdf_mem_copy(hdd_ctx->reg.alpha2, alpha2, REG_ALPHA2_LEN + 1);
870 sme_set_cc_src(hdd_ctx->hHal, cc_src);
871
872 sme_generic_change_country_code(hdd_ctx->hHal,
873 hdd_ctx->reg.alpha2);
874}
875
Amar Singhal5cccafe2017-02-15 12:42:58 -0800876/**
877 * hdd_regulatory_init_offload() - regulatory init
878 * @hdd_ctx: hdd context
879 * @wiphy: wiphy
880 *
881 * Return: int
882 */
883static int hdd_regulatory_init_offload(hdd_context_t *hdd_ctx,
884 struct wiphy *wiphy)
885{
886 struct reg_config_vars config_vars;
887 struct regulatory_channel cur_chan_list[NUM_CHANNELS];
Amar Singhal6f8592b2017-04-26 14:31:58 -0700888 enum country_src cc_src;
889 uint8_t alpha2[REG_ALPHA2_LEN + 1];
Amar Singhal5cccafe2017-02-15 12:42:58 -0800890
891 reg_program_config_vars(hdd_ctx, &config_vars);
892 ucfg_reg_set_config_vars(hdd_ctx->hdd_psoc, config_vars);
Amar Singhal6f8592b2017-04-26 14:31:58 -0700893
Amar Singhal5cccafe2017-02-15 12:42:58 -0800894 ucfg_reg_get_current_chan_list(hdd_ctx->hdd_pdev, cur_chan_list);
895 fill_wiphy_band_channels(wiphy, cur_chan_list, NL80211_BAND_2GHZ);
896 fill_wiphy_band_channels(wiphy, cur_chan_list, NL80211_BAND_5GHZ);
897
Amar Singhal6f8592b2017-04-26 14:31:58 -0700898 cc_src = ucfg_reg_get_cc_and_src(hdd_ctx->hdd_psoc, alpha2);
899 qdf_mem_copy(hdd_ctx->reg.alpha2, alpha2, REG_ALPHA2_LEN + 1);
900 sme_set_cc_src(hdd_ctx->hHal, cc_src);
901
902 ucfg_reg_register_chan_change_callback(hdd_ctx->hdd_psoc,
903 hdd_regulatory_dyn_cbk,
904 NULL);
Amar Singhal5cccafe2017-02-15 12:42:58 -0800905 return 0;
906}
907
908
Amar Singhal5cccafe2017-02-15 12:42:58 -0800909int hdd_regulatory_init(hdd_context_t *hdd_ctx, struct wiphy *wiphy)
910{
911 bool offload_enabled;
Amar Singhal5cccafe2017-02-15 12:42:58 -0800912
913 offload_enabled = ucfg_reg_is_regdb_offloaded(hdd_ctx->hdd_psoc);
914
915 if (offload_enabled && hdd_ctx->config->reg_offload_enabled) {
916 hdd_ctx->reg_offload = true;
917 wiphy->reg_notifier = NULL;
918 wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
919 hdd_regulatory_init_offload(hdd_ctx, wiphy);
Amar Singhal5cccafe2017-02-15 12:42:58 -0800920 } else {
921 hdd_ctx->reg_offload = false;
922 wiphy->reg_notifier = hdd_reg_notifier;
923 wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
924 wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
925 hdd_regulatory_init_no_offload(hdd_ctx, wiphy);
Amar Singhal5cccafe2017-02-15 12:42:58 -0800926 }
927
928 return 0;
929}
Amar Singhal6f8592b2017-04-26 14:31:58 -0700930
Amar Singhal5cccafe2017-02-15 12:42:58 -0800931#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
932int hdd_regulatory_init(hdd_context_t *hdd_ctx, struct wiphy *wiphy)
933{
934 hdd_ctx->reg_offload = false;
935 wiphy->reg_notifier = hdd_reg_notifier;
936 wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
937 wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
938 hdd_regulatory_init_no_offload(hdd_ctx, wiphy);
939
940 return 0;
941}
Amar Singhal6f8592b2017-04-26 14:31:58 -0700942
Amar Singhal5cccafe2017-02-15 12:42:58 -0800943#else
944int hdd_regulatory_init(hdd_context_t *hdd_ctx, struct wiphy *wiphy)
945{
946 hdd_ctx->reg_offload = false;
947 wiphy->reg_notifier = hdd_reg_notifier;
948 wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
949 wiphy->country_ie_pref |= NL80211_COUNTRY_IE_IGNORE_CORE;
950 hdd_regulatory_init_no_offload(hdd_ctx, wiphy);
951
952 return 0;
953}
954#endif