Blame view

net/wireless/reg.c 87 KB
8318d78a4   Johannes Berg   cfg80211 API for ...
1
2
3
4
  /*
   * Copyright 2002-2005, Instant802 Networks, Inc.
   * Copyright 2005-2006, Devicescape Software, Inc.
   * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
3b77d5ec0   Luis R. Rodriguez   cfg80211: relicen...
5
   * Copyright 2008-2011	Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
2740f0cf8   Johannes Berg   cfg80211: add Int...
6
   * Copyright 2013-2014  Intel Mobile Communications GmbH
4e0854a74   Emmanuel Grumbach   cfg80211: honor N...
7
   * Copyright      2017  Intel Deutschland GmbH
8318d78a4   Johannes Berg   cfg80211 API for ...
8
   *
3b77d5ec0   Luis R. Rodriguez   cfg80211: relicen...
9
10
11
12
13
14
15
16
17
18
19
   * Permission to use, copy, modify, and/or distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8318d78a4   Johannes Berg   cfg80211 API for ...
20
   */
3b77d5ec0   Luis R. Rodriguez   cfg80211: relicen...
21

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
22
23
  /**
   * DOC: Wireless regulatory infrastructure
8318d78a4   Johannes Berg   cfg80211 API for ...
24
25
26
27
28
29
   *
   * The usual implementation is for a driver to read a device EEPROM to
   * determine which regulatory domain it should be operating under, then
   * looking up the allowable channels in a driver-local table and finally
   * registering those channels in the wiphy structure.
   *
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
   * Another set of compliance enforcement is for drivers to use their
   * own compliance limits which can be stored on the EEPROM. The host
   * driver or firmware may ensure these are used.
   *
   * In addition to all this we provide an extra layer of regulatory
   * conformance. For drivers which do not have any regulatory
   * information CRDA provides the complete regulatory solution.
   * For others it provides a community effort on further restrictions
   * to enhance compliance.
   *
   * Note: When number of rules --> infinity we will not be able to
   * index on alpha2 any more, instead we'll probably have to
   * rely on some SHA1 checksum of the regdomain for example.
   *
8318d78a4   Johannes Berg   cfg80211 API for ...
44
   */
e9c0268f0   Joe Perches   net/wireless: Use...
45
46
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8318d78a4   Johannes Berg   cfg80211 API for ...
47
  #include <linux/kernel.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
48
  #include <linux/export.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
49
  #include <linux/slab.h>
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
50
  #include <linux/list.h>
c61029c77   John W. Linville   wireless: upcase ...
51
  #include <linux/ctype.h>
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
52
53
  #include <linux/nl80211.h>
  #include <linux/platform_device.h>
d9b938421   Paul Gortmaker   net: add modulepa...
54
  #include <linux/moduleparam.h>
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
55
  #include <net/cfg80211.h>
8318d78a4   Johannes Berg   cfg80211 API for ...
56
  #include "core.h"
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
57
  #include "reg.h"
ad932f046   Arik Nemtsov   cfg80211: leave i...
58
  #include "rdev-ops.h"
3b377ea9d   John W. Linville   wireless: support...
59
  #include "regdb.h"
73d54c9e7   Luis R. Rodriguez   cfg80211: add reg...
60
  #include "nl80211.h"
8318d78a4   Johannes Berg   cfg80211 API for ...
61

ad932f046   Arik Nemtsov   cfg80211: leave i...
62
63
64
65
66
  /*
   * Grace period we give before making sure all current interfaces reside on
   * channels allowed by the current regulatory domain.
   */
  #define REG_ENFORCE_GRACE_MS 60000
52616f2b4   Ilan Peer   cfg80211: Add an ...
67
68
69
70
71
72
73
74
75
  /**
   * enum reg_request_treatment - regulatory request treatment
   *
   * @REG_REQ_OK: continue processing the regulatory request
   * @REG_REQ_IGNORE: ignore the regulatory request
   * @REG_REQ_INTERSECT: the regulatory domain resulting from this request should
   *	be intersected with the current one.
   * @REG_REQ_ALREADY_SET: the regulatory request will not change the current
   *	regulatory settings, and no further processing is required.
52616f2b4   Ilan Peer   cfg80211: Add an ...
76
   */
2f92212b7   Johannes Berg   regulatory: use p...
77
78
79
80
81
82
  enum reg_request_treatment {
  	REG_REQ_OK,
  	REG_REQ_IGNORE,
  	REG_REQ_INTERSECT,
  	REG_REQ_ALREADY_SET,
  };
a042994dd   Luis R. Rodriguez   cfg80211: fix rac...
83
84
85
86
87
88
89
90
  static struct regulatory_request core_request_world = {
  	.initiator = NL80211_REGDOM_SET_BY_CORE,
  	.alpha2[0] = '0',
  	.alpha2[1] = '0',
  	.intersect = false,
  	.processed = true,
  	.country_ie_env = ENVIRON_ANY,
  };
38fd2143f   Johannes Berg   regulatory: remov...
91
92
93
94
  /*
   * Receipt of information from last regulatory request,
   * protected by RTNL (and can be accessed with RCU protection)
   */
c492db370   Johannes Berg   regulatory: use R...
95
  static struct regulatory_request __rcu *last_request =
cec3f0ed7   Johannes Berg   cfg80211: use __f...
96
  	(void __force __rcu *)&core_request_world;
734366dea   Johannes Berg   cfg80211: clean u...
97

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
98
99
  /* To trigger userspace events */
  static struct platform_device *reg_pdev;
8318d78a4   Johannes Berg   cfg80211 API for ...
100

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
101
102
  /*
   * Central wireless core regulatory domains, we only need two,
734366dea   Johannes Berg   cfg80211: clean u...
103
   * the current one and a world regulatory domain in case we have no
e8da2bb4f   Johannes Berg   regulatory: clari...
104
   * information to give us an alpha2.
38fd2143f   Johannes Berg   regulatory: remov...
105
   * (protected by RTNL, can be read under RCU)
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
106
   */
458f4f9e9   Johannes Berg   regulatory: use R...
107
  const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
734366dea   Johannes Berg   cfg80211: clean u...
108

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
109
  /*
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
110
111
   * Number of devices that registered to the core
   * that support cellular base station regulatory hints
38fd2143f   Johannes Berg   regulatory: remov...
112
   * (protected by RTNL)
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
113
114
   */
  static int reg_num_devs_support_basehint;
52616f2b4   Ilan Peer   cfg80211: Add an ...
115
116
117
118
  /*
   * State variable indicating if the platform on which the devices
   * are attached is operating in an indoor environment. The state variable
   * is relevant for all registered devices.
52616f2b4   Ilan Peer   cfg80211: Add an ...
119
120
   */
  static bool reg_is_indoor;
050507536   Ilan peer   cfg80211: Add API...
121
122
123
124
  static spinlock_t reg_indoor_lock;
  
  /* Used to track the userspace process controlling the indoor setting */
  static u32 reg_is_indoor_portid;
52616f2b4   Ilan Peer   cfg80211: Add an ...
125

b68630369   Johannes Berg   cfg80211: reg: ma...
126
  static void restore_regulatory_settings(bool reset_user);
c37722bd1   Ilan peer   cfg80211: Stop ca...
127

458f4f9e9   Johannes Berg   regulatory: use R...
128
129
  static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
  {
38fd2143f   Johannes Berg   regulatory: remov...
130
  	return rtnl_dereference(cfg80211_regdomain);
458f4f9e9   Johannes Berg   regulatory: use R...
131
  }
ad30ca2c0   Arik Nemtsov   cfg80211: allow u...
132
  const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
458f4f9e9   Johannes Berg   regulatory: use R...
133
  {
38fd2143f   Johannes Berg   regulatory: remov...
134
  	return rtnl_dereference(wiphy->regd);
458f4f9e9   Johannes Berg   regulatory: use R...
135
  }
3ef121b51   Luis R. Rodriguez   cfg80211: replace...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region)
  {
  	switch (dfs_region) {
  	case NL80211_DFS_UNSET:
  		return "unset";
  	case NL80211_DFS_FCC:
  		return "FCC";
  	case NL80211_DFS_ETSI:
  		return "ETSI";
  	case NL80211_DFS_JP:
  		return "JP";
  	}
  	return "Unknown";
  }
6c474799d   Luis R. Rodriguez   cfg80211: add reg...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
  {
  	const struct ieee80211_regdomain *regd = NULL;
  	const struct ieee80211_regdomain *wiphy_regd = NULL;
  
  	regd = get_cfg80211_regdom();
  	if (!wiphy)
  		goto out;
  
  	wiphy_regd = get_wiphy_regdom(wiphy);
  	if (!wiphy_regd)
  		goto out;
  
  	if (wiphy_regd->dfs_region == regd->dfs_region)
  		goto out;
c799ba6ea   Johannes Berg   cfg80211: remove ...
165
166
167
168
169
  	pr_debug("%s: device specific dfs_region (%s) disagrees with cfg80211's central dfs_region (%s)
  ",
  		 dev_name(&wiphy->dev),
  		 reg_dfs_region_str(wiphy_regd->dfs_region),
  		 reg_dfs_region_str(regd->dfs_region));
6c474799d   Luis R. Rodriguez   cfg80211: add reg...
170
171
172
173
  
  out:
  	return regd->dfs_region;
  }
458f4f9e9   Johannes Berg   regulatory: use R...
174
175
176
177
178
179
  static void rcu_free_regdom(const struct ieee80211_regdomain *r)
  {
  	if (!r)
  		return;
  	kfree_rcu((struct ieee80211_regdomain *)r, rcu_head);
  }
c492db370   Johannes Berg   regulatory: use R...
180
181
  static struct regulatory_request *get_last_request(void)
  {
38fd2143f   Johannes Berg   regulatory: remov...
182
  	return rcu_dereference_rtnl(last_request);
c492db370   Johannes Berg   regulatory: use R...
183
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
184
  /* Used to queue up regulatory hints */
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
185
186
  static LIST_HEAD(reg_requests_list);
  static spinlock_t reg_requests_lock;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
187
188
189
190
191
192
193
194
195
196
197
  /* Used to queue up beacon hints for review */
  static LIST_HEAD(reg_pending_beacons);
  static spinlock_t reg_pending_beacons_lock;
  
  /* Used to keep track of processed beacon hints */
  static LIST_HEAD(reg_beacon_list);
  
  struct reg_beacon {
  	struct list_head list;
  	struct ieee80211_channel chan;
  };
ad932f046   Arik Nemtsov   cfg80211: leave i...
198
199
  static void reg_check_chans_work(struct work_struct *work);
  static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work);
f333a7a2f   Luis R. Rodriguez   cfg80211: move re...
200
201
  static void reg_todo(struct work_struct *work);
  static DECLARE_WORK(reg_work, reg_todo);
734366dea   Johannes Berg   cfg80211: clean u...
202
203
  /* We keep a static world regulatory domain in case of the absence of CRDA */
  static const struct ieee80211_regdomain world_regdom = {
28981e5eb   Jason Abele   cfg80211: fix n_r...
204
  	.n_reg_rules = 8,
734366dea   Johannes Berg   cfg80211: clean u...
205
206
  	.alpha2 =  "00",
  	.reg_rules = {
68798a626   Luis R. Rodriguez   cfg80211: enable ...
207
208
  		/* IEEE 802.11b/g, channels 1..11 */
  		REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
43c771a19   Johannes Berg   wireless: allow 4...
209
  		/* IEEE 802.11b/g, channels 12..13. */
c3826807b   Johannes Berg   regulatory: fix w...
210
211
  		REG_RULE(2467-10, 2472+10, 20, 6, 20,
  			NL80211_RRF_NO_IR | NL80211_RRF_AUTO_BW),
611b6a82a   Luis R. Rodriguez   cfg80211: Enable ...
212
213
214
  		/* IEEE 802.11 channel 14 - Only JP enables
  		 * this and for 802.11b only */
  		REG_RULE(2484-10, 2484+10, 20, 6, 20,
8fe02e167   Luis R. Rodriguez   cfg80211: consoli...
215
  			NL80211_RRF_NO_IR |
611b6a82a   Luis R. Rodriguez   cfg80211: Enable ...
216
217
  			NL80211_RRF_NO_OFDM),
  		/* IEEE 802.11a, channel 36..48 */
c3826807b   Johannes Berg   regulatory: fix w...
218
219
220
  		REG_RULE(5180-10, 5240+10, 80, 6, 20,
                          NL80211_RRF_NO_IR |
                          NL80211_RRF_AUTO_BW),
3fc71f775   Luis R. Rodriguez   cfg80211: enable ...
221

131a19bc9   Johannes Berg   regulatory: enabl...
222
  		/* IEEE 802.11a, channel 52..64 - DFS required */
c3826807b   Johannes Berg   regulatory: fix w...
223
  		REG_RULE(5260-10, 5320+10, 80, 6, 20,
8fe02e167   Luis R. Rodriguez   cfg80211: consoli...
224
  			NL80211_RRF_NO_IR |
c3826807b   Johannes Berg   regulatory: fix w...
225
  			NL80211_RRF_AUTO_BW |
131a19bc9   Johannes Berg   regulatory: enabl...
226
227
228
229
  			NL80211_RRF_DFS),
  
  		/* IEEE 802.11a, channel 100..144 - DFS required */
  		REG_RULE(5500-10, 5720+10, 160, 6, 20,
8fe02e167   Luis R. Rodriguez   cfg80211: consoli...
230
  			NL80211_RRF_NO_IR |
131a19bc9   Johannes Berg   regulatory: enabl...
231
  			NL80211_RRF_DFS),
3fc71f775   Luis R. Rodriguez   cfg80211: enable ...
232
233
  
  		/* IEEE 802.11a, channel 149..165 */
8ab9d85c6   Johannes Berg   regulatory: allow...
234
  		REG_RULE(5745-10, 5825+10, 80, 6, 20,
8fe02e167   Luis R. Rodriguez   cfg80211: consoli...
235
  			NL80211_RRF_NO_IR),
90cdc6df7   Vladimir Kondratiev   wireless: regulat...
236

8047d2616   Johannes Berg   cfg80211: fix gHz...
237
  		/* IEEE 802.11ad (60GHz), channels 1..3 */
90cdc6df7   Vladimir Kondratiev   wireless: regulat...
238
  		REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
734366dea   Johannes Berg   cfg80211: clean u...
239
240
  	}
  };
38fd2143f   Johannes Berg   regulatory: remov...
241
  /* protected by RTNL */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
242
243
  static const struct ieee80211_regdomain *cfg80211_world_regdom =
  	&world_regdom;
734366dea   Johannes Berg   cfg80211: clean u...
244

6ee7d3305   Luis R. Rodriguez   cfg80211: make re...
245
  static char *ieee80211_regdom = "00";
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
246
  static char user_alpha2[2];
6ee7d3305   Luis R. Rodriguez   cfg80211: make re...
247

734366dea   Johannes Berg   cfg80211: clean u...
248
249
  module_param(ieee80211_regdom, charp, 0444);
  MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
c888393b7   Arik Nemtsov   cfg80211: avoid f...
250
  static void reg_free_request(struct regulatory_request *request)
5ad6ef5e0   Luis R. Rodriguez   cfg80211: add hel...
251
  {
d34265a3e   Johannes Berg   cfg80211: reg: ce...
252
253
  	if (request == &core_request_world)
  		return;
c888393b7   Arik Nemtsov   cfg80211: avoid f...
254
255
256
257
258
259
260
  	if (request != get_last_request())
  		kfree(request);
  }
  
  static void reg_free_last_request(void)
  {
  	struct regulatory_request *lr = get_last_request();
5ad6ef5e0   Luis R. Rodriguez   cfg80211: add hel...
261
262
263
  	if (lr != &core_request_world && lr)
  		kfree_rcu(lr, rcu_head);
  }
05f1a3ea2   Luis R. Rodriguez   cfg80211: add hel...
264
265
  static void reg_update_last_request(struct regulatory_request *request)
  {
255e25b0e   Luis R. Rodriguez   cfg80211: allow r...
266
267
268
269
270
  	struct regulatory_request *lr;
  
  	lr = get_last_request();
  	if (lr == request)
  		return;
c888393b7   Arik Nemtsov   cfg80211: avoid f...
271
  	reg_free_last_request();
05f1a3ea2   Luis R. Rodriguez   cfg80211: add hel...
272
273
  	rcu_assign_pointer(last_request, request);
  }
379b82f4c   Johannes Berg   regulatory: pass ...
274
275
  static void reset_regdomains(bool full_reset,
  			     const struct ieee80211_regdomain *new_regdom)
734366dea   Johannes Berg   cfg80211: clean u...
276
  {
458f4f9e9   Johannes Berg   regulatory: use R...
277
  	const struct ieee80211_regdomain *r;
38fd2143f   Johannes Berg   regulatory: remov...
278
  	ASSERT_RTNL();
e8da2bb4f   Johannes Berg   regulatory: clari...
279

458f4f9e9   Johannes Berg   regulatory: use R...
280
  	r = get_cfg80211_regdom();
942b25cf9   Johannes Berg   cfg80211: clean u...
281
  	/* avoid freeing static information or freeing something twice */
458f4f9e9   Johannes Berg   regulatory: use R...
282
283
  	if (r == cfg80211_world_regdom)
  		r = NULL;
942b25cf9   Johannes Berg   cfg80211: clean u...
284
285
  	if (cfg80211_world_regdom == &world_regdom)
  		cfg80211_world_regdom = NULL;
458f4f9e9   Johannes Berg   regulatory: use R...
286
287
  	if (r == &world_regdom)
  		r = NULL;
942b25cf9   Johannes Berg   cfg80211: clean u...
288

458f4f9e9   Johannes Berg   regulatory: use R...
289
290
  	rcu_free_regdom(r);
  	rcu_free_regdom(cfg80211_world_regdom);
734366dea   Johannes Berg   cfg80211: clean u...
291

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
292
  	cfg80211_world_regdom = &world_regdom;
458f4f9e9   Johannes Berg   regulatory: use R...
293
  	rcu_assign_pointer(cfg80211_regdomain, new_regdom);
a042994dd   Luis R. Rodriguez   cfg80211: fix rac...
294
295
296
  
  	if (!full_reset)
  		return;
05f1a3ea2   Luis R. Rodriguez   cfg80211: add hel...
297
  	reg_update_last_request(&core_request_world);
734366dea   Johannes Berg   cfg80211: clean u...
298
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
299
300
301
302
  /*
   * Dynamic world regulatory domain requested by the wireless
   * core upon initialization
   */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
303
  static void update_world_regdomain(const struct ieee80211_regdomain *rd)
734366dea   Johannes Berg   cfg80211: clean u...
304
  {
c492db370   Johannes Berg   regulatory: use R...
305
  	struct regulatory_request *lr;
734366dea   Johannes Berg   cfg80211: clean u...
306

c492db370   Johannes Berg   regulatory: use R...
307
308
309
  	lr = get_last_request();
  
  	WARN_ON(!lr);
734366dea   Johannes Berg   cfg80211: clean u...
310

379b82f4c   Johannes Berg   regulatory: pass ...
311
  	reset_regdomains(false, rd);
734366dea   Johannes Berg   cfg80211: clean u...
312
313
  
  	cfg80211_world_regdom = rd;
734366dea   Johannes Berg   cfg80211: clean u...
314
  }
734366dea   Johannes Berg   cfg80211: clean u...
315

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
316
  bool is_world_regdom(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
317
318
319
  {
  	if (!alpha2)
  		return false;
1a9193185   Johannes Berg   regulatory: code ...
320
  	return alpha2[0] == '0' && alpha2[1] == '0';
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
321
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
322

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
323
  static bool is_alpha2_set(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
324
325
326
  {
  	if (!alpha2)
  		return false;
1a9193185   Johannes Berg   regulatory: code ...
327
  	return alpha2[0] && alpha2[1];
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
328
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
329

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
330
  static bool is_unknown_alpha2(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
331
332
333
  {
  	if (!alpha2)
  		return false;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
334
335
336
337
  	/*
  	 * Special case where regulatory domain was built by driver
  	 * but a specific alpha2 cannot be determined
  	 */
1a9193185   Johannes Berg   regulatory: code ...
338
  	return alpha2[0] == '9' && alpha2[1] == '9';
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
339
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
340

3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
341
342
343
344
  static bool is_intersected_alpha2(const char *alpha2)
  {
  	if (!alpha2)
  		return false;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
345
346
  	/*
  	 * Special case where regulatory domain is the
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
347
  	 * result of an intersection between two regulatory domain
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
348
349
  	 * structures
  	 */
1a9193185   Johannes Berg   regulatory: code ...
350
  	return alpha2[0] == '9' && alpha2[1] == '8';
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
351
  }
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
352
  static bool is_an_alpha2(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
353
354
355
  {
  	if (!alpha2)
  		return false;
1a9193185   Johannes Berg   regulatory: code ...
356
  	return isalpha(alpha2[0]) && isalpha(alpha2[1]);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
357
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
358

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
359
  static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
360
361
362
  {
  	if (!alpha2_x || !alpha2_y)
  		return false;
1a9193185   Johannes Berg   regulatory: code ...
363
  	return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1];
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
364
  }
69b1572bd   Luis R. Rodriguez   cfg80211: rename ...
365
  static bool regdom_changes(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
366
  {
458f4f9e9   Johannes Berg   regulatory: use R...
367
  	const struct ieee80211_regdomain *r = get_cfg80211_regdom();
761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
368

458f4f9e9   Johannes Berg   regulatory: use R...
369
  	if (!r)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
370
  		return true;
458f4f9e9   Johannes Berg   regulatory: use R...
371
  	return !alpha2_equal(r->alpha2, alpha2);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
372
  }
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
373
374
375
376
377
378
379
380
381
382
383
  /*
   * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets
   * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER
   * has ever been issued.
   */
  static bool is_user_regdom_saved(void)
  {
  	if (user_alpha2[0] == '9' && user_alpha2[1] == '7')
  		return false;
  
  	/* This would indicate a mistake on the design */
1a9193185   Johannes Berg   regulatory: code ...
384
  	if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2),
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
385
386
  		 "Unexpected user alpha2: %c%c
  ",
1a9193185   Johannes Berg   regulatory: code ...
387
  		 user_alpha2[0], user_alpha2[1]))
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
388
389
390
391
  		return false;
  
  	return true;
  }
e9763c3c2   Johannes Berg   regulatory: clean...
392
393
  static const struct ieee80211_regdomain *
  reg_copy_regd(const struct ieee80211_regdomain *src_regd)
3b377ea9d   John W. Linville   wireless: support...
394
395
  {
  	struct ieee80211_regdomain *regd;
e9763c3c2   Johannes Berg   regulatory: clean...
396
  	int size_of_regd;
3b377ea9d   John W. Linville   wireless: support...
397
  	unsigned int i;
82f208563   Johannes Berg   regulatory: don't...
398
399
400
  	size_of_regd =
  		sizeof(struct ieee80211_regdomain) +
  		src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
3b377ea9d   John W. Linville   wireless: support...
401
402
403
  
  	regd = kzalloc(size_of_regd, GFP_KERNEL);
  	if (!regd)
e9763c3c2   Johannes Berg   regulatory: clean...
404
  		return ERR_PTR(-ENOMEM);
3b377ea9d   John W. Linville   wireless: support...
405
406
407
408
409
  
  	memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
  
  	for (i = 0; i < src_regd->n_reg_rules; i++)
  		memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],
e9763c3c2   Johannes Berg   regulatory: clean...
410
  		       sizeof(struct ieee80211_reg_rule));
3b377ea9d   John W. Linville   wireless: support...
411

e9763c3c2   Johannes Berg   regulatory: clean...
412
  	return regd;
3b377ea9d   John W. Linville   wireless: support...
413
414
415
  }
  
  #ifdef CONFIG_CFG80211_INTERNAL_REGDB
c7d319e54   Johannes Berg   cfg80211: reg: se...
416
  struct reg_regdb_apply_request {
3b377ea9d   John W. Linville   wireless: support...
417
  	struct list_head list;
c7d319e54   Johannes Berg   cfg80211: reg: se...
418
  	const struct ieee80211_regdomain *regdom;
3b377ea9d   John W. Linville   wireless: support...
419
  };
c7d319e54   Johannes Berg   cfg80211: reg: se...
420
421
  static LIST_HEAD(reg_regdb_apply_list);
  static DEFINE_MUTEX(reg_regdb_apply_mutex);
3b377ea9d   John W. Linville   wireless: support...
422

c7d319e54   Johannes Berg   cfg80211: reg: se...
423
  static void reg_regdb_apply(struct work_struct *work)
3b377ea9d   John W. Linville   wireless: support...
424
  {
c7d319e54   Johannes Berg   cfg80211: reg: se...
425
  	struct reg_regdb_apply_request *request;
a85d0d7f3   Luis R. Rodriguez   cfg80211: fix pos...
426

5fe231e87   Johannes Berg   cfg80211: vastly ...
427
  	rtnl_lock();
3b377ea9d   John W. Linville   wireless: support...
428

c7d319e54   Johannes Berg   cfg80211: reg: se...
429
430
431
432
  	mutex_lock(&reg_regdb_apply_mutex);
  	while (!list_empty(&reg_regdb_apply_list)) {
  		request = list_first_entry(&reg_regdb_apply_list,
  					   struct reg_regdb_apply_request,
3b377ea9d   John W. Linville   wireless: support...
433
434
  					   list);
  		list_del(&request->list);
c7d319e54   Johannes Berg   cfg80211: reg: se...
435
  		set_regdom(request->regdom, REGD_SOURCE_INTERNAL_DB);
3b377ea9d   John W. Linville   wireless: support...
436
437
  		kfree(request);
  	}
c7d319e54   Johannes Berg   cfg80211: reg: se...
438
  	mutex_unlock(&reg_regdb_apply_mutex);
a85d0d7f3   Luis R. Rodriguez   cfg80211: fix pos...
439

5fe231e87   Johannes Berg   cfg80211: vastly ...
440
  	rtnl_unlock();
3b377ea9d   John W. Linville   wireless: support...
441
  }
c7d319e54   Johannes Berg   cfg80211: reg: se...
442
  static DECLARE_WORK(reg_regdb_work, reg_regdb_apply);
3b377ea9d   John W. Linville   wireless: support...
443

fd453d3c5   Johannes Berg   cfg80211: reg: re...
444
  static int reg_query_builtin(const char *alpha2)
3b377ea9d   John W. Linville   wireless: support...
445
  {
c7d319e54   Johannes Berg   cfg80211: reg: se...
446
447
448
449
450
451
452
453
454
455
456
457
458
  	const struct ieee80211_regdomain *regdom = NULL;
  	struct reg_regdb_apply_request *request;
  	unsigned int i;
  
  	for (i = 0; i < reg_regdb_size; i++) {
  		if (alpha2_equal(alpha2, reg_regdb[i]->alpha2)) {
  			regdom = reg_regdb[i];
  			break;
  		}
  	}
  
  	if (!regdom)
  		return -ENODATA;
3b377ea9d   John W. Linville   wireless: support...
459

c7d319e54   Johannes Berg   cfg80211: reg: se...
460
  	request = kzalloc(sizeof(struct reg_regdb_apply_request), GFP_KERNEL);
3b377ea9d   John W. Linville   wireless: support...
461
  	if (!request)
c7d319e54   Johannes Berg   cfg80211: reg: se...
462
  		return -ENOMEM;
3b377ea9d   John W. Linville   wireless: support...
463

c7d319e54   Johannes Berg   cfg80211: reg: se...
464
465
466
467
468
  	request->regdom = reg_copy_regd(regdom);
  	if (IS_ERR_OR_NULL(request->regdom)) {
  		kfree(request);
  		return -ENOMEM;
  	}
3b377ea9d   John W. Linville   wireless: support...
469

c7d319e54   Johannes Berg   cfg80211: reg: se...
470
471
472
  	mutex_lock(&reg_regdb_apply_mutex);
  	list_add_tail(&request->list, &reg_regdb_apply_list);
  	mutex_unlock(&reg_regdb_apply_mutex);
3b377ea9d   John W. Linville   wireless: support...
473
474
  
  	schedule_work(&reg_regdb_work);
c7d319e54   Johannes Berg   cfg80211: reg: se...
475
476
  
  	return 0;
3b377ea9d   John W. Linville   wireless: support...
477
  }
80007efef   Luis R. Rodriguez   cfg80211: warn if...
478
479
480
481
482
483
484
  
  /* Feel free to add any other sanity checks here */
  static void reg_regdb_size_check(void)
  {
  	/* We should ideally BUILD_BUG_ON() but then random builds would fail */
  	WARN_ONCE(!reg_regdb_size, "db.txt is empty, you should update it...");
  }
3b377ea9d   John W. Linville   wireless: support...
485
  #else
80007efef   Luis R. Rodriguez   cfg80211: warn if...
486
  static inline void reg_regdb_size_check(void) {}
fd453d3c5   Johannes Berg   cfg80211: reg: re...
487
  static inline int reg_query_builtin(const char *alpha2)
c7d319e54   Johannes Berg   cfg80211: reg: se...
488
489
490
  {
  	return -ENODATA;
  }
3b377ea9d   John W. Linville   wireless: support...
491
  #endif /* CONFIG_CFG80211_INTERNAL_REGDB */
b68630369   Johannes Berg   cfg80211: reg: ma...
492
493
494
495
496
497
498
499
500
501
502
  #ifdef CONFIG_CFG80211_CRDA_SUPPORT
  /* Max number of consecutive attempts to communicate with CRDA  */
  #define REG_MAX_CRDA_TIMEOUTS 10
  
  static u32 reg_crda_timeouts;
  
  static void crda_timeout_work(struct work_struct *work);
  static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work);
  
  static void crda_timeout_work(struct work_struct *work)
  {
c799ba6ea   Johannes Berg   cfg80211: remove ...
503
504
  	pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings
  ");
b68630369   Johannes Berg   cfg80211: reg: ma...
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
  	rtnl_lock();
  	reg_crda_timeouts++;
  	restore_regulatory_settings(true);
  	rtnl_unlock();
  }
  
  static void cancel_crda_timeout(void)
  {
  	cancel_delayed_work(&crda_timeout);
  }
  
  static void cancel_crda_timeout_sync(void)
  {
  	cancel_delayed_work_sync(&crda_timeout);
  }
  
  static void reset_crda_timeouts(void)
  {
  	reg_crda_timeouts = 0;
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
525
526
  /*
   * This lets us keep regulatory code which is updated on a regulatory
1226d2587   Johannes Berg   cfg80211: regulat...
527
   * basis in userspace.
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
528
   */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
529
530
  static int call_crda(const char *alpha2)
  {
1226d2587   Johannes Berg   cfg80211: regulat...
531
532
  	char country[12];
  	char *env[] = { country, NULL };
c7d319e54   Johannes Berg   cfg80211: reg: se...
533
  	int ret;
1226d2587   Johannes Berg   cfg80211: regulat...
534
535
536
  
  	snprintf(country, sizeof(country), "COUNTRY=%c%c",
  		 alpha2[0], alpha2[1]);
c37722bd1   Ilan peer   cfg80211: Stop ca...
537
  	if (reg_crda_timeouts > REG_MAX_CRDA_TIMEOUTS) {
042ab5fc7   Thomas Petazzoni   wireless: regulat...
538
539
  		pr_debug("Exceeded CRDA call max attempts. Not calling CRDA
  ");
c37722bd1   Ilan peer   cfg80211: Stop ca...
540
541
  		return -EINVAL;
  	}
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
542
  	if (!is_world_regdom((char *) alpha2))
042ab5fc7   Thomas Petazzoni   wireless: regulat...
543
544
  		pr_debug("Calling CRDA for country: %c%c
  ",
c799ba6ea   Johannes Berg   cfg80211: remove ...
545
  			 alpha2[0], alpha2[1]);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
546
  	else
042ab5fc7   Thomas Petazzoni   wireless: regulat...
547
548
  		pr_debug("Calling CRDA to update world regulatory domain
  ");
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
549

c7d319e54   Johannes Berg   cfg80211: reg: se...
550
551
552
553
554
  	ret = kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, env);
  	if (ret)
  		return ret;
  
  	queue_delayed_work(system_power_efficient_wq,
b68630369   Johannes Berg   cfg80211: reg: ma...
555
  			   &crda_timeout, msecs_to_jiffies(3142));
c7d319e54   Johannes Berg   cfg80211: reg: se...
556
  	return 0;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
557
  }
b68630369   Johannes Berg   cfg80211: reg: ma...
558
559
560
561
562
563
564
565
566
  #else
  static inline void cancel_crda_timeout(void) {}
  static inline void cancel_crda_timeout_sync(void) {}
  static inline void reset_crda_timeouts(void) {}
  static inline int call_crda(const char *alpha2)
  {
  	return -ENODATA;
  }
  #endif /* CONFIG_CFG80211_CRDA_SUPPORT */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
567

cecbb069c   Johannes Berg   cfg80211: reg: re...
568
  static bool reg_query_database(struct regulatory_request *request)
fe6631ff0   Luis R. Rodriguez   cfg80211: add hel...
569
  {
c7d319e54   Johannes Berg   cfg80211: reg: se...
570
  	/* query internal regulatory database (if it exists) */
fd453d3c5   Johannes Berg   cfg80211: reg: re...
571
  	if (reg_query_builtin(request->alpha2) == 0)
c7d319e54   Johannes Berg   cfg80211: reg: se...
572
  		return true;
eeca9fce1   Ilan peer   cfg80211: Schedul...
573

c7d319e54   Johannes Berg   cfg80211: reg: se...
574
575
576
577
  	if (call_crda(request->alpha2) == 0)
  		return true;
  
  	return false;
fe6631ff0   Luis R. Rodriguez   cfg80211: add hel...
578
  }
e438768ff   Luis R. Rodriguez   cfg80211: check r...
579
  bool reg_is_valid_request(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
580
  {
c492db370   Johannes Berg   regulatory: use R...
581
  	struct regulatory_request *lr = get_last_request();
61405e977   Luis R. Rodriguez   cfg80211: fix in ...
582

c492db370   Johannes Berg   regulatory: use R...
583
  	if (!lr || lr->processed)
f6037d09e   Johannes Berg   wireless: get rid...
584
  		return false;
c492db370   Johannes Berg   regulatory: use R...
585
  	return alpha2_equal(lr->alpha2, alpha2);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
586
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
587

e3961af1e   Janusz Dziedzic   cfg80211: add hel...
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy)
  {
  	struct regulatory_request *lr = get_last_request();
  
  	/*
  	 * Follow the driver's regulatory domain, if present, unless a country
  	 * IE has been processed or a user wants to help complaince further
  	 */
  	if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
  	    lr->initiator != NL80211_REGDOM_SET_BY_USER &&
  	    wiphy->regd)
  		return get_wiphy_regdom(wiphy);
  
  	return get_cfg80211_regdom();
  }
a6d4a534e   Arik Nemtsov   cfg80211: introdu...
603
604
605
  static unsigned int
  reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd,
  				 const struct ieee80211_reg_rule *rule)
975248208   Janusz Dziedzic   cfg80211: regulat...
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  {
  	const struct ieee80211_freq_range *freq_range = &rule->freq_range;
  	const struct ieee80211_freq_range *freq_range_tmp;
  	const struct ieee80211_reg_rule *tmp;
  	u32 start_freq, end_freq, idx, no;
  
  	for (idx = 0; idx < rd->n_reg_rules; idx++)
  		if (rule == &rd->reg_rules[idx])
  			break;
  
  	if (idx == rd->n_reg_rules)
  		return 0;
  
  	/* get start_freq */
  	no = idx;
  
  	while (no) {
  		tmp = &rd->reg_rules[--no];
  		freq_range_tmp = &tmp->freq_range;
  
  		if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz)
  			break;
975248208   Janusz Dziedzic   cfg80211: regulat...
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
  		freq_range = freq_range_tmp;
  	}
  
  	start_freq = freq_range->start_freq_khz;
  
  	/* get end_freq */
  	freq_range = &rule->freq_range;
  	no = idx;
  
  	while (no < rd->n_reg_rules - 1) {
  		tmp = &rd->reg_rules[++no];
  		freq_range_tmp = &tmp->freq_range;
  
  		if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz)
  			break;
975248208   Janusz Dziedzic   cfg80211: regulat...
643
644
645
646
647
648
649
  		freq_range = freq_range_tmp;
  	}
  
  	end_freq = freq_range->end_freq_khz;
  
  	return end_freq - start_freq;
  }
a6d4a534e   Arik Nemtsov   cfg80211: introdu...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
  unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
  				   const struct ieee80211_reg_rule *rule)
  {
  	unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule);
  
  	if (rule->flags & NL80211_RRF_NO_160MHZ)
  		bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80));
  	if (rule->flags & NL80211_RRF_NO_80MHZ)
  		bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40));
  
  	/*
  	 * HT40+/HT40- limits are handled per-channel. Only limit BW if both
  	 * are not allowed.
  	 */
  	if (rule->flags & NL80211_RRF_NO_HT40MINUS &&
  	    rule->flags & NL80211_RRF_NO_HT40PLUS)
  		bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20));
  
  	return bw;
  }
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
670
  /* Sanity check on a regulatory rule */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
671
  static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
8318d78a4   Johannes Berg   cfg80211 API for ...
672
  {
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
673
  	const struct ieee80211_freq_range *freq_range = &rule->freq_range;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
674
  	u32 freq_diff;
91e990041   Luis R. Rodriguez   cfg80211: mark ne...
675
  	if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
676
677
678
679
680
681
  		return false;
  
  	if (freq_range->start_freq_khz > freq_range->end_freq_khz)
  		return false;
  
  	freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
bd05f28e1   Roel Kluin   cfg80211: test be...
682
  	if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
1a9193185   Johannes Berg   regulatory: code ...
683
  	    freq_range->max_bandwidth_khz > freq_diff)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
684
685
686
687
  		return false;
  
  	return true;
  }
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
688
  static bool is_valid_rd(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
689
  {
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
690
  	const struct ieee80211_reg_rule *reg_rule = NULL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
691
  	unsigned int i;
8318d78a4   Johannes Berg   cfg80211 API for ...
692

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
693
694
  	if (!rd->n_reg_rules)
  		return false;
8318d78a4   Johannes Berg   cfg80211 API for ...
695

88dc1c3f7   Luis R. Rodriguez   cfg80211: mark re...
696
697
  	if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
  		return false;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
698
699
700
701
702
703
704
  	for (i = 0; i < rd->n_reg_rules; i++) {
  		reg_rule = &rd->reg_rules[i];
  		if (!is_valid_reg_rule(reg_rule))
  			return false;
  	}
  
  	return true;
8318d78a4   Johannes Berg   cfg80211 API for ...
705
  }
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
706
707
708
709
710
711
712
  /**
   * freq_in_rule_band - tells us if a frequency is in a frequency band
   * @freq_range: frequency rule we want to query
   * @freq_khz: frequency we are inquiring about
   *
   * This lets us know if a specific frequency rule is or is not relevant to
   * a specific frequency's band. Bands are device specific and artificial
64629b9d4   Vladimir Kondratiev   cfg80211: Fix reg...
713
714
715
716
717
   * definitions (the "2.4 GHz band", the "5 GHz band" and the "60GHz band"),
   * however it is safe for now to assume that a frequency rule should not be
   * part of a frequency's band if the start freq or end freq are off by more
   * than 2 GHz for the 2.4 and 5 GHz bands, and by more than 10 GHz for the
   * 60 GHz band.
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
718
719
720
721
   * This resolution can be lowered and should be considered as we add
   * regulatory rule support for other "bands".
   **/
  static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
1a9193185   Johannes Berg   regulatory: code ...
722
  			      u32 freq_khz)
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
723
724
  {
  #define ONE_GHZ_IN_KHZ	1000000
64629b9d4   Vladimir Kondratiev   cfg80211: Fix reg...
725
726
727
728
729
730
731
732
  	/*
  	 * From 802.11ad: directional multi-gigabit (DMG):
  	 * Pertaining to operation in a frequency band containing a channel
  	 * with the Channel starting frequency above 45 GHz.
  	 */
  	u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ?
  			10 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ;
  	if (abs(freq_khz - freq_range->start_freq_khz) <= limit)
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
733
  		return true;
64629b9d4   Vladimir Kondratiev   cfg80211: Fix reg...
734
  	if (abs(freq_khz - freq_range->end_freq_khz) <= limit)
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
735
736
737
738
  		return true;
  	return false;
  #undef ONE_GHZ_IN_KHZ
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
739
  /*
adbfb0581   Luis R. Rodriguez   cfg80211: interse...
740
741
742
743
744
745
746
747
748
749
750
751
752
753
   * Later on we can perhaps use the more restrictive DFS
   * region but we don't have information for that yet so
   * for now simply disallow conflicts.
   */
  static enum nl80211_dfs_regions
  reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
  			 const enum nl80211_dfs_regions dfs_region2)
  {
  	if (dfs_region1 != dfs_region2)
  		return NL80211_DFS_UNSET;
  	return dfs_region1;
  }
  
  /*
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
754
755
756
   * Helper for regdom_intersect(), this does the real
   * mathematical intersection fun
   */
975248208   Janusz Dziedzic   cfg80211: regulat...
757
758
759
  static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
  			       const struct ieee80211_regdomain *rd2,
  			       const struct ieee80211_reg_rule *rule1,
1a9193185   Johannes Berg   regulatory: code ...
760
761
  			       const struct ieee80211_reg_rule *rule2,
  			       struct ieee80211_reg_rule *intersected_rule)
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
762
763
764
765
766
  {
  	const struct ieee80211_freq_range *freq_range1, *freq_range2;
  	struct ieee80211_freq_range *freq_range;
  	const struct ieee80211_power_rule *power_rule1, *power_rule2;
  	struct ieee80211_power_rule *power_rule;
975248208   Janusz Dziedzic   cfg80211: regulat...
767
  	u32 freq_diff, max_bandwidth1, max_bandwidth2;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
768
769
770
771
772
773
774
775
776
777
  
  	freq_range1 = &rule1->freq_range;
  	freq_range2 = &rule2->freq_range;
  	freq_range = &intersected_rule->freq_range;
  
  	power_rule1 = &rule1->power_rule;
  	power_rule2 = &rule2->power_rule;
  	power_rule = &intersected_rule->power_rule;
  
  	freq_range->start_freq_khz = max(freq_range1->start_freq_khz,
1a9193185   Johannes Berg   regulatory: code ...
778
  					 freq_range2->start_freq_khz);
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
779
  	freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
1a9193185   Johannes Berg   regulatory: code ...
780
  				       freq_range2->end_freq_khz);
975248208   Janusz Dziedzic   cfg80211: regulat...
781
782
783
  
  	max_bandwidth1 = freq_range1->max_bandwidth_khz;
  	max_bandwidth2 = freq_range2->max_bandwidth_khz;
b0dfd2ea1   Janusz Dziedzic   cfg80211: regulat...
784
785
786
787
  	if (rule1->flags & NL80211_RRF_AUTO_BW)
  		max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1);
  	if (rule2->flags & NL80211_RRF_AUTO_BW)
  		max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2);
975248208   Janusz Dziedzic   cfg80211: regulat...
788
789
  
  	freq_range->max_bandwidth_khz = min(max_bandwidth1, max_bandwidth2);
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
790

b0dfd2ea1   Janusz Dziedzic   cfg80211: regulat...
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
  	intersected_rule->flags = rule1->flags | rule2->flags;
  
  	/*
  	 * In case NL80211_RRF_AUTO_BW requested for both rules
  	 * set AUTO_BW in intersected rule also. Next we will
  	 * calculate BW correctly in handle_channel function.
  	 * In other case remove AUTO_BW flag while we calculate
  	 * maximum bandwidth correctly and auto calculation is
  	 * not required.
  	 */
  	if ((rule1->flags & NL80211_RRF_AUTO_BW) &&
  	    (rule2->flags & NL80211_RRF_AUTO_BW))
  		intersected_rule->flags |= NL80211_RRF_AUTO_BW;
  	else
  		intersected_rule->flags &= ~NL80211_RRF_AUTO_BW;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
806
807
808
809
810
811
812
813
  	freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
  	if (freq_range->max_bandwidth_khz > freq_diff)
  		freq_range->max_bandwidth_khz = freq_diff;
  
  	power_rule->max_eirp = min(power_rule1->max_eirp,
  		power_rule2->max_eirp);
  	power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
  		power_rule2->max_antenna_gain);
089027e57   Janusz Dziedzic   cfg80211: regulat...
814
815
  	intersected_rule->dfs_cac_ms = max(rule1->dfs_cac_ms,
  					   rule2->dfs_cac_ms);
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
816
817
818
819
820
  	if (!is_valid_reg_rule(intersected_rule))
  		return -EINVAL;
  
  	return 0;
  }
a62a1aed3   Eliad Peller   cfg80211: avoid d...
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
  /* check whether old rule contains new rule */
  static bool rule_contains(struct ieee80211_reg_rule *r1,
  			  struct ieee80211_reg_rule *r2)
  {
  	/* for simplicity, currently consider only same flags */
  	if (r1->flags != r2->flags)
  		return false;
  
  	/* verify r1 is more restrictive */
  	if ((r1->power_rule.max_antenna_gain >
  	     r2->power_rule.max_antenna_gain) ||
  	    r1->power_rule.max_eirp > r2->power_rule.max_eirp)
  		return false;
  
  	/* make sure r2's range is contained within r1 */
  	if (r1->freq_range.start_freq_khz > r2->freq_range.start_freq_khz ||
  	    r1->freq_range.end_freq_khz < r2->freq_range.end_freq_khz)
  		return false;
  
  	/* and finally verify that r1.max_bw >= r2.max_bw */
  	if (r1->freq_range.max_bandwidth_khz <
  	    r2->freq_range.max_bandwidth_khz)
  		return false;
  
  	return true;
  }
  
  /* add or extend current rules. do nothing if rule is already contained */
  static void add_rule(struct ieee80211_reg_rule *rule,
  		     struct ieee80211_reg_rule *reg_rules, u32 *n_rules)
  {
  	struct ieee80211_reg_rule *tmp_rule;
  	int i;
  
  	for (i = 0; i < *n_rules; i++) {
  		tmp_rule = &reg_rules[i];
  		/* rule is already contained - do nothing */
  		if (rule_contains(tmp_rule, rule))
  			return;
  
  		/* extend rule if possible */
  		if (rule_contains(rule, tmp_rule)) {
  			memcpy(tmp_rule, rule, sizeof(*rule));
  			return;
  		}
  	}
  
  	memcpy(&reg_rules[*n_rules], rule, sizeof(*rule));
  	(*n_rules)++;
  }
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
871
872
873
874
875
876
877
878
879
880
881
882
883
  /**
   * regdom_intersect - do the intersection between two regulatory domains
   * @rd1: first regulatory domain
   * @rd2: second regulatory domain
   *
   * Use this function to get the intersection between two regulatory domains.
   * Once completed we will mark the alpha2 for the rd as intersected, "98",
   * as no one single alpha2 can represent this regulatory domain.
   *
   * Returns a pointer to the regulatory domain structure which will hold the
   * resulting intersection of rules between rd1 and rd2. We will
   * kzalloc() this structure for you.
   */
1a9193185   Johannes Berg   regulatory: code ...
884
885
886
  static struct ieee80211_regdomain *
  regdom_intersect(const struct ieee80211_regdomain *rd1,
  		 const struct ieee80211_regdomain *rd2)
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
887
888
889
  {
  	int r, size_of_regd;
  	unsigned int x, y;
a62a1aed3   Eliad Peller   cfg80211: avoid d...
890
  	unsigned int num_rules = 0;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
891
  	const struct ieee80211_reg_rule *rule1, *rule2;
a62a1aed3   Eliad Peller   cfg80211: avoid d...
892
  	struct ieee80211_reg_rule intersected_rule;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
893
  	struct ieee80211_regdomain *rd;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
894
895
896
  
  	if (!rd1 || !rd2)
  		return NULL;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
897
898
  	/*
  	 * First we get a count of the rules we'll need, then we actually
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
899
900
901
  	 * build them. This is to so we can malloc() and free() a
  	 * regdomain once. The reason we use reg_rules_intersect() here
  	 * is it will return -EINVAL if the rule computed makes no sense.
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
902
903
  	 * All rules that do check out OK are valid.
  	 */
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
904
905
906
907
908
  
  	for (x = 0; x < rd1->n_reg_rules; x++) {
  		rule1 = &rd1->reg_rules[x];
  		for (y = 0; y < rd2->n_reg_rules; y++) {
  			rule2 = &rd2->reg_rules[y];
975248208   Janusz Dziedzic   cfg80211: regulat...
909
  			if (!reg_rules_intersect(rd1, rd2, rule1, rule2,
a62a1aed3   Eliad Peller   cfg80211: avoid d...
910
  						 &intersected_rule))
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
911
  				num_rules++;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
912
913
914
915
916
917
918
  		}
  	}
  
  	if (!num_rules)
  		return NULL;
  
  	size_of_regd = sizeof(struct ieee80211_regdomain) +
82f208563   Johannes Berg   regulatory: don't...
919
  		       num_rules * sizeof(struct ieee80211_reg_rule);
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
920
921
922
923
  
  	rd = kzalloc(size_of_regd, GFP_KERNEL);
  	if (!rd)
  		return NULL;
a62a1aed3   Eliad Peller   cfg80211: avoid d...
924
  	for (x = 0; x < rd1->n_reg_rules; x++) {
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
925
  		rule1 = &rd1->reg_rules[x];
a62a1aed3   Eliad Peller   cfg80211: avoid d...
926
  		for (y = 0; y < rd2->n_reg_rules; y++) {
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
927
  			rule2 = &rd2->reg_rules[y];
975248208   Janusz Dziedzic   cfg80211: regulat...
928
  			r = reg_rules_intersect(rd1, rd2, rule1, rule2,
a62a1aed3   Eliad Peller   cfg80211: avoid d...
929
  						&intersected_rule);
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
930
931
932
933
  			/*
  			 * No need to memset here the intersected rule here as
  			 * we're not using the stack anymore
  			 */
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
934
935
  			if (r)
  				continue;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
936

a62a1aed3   Eliad Peller   cfg80211: avoid d...
937
938
939
  			add_rule(&intersected_rule, rd->reg_rules,
  				 &rd->n_reg_rules);
  		}
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
940
  	}
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
941
942
  	rd->alpha2[0] = '9';
  	rd->alpha2[1] = '8';
adbfb0581   Luis R. Rodriguez   cfg80211: interse...
943
944
  	rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region,
  						  rd2->dfs_region);
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
945
946
947
  
  	return rd;
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
948
949
950
951
  /*
   * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
   * want to just have the channel structure use these
   */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
952
953
954
  static u32 map_regdom_flags(u32 rd_flags)
  {
  	u32 channel_flags = 0;
8fe02e167   Luis R. Rodriguez   cfg80211: consoli...
955
956
  	if (rd_flags & NL80211_RRF_NO_IR_ALL)
  		channel_flags |= IEEE80211_CHAN_NO_IR;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
957
958
  	if (rd_flags & NL80211_RRF_DFS)
  		channel_flags |= IEEE80211_CHAN_RADAR;
03f6b0843   Seth Forshee   cfg80211: add cha...
959
960
  	if (rd_flags & NL80211_RRF_NO_OFDM)
  		channel_flags |= IEEE80211_CHAN_NO_OFDM;
570dbde13   David Spinadel   cfg80211: Add ind...
961
962
  	if (rd_flags & NL80211_RRF_NO_OUTDOOR)
  		channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;
06f207fc5   Arik Nemtsov   cfg80211: change ...
963
964
  	if (rd_flags & NL80211_RRF_IR_CONCURRENT)
  		channel_flags |= IEEE80211_CHAN_IR_CONCURRENT;
a6d4a534e   Arik Nemtsov   cfg80211: introdu...
965
966
967
968
969
970
971
972
  	if (rd_flags & NL80211_RRF_NO_HT40MINUS)
  		channel_flags |= IEEE80211_CHAN_NO_HT40MINUS;
  	if (rd_flags & NL80211_RRF_NO_HT40PLUS)
  		channel_flags |= IEEE80211_CHAN_NO_HT40PLUS;
  	if (rd_flags & NL80211_RRF_NO_80MHZ)
  		channel_flags |= IEEE80211_CHAN_NO_80MHZ;
  	if (rd_flags & NL80211_RRF_NO_160MHZ)
  		channel_flags |= IEEE80211_CHAN_NO_160MHZ;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
973
974
  	return channel_flags;
  }
361c9c8b0   Johannes Berg   regulatory: use I...
975
  static const struct ieee80211_reg_rule *
491728746   Michal Sojka   cfg80211: reg: Re...
976
  freq_reg_info_regd(u32 center_freq,
4edd56981   Matthias May   cfg80211: regulat...
977
  		   const struct ieee80211_regdomain *regd, u32 bw)
8318d78a4   Johannes Berg   cfg80211 API for ...
978
979
  {
  	int i;
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
980
  	bool band_rule_found = false;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
981
  	bool bw_fits = false;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
982
  	if (!regd)
361c9c8b0   Johannes Berg   regulatory: use I...
983
  		return ERR_PTR(-EINVAL);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
984

3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
985
  	for (i = 0; i < regd->n_reg_rules; i++) {
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
986
987
  		const struct ieee80211_reg_rule *rr;
  		const struct ieee80211_freq_range *fr = NULL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
988

3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
989
  		rr = &regd->reg_rules[i];
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
990
  		fr = &rr->freq_range;
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
991

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
992
993
  		/*
  		 * We only need to know if one frequency rule was
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
994
  		 * was in center_freq's band, that's enough, so lets
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
995
996
  		 * not overwrite it once found
  		 */
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
997
998
  		if (!band_rule_found)
  			band_rule_found = freq_in_rule_band(fr, center_freq);
4787cfa08   Rafał Miłecki   cfg80211: move fu...
999
  		bw_fits = cfg80211_does_bw_fit_range(fr, center_freq, bw);
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
1000

361c9c8b0   Johannes Berg   regulatory: use I...
1001
1002
  		if (band_rule_found && bw_fits)
  			return rr;
8318d78a4   Johannes Berg   cfg80211 API for ...
1003
  	}
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
1004
  	if (!band_rule_found)
361c9c8b0   Johannes Berg   regulatory: use I...
1005
  		return ERR_PTR(-ERANGE);
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
1006

361c9c8b0   Johannes Berg   regulatory: use I...
1007
  	return ERR_PTR(-EINVAL);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1008
  }
8de1c63ba   Johannes Berg   wireless: make __...
1009
1010
  static const struct ieee80211_reg_rule *
  __freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 min_bw)
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1011
  {
4edd56981   Matthias May   cfg80211: regulat...
1012
1013
1014
  	const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy);
  	const struct ieee80211_reg_rule *reg_rule = NULL;
  	u32 bw;
1a9193185   Johannes Berg   regulatory: code ...
1015

4edd56981   Matthias May   cfg80211: regulat...
1016
  	for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
491728746   Michal Sojka   cfg80211: reg: Re...
1017
  		reg_rule = freq_reg_info_regd(center_freq, regd, bw);
4edd56981   Matthias May   cfg80211: regulat...
1018
1019
1020
  		if (!IS_ERR(reg_rule))
  			return reg_rule;
  	}
5d885b999   Johannes Berg   regulatory: simpl...
1021

4edd56981   Matthias May   cfg80211: regulat...
1022
1023
1024
1025
1026
1027
1028
  	return reg_rule;
  }
  
  const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
  					       u32 center_freq)
  {
  	return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1029
  }
4f366c5da   John W. Linville   wireless: only us...
1030
  EXPORT_SYMBOL(freq_reg_info);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1031

034c6d6e6   Luis R. Rodriguez   cfg80211: export ...
1032
  const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1033
1034
1035
  {
  	switch (initiator) {
  	case NL80211_REGDOM_SET_BY_CORE:
034c6d6e6   Luis R. Rodriguez   cfg80211: export ...
1036
  		return "core";
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1037
  	case NL80211_REGDOM_SET_BY_USER:
034c6d6e6   Luis R. Rodriguez   cfg80211: export ...
1038
  		return "user";
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1039
  	case NL80211_REGDOM_SET_BY_DRIVER:
034c6d6e6   Luis R. Rodriguez   cfg80211: export ...
1040
  		return "driver";
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1041
  	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
034c6d6e6   Luis R. Rodriguez   cfg80211: export ...
1042
  		return "country IE";
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1043
1044
  	default:
  		WARN_ON(1);
034c6d6e6   Luis R. Rodriguez   cfg80211: export ...
1045
  		return "bug";
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1046
1047
  	}
  }
034c6d6e6   Luis R. Rodriguez   cfg80211: export ...
1048
  EXPORT_SYMBOL(reg_initiator_name);
e702d3cf2   Luis R. Rodriguez   cfg80211: add deb...
1049

1aeb135f8   Michal Sojka   cfg80211: reg: Re...
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
  static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd,
  					  const struct ieee80211_reg_rule *reg_rule,
  					  const struct ieee80211_channel *chan)
  {
  	const struct ieee80211_freq_range *freq_range = NULL;
  	u32 max_bandwidth_khz, bw_flags = 0;
  
  	freq_range = &reg_rule->freq_range;
  
  	max_bandwidth_khz = freq_range->max_bandwidth_khz;
  	/* Check if auto calculation requested */
  	if (reg_rule->flags & NL80211_RRF_AUTO_BW)
  		max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
  
  	/* If we get a reg_rule we can assume that at least 5Mhz fit */
4787cfa08   Rafał Miłecki   cfg80211: move fu...
1065
1066
1067
  	if (!cfg80211_does_bw_fit_range(freq_range,
  					MHZ_TO_KHZ(chan->center_freq),
  					MHZ_TO_KHZ(10)))
1aeb135f8   Michal Sojka   cfg80211: reg: Re...
1068
  		bw_flags |= IEEE80211_CHAN_NO_10MHZ;
4787cfa08   Rafał Miłecki   cfg80211: move fu...
1069
1070
1071
  	if (!cfg80211_does_bw_fit_range(freq_range,
  					MHZ_TO_KHZ(chan->center_freq),
  					MHZ_TO_KHZ(20)))
1aeb135f8   Michal Sojka   cfg80211: reg: Re...
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
  		bw_flags |= IEEE80211_CHAN_NO_20MHZ;
  
  	if (max_bandwidth_khz < MHZ_TO_KHZ(10))
  		bw_flags |= IEEE80211_CHAN_NO_10MHZ;
  	if (max_bandwidth_khz < MHZ_TO_KHZ(20))
  		bw_flags |= IEEE80211_CHAN_NO_20MHZ;
  	if (max_bandwidth_khz < MHZ_TO_KHZ(40))
  		bw_flags |= IEEE80211_CHAN_NO_HT40;
  	if (max_bandwidth_khz < MHZ_TO_KHZ(80))
  		bw_flags |= IEEE80211_CHAN_NO_80MHZ;
  	if (max_bandwidth_khz < MHZ_TO_KHZ(160))
  		bw_flags |= IEEE80211_CHAN_NO_160MHZ;
  	return bw_flags;
  }
e33e2241e   Johannes Berg   Revert "cfg80211:...
1086
1087
1088
1089
  /*
   * Note that right now we assume the desired channel bandwidth
   * is always 20 MHz for each individual channel (HT40 uses 20 MHz
   * per channel, the primary and the extension channel).
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1090
   */
7ca43d03b   Luis R. Rodriguez   cfg80211: pass th...
1091
1092
  static void handle_channel(struct wiphy *wiphy,
  			   enum nl80211_reg_initiator initiator,
fdc9d7b28   Johannes Berg   regulatory: remov...
1093
  			   struct ieee80211_channel *chan)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1094
  {
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1095
  	u32 flags, bw_flags = 0;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1096
1097
  	const struct ieee80211_reg_rule *reg_rule = NULL;
  	const struct ieee80211_power_rule *power_rule = NULL;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1098
  	struct wiphy *request_wiphy = NULL;
c492db370   Johannes Berg   regulatory: use R...
1099
  	struct regulatory_request *lr = get_last_request();
975248208   Janusz Dziedzic   cfg80211: regulat...
1100
  	const struct ieee80211_regdomain *regd;
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
1101

c492db370   Johannes Berg   regulatory: use R...
1102
  	request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
1103
1104
  
  	flags = chan->orig_flags;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1105

361c9c8b0   Johannes Berg   regulatory: use I...
1106
1107
  	reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
  	if (IS_ERR(reg_rule)) {
ca4ffe8f2   Luis R. Rodriguez   cfg80211: fix dis...
1108
1109
  		/*
  		 * We will disable all channels that do not match our
25985edce   Lucas De Marchi   Fix common misspe...
1110
  		 * received regulatory rule unless the hint is coming
ca4ffe8f2   Luis R. Rodriguez   cfg80211: fix dis...
1111
1112
1113
1114
1115
1116
1117
1118
  		 * from a Country IE and the Country IE had no information
  		 * about a band. The IEEE 802.11 spec allows for an AP
  		 * to send only a subset of the regulatory rules allowed,
  		 * so an AP in the US that only supports 2.4 GHz may only send
  		 * a country IE with information for the 2.4 GHz band
  		 * while 5 GHz is still supported.
  		 */
  		if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
361c9c8b0   Johannes Berg   regulatory: use I...
1119
  		    PTR_ERR(reg_rule) == -ERANGE)
ca4ffe8f2   Luis R. Rodriguez   cfg80211: fix dis...
1120
  			return;
cc493e4f5   Luis R. Rodriguez   cfg80211: enforce...
1121
1122
  		if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
  		    request_wiphy && request_wiphy == wiphy &&
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1123
  		    request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
1124
1125
1126
  			pr_debug("Disabling freq %d MHz for good
  ",
  				 chan->center_freq);
cc493e4f5   Luis R. Rodriguez   cfg80211: enforce...
1127
1128
1129
  			chan->orig_flags |= IEEE80211_CHAN_DISABLED;
  			chan->flags = chan->orig_flags;
  		} else {
c799ba6ea   Johannes Berg   cfg80211: remove ...
1130
1131
1132
  			pr_debug("Disabling freq %d MHz
  ",
  				 chan->center_freq);
cc493e4f5   Luis R. Rodriguez   cfg80211: enforce...
1133
1134
  			chan->flags |= IEEE80211_CHAN_DISABLED;
  		}
8318d78a4   Johannes Berg   cfg80211 API for ...
1135
  		return;
ca4ffe8f2   Luis R. Rodriguez   cfg80211: fix dis...
1136
  	}
8318d78a4   Johannes Berg   cfg80211 API for ...
1137

b0dfd2ea1   Janusz Dziedzic   cfg80211: regulat...
1138
  	regd = reg_get_regdomain(wiphy);
e702d3cf2   Luis R. Rodriguez   cfg80211: add deb...
1139

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1140
  	power_rule = &reg_rule->power_rule;
1aeb135f8   Michal Sojka   cfg80211: reg: Re...
1141
  	bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1142

c492db370   Johannes Berg   regulatory: use R...
1143
  	if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1144
  	    request_wiphy && request_wiphy == wiphy &&
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1145
  	    request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1146
  		/*
25985edce   Lucas De Marchi   Fix common misspe...
1147
  		 * This guarantees the driver's requested regulatory domain
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
1148
  		 * will always be used as a base for further regulatory
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1149
1150
  		 * settings
  		 */
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
1151
  		chan->flags = chan->orig_flags =
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1152
  			map_regdom_flags(reg_rule->flags) | bw_flags;
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
1153
1154
  		chan->max_antenna_gain = chan->orig_mag =
  			(int) MBI_TO_DBI(power_rule->max_antenna_gain);
279f0f552   Felix Fietkau   cfg80211: fix ini...
1155
  		chan->max_reg_power = chan->max_power = chan->orig_mpwr =
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
1156
  			(int) MBM_TO_DBM(power_rule->max_eirp);
4f267c119   Janusz Dziedzic   cfg80211: reg: se...
1157
1158
1159
1160
1161
1162
  
  		if (chan->flags & IEEE80211_CHAN_RADAR) {
  			chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
  			if (reg_rule->dfs_cac_ms)
  				chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
  		}
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
1163
1164
  		return;
  	}
04f39047a   Simon Wunderlich   nl80211/cfg80211:...
1165
1166
  	chan->dfs_state = NL80211_DFS_USABLE;
  	chan->dfs_state_entered = jiffies;
aa3d7eef3   Rajkumar Manoharan   wireless: Reset b...
1167
  	chan->beacon_found = false;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1168
  	chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
1a9193185   Johannes Berg   regulatory: code ...
1169
1170
1171
  	chan->max_antenna_gain =
  		min_t(int, chan->orig_mag,
  		      MBI_TO_DBI(power_rule->max_antenna_gain));
eccc068e8   Hong Wu   wireless: Save or...
1172
  	chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
089027e57   Janusz Dziedzic   cfg80211: regulat...
1173
1174
1175
1176
1177
1178
1179
  
  	if (chan->flags & IEEE80211_CHAN_RADAR) {
  		if (reg_rule->dfs_cac_ms)
  			chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
  		else
  			chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
  	}
5e31fc081   Stanislaw Gruszka   wireless: reg: re...
1180
1181
  	if (chan->orig_mpwr) {
  		/*
a09a85a01   Luis R. Rodriguez   cfg80211: add fla...
1182
1183
  		 * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
  		 * will always follow the passed country IE power settings.
5e31fc081   Stanislaw Gruszka   wireless: reg: re...
1184
1185
  		 */
  		if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
a09a85a01   Luis R. Rodriguez   cfg80211: add fla...
1186
  		    wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER)
5e31fc081   Stanislaw Gruszka   wireless: reg: re...
1187
1188
1189
1190
1191
1192
  			chan->max_power = chan->max_reg_power;
  		else
  			chan->max_power = min(chan->orig_mpwr,
  					      chan->max_reg_power);
  	} else
  		chan->max_power = chan->max_reg_power;
8318d78a4   Johannes Berg   cfg80211 API for ...
1193
  }
7ca43d03b   Luis R. Rodriguez   cfg80211: pass th...
1194
  static void handle_band(struct wiphy *wiphy,
fdc9d7b28   Johannes Berg   regulatory: remov...
1195
1196
  			enum nl80211_reg_initiator initiator,
  			struct ieee80211_supported_band *sband)
8318d78a4   Johannes Berg   cfg80211 API for ...
1197
  {
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
1198
  	unsigned int i;
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
1199

fdc9d7b28   Johannes Berg   regulatory: remov...
1200
1201
  	if (!sband)
  		return;
8318d78a4   Johannes Berg   cfg80211 API for ...
1202
1203
  
  	for (i = 0; i < sband->n_channels; i++)
fdc9d7b28   Johannes Berg   regulatory: remov...
1204
  		handle_channel(wiphy, initiator, &sband->channels[i]);
8318d78a4   Johannes Berg   cfg80211 API for ...
1205
  }
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1206
1207
1208
1209
  static bool reg_request_cell_base(struct regulatory_request *request)
  {
  	if (request->initiator != NL80211_REGDOM_SET_BY_USER)
  		return false;
1a9193185   Johannes Berg   regulatory: code ...
1210
  	return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE;
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1211
1212
1213
1214
  }
  
  bool reg_last_request_cell_base(void)
  {
38fd2143f   Johannes Berg   regulatory: remov...
1215
  	return reg_request_cell_base(get_last_request());
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1216
  }
94fc661f6   Ilan Peer   cfg80211: Add Kco...
1217
  #ifdef CONFIG_CFG80211_REG_CELLULAR_HINTS
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1218
  /* Core specific check */
2f92212b7   Johannes Berg   regulatory: use p...
1219
1220
  static enum reg_request_treatment
  reg_ignore_cell_hint(struct regulatory_request *pending_request)
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1221
  {
c492db370   Johannes Berg   regulatory: use R...
1222
  	struct regulatory_request *lr = get_last_request();
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1223
  	if (!reg_num_devs_support_basehint)
2f92212b7   Johannes Berg   regulatory: use p...
1224
  		return REG_REQ_IGNORE;
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1225

c492db370   Johannes Berg   regulatory: use R...
1226
  	if (reg_request_cell_base(lr) &&
1a9193185   Johannes Berg   regulatory: code ...
1227
  	    !regdom_changes(pending_request->alpha2))
2f92212b7   Johannes Berg   regulatory: use p...
1228
  		return REG_REQ_ALREADY_SET;
1a9193185   Johannes Berg   regulatory: code ...
1229

2f92212b7   Johannes Berg   regulatory: use p...
1230
  	return REG_REQ_OK;
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1231
1232
1233
1234
1235
  }
  
  /* Device specific check */
  static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
  {
1a9193185   Johannes Berg   regulatory: code ...
1236
  	return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS);
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1237
1238
  }
  #else
a515de660   Johannes Berg   cfg80211: reg: fi...
1239
1240
  static enum reg_request_treatment
  reg_ignore_cell_hint(struct regulatory_request *pending_request)
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1241
  {
2f92212b7   Johannes Berg   regulatory: use p...
1242
  	return REG_REQ_IGNORE;
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1243
  }
1a9193185   Johannes Berg   regulatory: code ...
1244
1245
  
  static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1246
1247
1248
1249
  {
  	return true;
  }
  #endif
fa1fb9cb1   Luis R. Rodriguez   cfg80211: simplif...
1250
1251
  static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
  {
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1252
1253
  	if (wiphy->regulatory_flags & REGULATORY_STRICT_REG &&
  	    !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG))
fa1fb9cb1   Luis R. Rodriguez   cfg80211: simplif...
1254
1255
1256
  		return true;
  	return false;
  }
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1257

7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1258
1259
  static bool ignore_reg_update(struct wiphy *wiphy,
  			      enum nl80211_reg_initiator initiator)
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
1260
  {
c492db370   Johannes Berg   regulatory: use R...
1261
  	struct regulatory_request *lr = get_last_request();
b0d7aa595   Jonathan Doron   cfg80211: allow w...
1262
1263
  	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
  		return true;
c492db370   Johannes Berg   regulatory: use R...
1264
  	if (!lr) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
1265
1266
1267
  		pr_debug("Ignoring regulatory request set by %s since last_request is not set
  ",
  			 reg_initiator_name(initiator));
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
1268
  		return true;
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1269
  	}
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1270
  	if (initiator == NL80211_REGDOM_SET_BY_CORE &&
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1271
  	    wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
1272
1273
1274
  		pr_debug("Ignoring regulatory request set by %s since the driver uses its own custom regulatory domain
  ",
  			 reg_initiator_name(initiator));
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
1275
  		return true;
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1276
  	}
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1277
1278
1279
1280
  	/*
  	 * wiphy->regd will be set once the device has its own
  	 * desired regulatory domain set
  	 */
fa1fb9cb1   Luis R. Rodriguez   cfg80211: simplif...
1281
  	if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
749b527b2   Luis R. Rodriguez   cfg80211: fix all...
1282
  	    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
c492db370   Johannes Berg   regulatory: use R...
1283
  	    !is_world_regdom(lr->alpha2)) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
1284
1285
1286
  		pr_debug("Ignoring regulatory request set by %s since the driver requires its own regulatory domain to be set first
  ",
  			 reg_initiator_name(initiator));
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
1287
  		return true;
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
1288
  	}
c492db370   Johannes Berg   regulatory: use R...
1289
  	if (reg_request_cell_base(lr))
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
1290
  		return reg_dev_ignore_cell_hint(wiphy);
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
1291
1292
  	return false;
  }
3195e489a   Luis R. Rodriguez   cfg80211: move re...
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
  static bool reg_is_world_roaming(struct wiphy *wiphy)
  {
  	const struct ieee80211_regdomain *cr = get_cfg80211_regdom();
  	const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy);
  	struct regulatory_request *lr = get_last_request();
  
  	if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2)))
  		return true;
  
  	if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1303
  	    wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
3195e489a   Luis R. Rodriguez   cfg80211: move re...
1304
1305
1306
1307
  		return true;
  
  	return false;
  }
1a9193185   Johannes Berg   regulatory: code ...
1308
  static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1309
1310
  			      struct reg_beacon *reg_beacon)
  {
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1311
1312
  	struct ieee80211_supported_band *sband;
  	struct ieee80211_channel *chan;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
1313
1314
  	bool channel_changed = false;
  	struct ieee80211_channel chan_before;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1315

e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1316
1317
1318
1319
1320
  	sband = wiphy->bands[reg_beacon->chan.band];
  	chan = &sband->channels[chan_idx];
  
  	if (likely(chan->center_freq != reg_beacon->chan.center_freq))
  		return;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
1321
1322
1323
1324
  	if (chan->beacon_found)
  		return;
  
  	chan->beacon_found = true;
0f500a5f6   Luis R. Rodriguez   cfg80211: move wo...
1325
1326
  	if (!reg_is_world_roaming(wiphy))
  		return;
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1327
  	if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS)
371842448   Luis R. Rodriguez   cfg80211: fix reg...
1328
  		return;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
1329
1330
  	chan_before.center_freq = chan->center_freq;
  	chan_before.flags = chan->flags;
8fe02e167   Luis R. Rodriguez   cfg80211: consoli...
1331
1332
  	if (chan->flags & IEEE80211_CHAN_NO_IR) {
  		chan->flags &= ~IEEE80211_CHAN_NO_IR;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
1333
  		channel_changed = true;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1334
  	}
6bad87666   Luis R. Rodriguez   cfg80211: send re...
1335
1336
  	if (channel_changed)
  		nl80211_send_beacon_hint_event(wiphy, &chan_before, chan);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
  }
  
  /*
   * Called when a scan on a wiphy finds a beacon on
   * new channel
   */
  static void wiphy_update_new_beacon(struct wiphy *wiphy,
  				    struct reg_beacon *reg_beacon)
  {
  	unsigned int i;
  	struct ieee80211_supported_band *sband;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
  	if (!wiphy->bands[reg_beacon->chan.band])
  		return;
  
  	sband = wiphy->bands[reg_beacon->chan.band];
  
  	for (i = 0; i < sband->n_channels; i++)
  		handle_reg_beacon(wiphy, i, reg_beacon);
  }
  
  /*
   * Called upon reg changes or a new wiphy is added
   */
  static void wiphy_update_beacon_reg(struct wiphy *wiphy)
  {
  	unsigned int i;
  	struct ieee80211_supported_band *sband;
  	struct reg_beacon *reg_beacon;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1365
1366
1367
1368
1369
1370
1371
1372
  	list_for_each_entry(reg_beacon, &reg_beacon_list, list) {
  		if (!wiphy->bands[reg_beacon->chan.band])
  			continue;
  		sband = wiphy->bands[reg_beacon->chan.band];
  		for (i = 0; i < sband->n_channels; i++)
  			handle_reg_beacon(wiphy, i, reg_beacon);
  	}
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1373
1374
1375
  /* Reap the advantages of previously found beacons */
  static void reg_process_beacons(struct wiphy *wiphy)
  {
b1ed8ddd2   Luis R. Rodriguez   cfg80211: fix bug...
1376
1377
1378
1379
1380
1381
  	/*
  	 * Means we are just firing up cfg80211, so no beacons would
  	 * have been processed yet.
  	 */
  	if (!last_request)
  		return;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1382
1383
  	wiphy_update_beacon_reg(wiphy);
  }
1a9193185   Johannes Berg   regulatory: code ...
1384
  static bool is_ht40_allowed(struct ieee80211_channel *chan)
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1385
1386
  {
  	if (!chan)
1a9193185   Johannes Berg   regulatory: code ...
1387
  		return false;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1388
  	if (chan->flags & IEEE80211_CHAN_DISABLED)
1a9193185   Johannes Berg   regulatory: code ...
1389
  		return false;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1390
  	/* This would happen when regulatory rules disallow HT40 completely */
55b183ad8   Felix Fietkau   wireless: fix reg...
1391
1392
1393
  	if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40)
  		return false;
  	return true;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1394
1395
1396
  }
  
  static void reg_process_ht_flags_channel(struct wiphy *wiphy,
fdc9d7b28   Johannes Berg   regulatory: remov...
1397
  					 struct ieee80211_channel *channel)
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1398
  {
fdc9d7b28   Johannes Berg   regulatory: remov...
1399
  	struct ieee80211_supported_band *sband = wiphy->bands[channel->band];
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1400
  	struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
4e0854a74   Emmanuel Grumbach   cfg80211: honor N...
1401
  	const struct ieee80211_regdomain *regd;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1402
  	unsigned int i;
4e0854a74   Emmanuel Grumbach   cfg80211: honor N...
1403
  	u32 flags;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1404

1a9193185   Johannes Berg   regulatory: code ...
1405
  	if (!is_ht40_allowed(channel)) {
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
  		channel->flags |= IEEE80211_CHAN_NO_HT40;
  		return;
  	}
  
  	/*
  	 * We need to ensure the extension channels exist to
  	 * be able to use HT40- or HT40+, this finds them (or not)
  	 */
  	for (i = 0; i < sband->n_channels; i++) {
  		struct ieee80211_channel *c = &sband->channels[i];
1a9193185   Johannes Berg   regulatory: code ...
1416

038659e7c   Luis R. Rodriguez   cfg80211: Process...
1417
1418
1419
1420
1421
  		if (c->center_freq == (channel->center_freq - 20))
  			channel_before = c;
  		if (c->center_freq == (channel->center_freq + 20))
  			channel_after = c;
  	}
4e0854a74   Emmanuel Grumbach   cfg80211: honor N...
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
  	flags = 0;
  	regd = get_wiphy_regdom(wiphy);
  	if (regd) {
  		const struct ieee80211_reg_rule *reg_rule =
  			freq_reg_info_regd(MHZ_TO_KHZ(channel->center_freq),
  					   regd, MHZ_TO_KHZ(20));
  
  		if (!IS_ERR(reg_rule))
  			flags = reg_rule->flags;
  	}
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1432
1433
1434
1435
1436
  	/*
  	 * Please note that this assumes target bandwidth is 20 MHz,
  	 * if that ever changes we also need to change the below logic
  	 * to include that as well.
  	 */
4e0854a74   Emmanuel Grumbach   cfg80211: honor N...
1437
1438
  	if (!is_ht40_allowed(channel_before) ||
  	    flags & NL80211_RRF_NO_HT40MINUS)
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1439
  		channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1440
  	else
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1441
  		channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1442

4e0854a74   Emmanuel Grumbach   cfg80211: honor N...
1443
1444
  	if (!is_ht40_allowed(channel_after) ||
  	    flags & NL80211_RRF_NO_HT40PLUS)
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1445
  		channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1446
  	else
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1447
  		channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1448
1449
1450
  }
  
  static void reg_process_ht_flags_band(struct wiphy *wiphy,
fdc9d7b28   Johannes Berg   regulatory: remov...
1451
  				      struct ieee80211_supported_band *sband)
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1452
1453
  {
  	unsigned int i;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1454

fdc9d7b28   Johannes Berg   regulatory: remov...
1455
1456
  	if (!sband)
  		return;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1457
1458
  
  	for (i = 0; i < sband->n_channels; i++)
fdc9d7b28   Johannes Berg   regulatory: remov...
1459
  		reg_process_ht_flags_channel(wiphy, &sband->channels[i]);
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1460
1461
1462
1463
  }
  
  static void reg_process_ht_flags(struct wiphy *wiphy)
  {
57fbcce37   Johannes Berg   cfg80211: remove ...
1464
  	enum nl80211_band band;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1465
1466
1467
  
  	if (!wiphy)
  		return;
57fbcce37   Johannes Berg   cfg80211: remove ...
1468
  	for (band = 0; band < NUM_NL80211_BANDS; band++)
fdc9d7b28   Johannes Berg   regulatory: remov...
1469
  		reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1470
  }
0e3802dbf   Luis R. Rodriguez   cfg80211: move co...
1471
1472
1473
1474
1475
1476
  static void reg_call_notifier(struct wiphy *wiphy,
  			      struct regulatory_request *request)
  {
  	if (wiphy->reg_notifier)
  		wiphy->reg_notifier(wiphy, request);
  }
ad932f046   Arik Nemtsov   cfg80211: leave i...
1477
1478
  static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
  {
ad932f046   Arik Nemtsov   cfg80211: leave i...
1479
1480
  	struct cfg80211_chan_def chandef;
  	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
20658702e   Arik Nemtsov   cfg80211: fix dea...
1481
  	enum nl80211_iftype iftype;
ad932f046   Arik Nemtsov   cfg80211: leave i...
1482
1483
  
  	wdev_lock(wdev);
20658702e   Arik Nemtsov   cfg80211: fix dea...
1484
  	iftype = wdev->iftype;
ad932f046   Arik Nemtsov   cfg80211: leave i...
1485

20658702e   Arik Nemtsov   cfg80211: fix dea...
1486
  	/* make sure the interface is active */
ad932f046   Arik Nemtsov   cfg80211: leave i...
1487
  	if (!wdev->netdev || !netif_running(wdev->netdev))
20658702e   Arik Nemtsov   cfg80211: fix dea...
1488
  		goto wdev_inactive_unlock;
ad932f046   Arik Nemtsov   cfg80211: leave i...
1489

20658702e   Arik Nemtsov   cfg80211: fix dea...
1490
  	switch (iftype) {
ad932f046   Arik Nemtsov   cfg80211: leave i...
1491
1492
1493
  	case NL80211_IFTYPE_AP:
  	case NL80211_IFTYPE_P2P_GO:
  		if (!wdev->beacon_interval)
20658702e   Arik Nemtsov   cfg80211: fix dea...
1494
1495
  			goto wdev_inactive_unlock;
  		chandef = wdev->chandef;
ad932f046   Arik Nemtsov   cfg80211: leave i...
1496
  		break;
185076d6d   Arik Nemtsov   cfg80211: correct...
1497
1498
  	case NL80211_IFTYPE_ADHOC:
  		if (!wdev->ssid_len)
20658702e   Arik Nemtsov   cfg80211: fix dea...
1499
1500
  			goto wdev_inactive_unlock;
  		chandef = wdev->chandef;
185076d6d   Arik Nemtsov   cfg80211: correct...
1501
  		break;
ad932f046   Arik Nemtsov   cfg80211: leave i...
1502
1503
  	case NL80211_IFTYPE_STATION:
  	case NL80211_IFTYPE_P2P_CLIENT:
ad932f046   Arik Nemtsov   cfg80211: leave i...
1504
1505
  		if (!wdev->current_bss ||
  		    !wdev->current_bss->pub.channel)
20658702e   Arik Nemtsov   cfg80211: fix dea...
1506
  			goto wdev_inactive_unlock;
ad932f046   Arik Nemtsov   cfg80211: leave i...
1507

20658702e   Arik Nemtsov   cfg80211: fix dea...
1508
1509
1510
1511
1512
  		if (!rdev->ops->get_channel ||
  		    rdev_get_channel(rdev, wdev, &chandef))
  			cfg80211_chandef_create(&chandef,
  						wdev->current_bss->pub.channel,
  						NL80211_CHAN_NO_HT);
ad932f046   Arik Nemtsov   cfg80211: leave i...
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
  		break;
  	case NL80211_IFTYPE_MONITOR:
  	case NL80211_IFTYPE_AP_VLAN:
  	case NL80211_IFTYPE_P2P_DEVICE:
  		/* no enforcement required */
  		break;
  	default:
  		/* others not implemented for now */
  		WARN_ON(1);
  		break;
  	}
ad932f046   Arik Nemtsov   cfg80211: leave i...
1524
  	wdev_unlock(wdev);
20658702e   Arik Nemtsov   cfg80211: fix dea...
1525
1526
1527
1528
1529
  
  	switch (iftype) {
  	case NL80211_IFTYPE_AP:
  	case NL80211_IFTYPE_P2P_GO:
  	case NL80211_IFTYPE_ADHOC:
923b352f1   Arik Nemtsov   cfg80211: use RTN...
1530
  		return cfg80211_reg_can_beacon_relax(wiphy, &chandef, iftype);
20658702e   Arik Nemtsov   cfg80211: fix dea...
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
  	case NL80211_IFTYPE_STATION:
  	case NL80211_IFTYPE_P2P_CLIENT:
  		return cfg80211_chandef_usable(wiphy, &chandef,
  					       IEEE80211_CHAN_DISABLED);
  	default:
  		break;
  	}
  
  	return true;
  
  wdev_inactive_unlock:
  	wdev_unlock(wdev);
  	return true;
ad932f046   Arik Nemtsov   cfg80211: leave i...
1544
1545
1546
1547
1548
1549
1550
1551
  }
  
  static void reg_leave_invalid_chans(struct wiphy *wiphy)
  {
  	struct wireless_dev *wdev;
  	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
  
  	ASSERT_RTNL();
53873f134   Johannes Berg   cfg80211: make wd...
1552
  	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
ad932f046   Arik Nemtsov   cfg80211: leave i...
1553
1554
1555
1556
1557
1558
1559
  		if (!reg_wdev_chan_valid(wiphy, wdev))
  			cfg80211_leave(rdev, wdev);
  }
  
  static void reg_check_chans_work(struct work_struct *work)
  {
  	struct cfg80211_registered_device *rdev;
c799ba6ea   Johannes Berg   cfg80211: remove ...
1560
1561
  	pr_debug("Verifying active interfaces after reg change
  ");
ad932f046   Arik Nemtsov   cfg80211: leave i...
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
  	rtnl_lock();
  
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list)
  		if (!(rdev->wiphy.regulatory_flags &
  		      REGULATORY_IGNORE_STALE_KICKOFF))
  			reg_leave_invalid_chans(&rdev->wiphy);
  
  	rtnl_unlock();
  }
  
  static void reg_check_channels(void)
  {
  	/*
  	 * Give usermode a chance to do something nicer (move to another
  	 * channel, orderly disconnection), before forcing a disconnection.
  	 */
  	mod_delayed_work(system_power_efficient_wq,
  			 &reg_check_chans,
  			 msecs_to_jiffies(REG_ENFORCE_GRACE_MS));
  }
eac03e381   Sven Neumann   cfg80211: hold re...
1582
1583
  static void wiphy_update_regulatory(struct wiphy *wiphy,
  				    enum nl80211_reg_initiator initiator)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1584
  {
57fbcce37   Johannes Berg   cfg80211: remove ...
1585
  	enum nl80211_band band;
c492db370   Johannes Berg   regulatory: use R...
1586
  	struct regulatory_request *lr = get_last_request();
eac03e381   Sven Neumann   cfg80211: hold re...
1587

0e3802dbf   Luis R. Rodriguez   cfg80211: move co...
1588
1589
1590
1591
1592
1593
1594
  	if (ignore_reg_update(wiphy, initiator)) {
  		/*
  		 * Regulatory updates set by CORE are ignored for custom
  		 * regulatory cards. Let us notify the changes to the driver,
  		 * as some drivers used this to restore its orig_* reg domain.
  		 */
  		if (initiator == NL80211_REGDOM_SET_BY_CORE &&
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1595
  		    wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
0e3802dbf   Luis R. Rodriguez   cfg80211: move co...
1596
  			reg_call_notifier(wiphy, lr);
a203c2aa4   Sven Neumann   cfg80211: really ...
1597
  		return;
0e3802dbf   Luis R. Rodriguez   cfg80211: move co...
1598
  	}
a203c2aa4   Sven Neumann   cfg80211: really ...
1599

c492db370   Johannes Berg   regulatory: use R...
1600
  	lr->dfs_region = get_cfg80211_regdom()->dfs_region;
b68e6b3b3   Luis R. Rodriguez   cfg80211: pass DF...
1601

57fbcce37   Johannes Berg   cfg80211: remove ...
1602
  	for (band = 0; band < NUM_NL80211_BANDS; band++)
fdc9d7b28   Johannes Berg   regulatory: remov...
1603
  		handle_band(wiphy, initiator, wiphy->bands[band]);
a203c2aa4   Sven Neumann   cfg80211: really ...
1604

e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1605
  	reg_process_beacons(wiphy);
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1606
  	reg_process_ht_flags(wiphy);
0e3802dbf   Luis R. Rodriguez   cfg80211: move co...
1607
  	reg_call_notifier(wiphy, lr);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1608
  }
d7549cbb9   Sven Neumann   cfg80211: reorder...
1609
1610
1611
  static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
  {
  	struct cfg80211_registered_device *rdev;
4a38994f1   Rajkumar Manoharan   cfg80211: notify ...
1612
  	struct wiphy *wiphy;
d7549cbb9   Sven Neumann   cfg80211: reorder...
1613

5fe231e87   Johannes Berg   cfg80211: vastly ...
1614
  	ASSERT_RTNL();
458f4f9e9   Johannes Berg   regulatory: use R...
1615

4a38994f1   Rajkumar Manoharan   cfg80211: notify ...
1616
1617
1618
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
  		wiphy = &rdev->wiphy;
  		wiphy_update_regulatory(wiphy, initiator);
4a38994f1   Rajkumar Manoharan   cfg80211: notify ...
1619
  	}
ad932f046   Arik Nemtsov   cfg80211: leave i...
1620
1621
  
  	reg_check_channels();
d7549cbb9   Sven Neumann   cfg80211: reorder...
1622
  }
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1623
  static void handle_channel_custom(struct wiphy *wiphy,
fdc9d7b28   Johannes Berg   regulatory: remov...
1624
  				  struct ieee80211_channel *chan,
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1625
1626
  				  const struct ieee80211_regdomain *regd)
  {
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1627
  	u32 bw_flags = 0;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1628
1629
  	const struct ieee80211_reg_rule *reg_rule = NULL;
  	const struct ieee80211_power_rule *power_rule = NULL;
4edd56981   Matthias May   cfg80211: regulat...
1630
  	u32 bw;
ac46d48e0   Luis R. Rodriguez   cfg80211: fix rac...
1631

4edd56981   Matthias May   cfg80211: regulat...
1632
  	for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
491728746   Michal Sojka   cfg80211: reg: Re...
1633
  		reg_rule = freq_reg_info_regd(MHZ_TO_KHZ(chan->center_freq),
4edd56981   Matthias May   cfg80211: regulat...
1634
1635
1636
1637
  					      regd, bw);
  		if (!IS_ERR(reg_rule))
  			break;
  	}
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1638

361c9c8b0   Johannes Berg   regulatory: use I...
1639
  	if (IS_ERR(reg_rule)) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
1640
1641
1642
  		pr_debug("Disabling freq %d MHz as custom regd has no rule that fits it
  ",
  			 chan->center_freq);
db8dfee57   Arik Nemtsov   cfg80211: avoid i...
1643
1644
1645
1646
1647
1648
  		if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
  			chan->flags |= IEEE80211_CHAN_DISABLED;
  		} else {
  			chan->orig_flags |= IEEE80211_CHAN_DISABLED;
  			chan->flags = chan->orig_flags;
  		}
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1649
1650
1651
1652
  		return;
  	}
  
  	power_rule = &reg_rule->power_rule;
1aeb135f8   Michal Sojka   cfg80211: reg: Re...
1653
  	bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1654

2e18b38fc   Arik Nemtsov   cfg80211: update ...
1655
  	chan->dfs_state_entered = jiffies;
c7ab50819   Arik Nemtsov   cfg80211: explici...
1656
1657
1658
  	chan->dfs_state = NL80211_DFS_USABLE;
  
  	chan->beacon_found = false;
db8dfee57   Arik Nemtsov   cfg80211: avoid i...
1659
1660
1661
1662
1663
1664
  
  	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
  		chan->flags = chan->orig_flags | bw_flags |
  			      map_regdom_flags(reg_rule->flags);
  	else
  		chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1665
  	chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
279f0f552   Felix Fietkau   cfg80211: fix ini...
1666
1667
  	chan->max_reg_power = chan->max_power =
  		(int) MBM_TO_DBM(power_rule->max_eirp);
2e18b38fc   Arik Nemtsov   cfg80211: update ...
1668
1669
1670
1671
1672
1673
1674
1675
1676
  
  	if (chan->flags & IEEE80211_CHAN_RADAR) {
  		if (reg_rule->dfs_cac_ms)
  			chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
  		else
  			chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
  	}
  
  	chan->max_power = chan->max_reg_power;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1677
  }
fdc9d7b28   Johannes Berg   regulatory: remov...
1678
1679
  static void handle_band_custom(struct wiphy *wiphy,
  			       struct ieee80211_supported_band *sband,
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1680
1681
1682
  			       const struct ieee80211_regdomain *regd)
  {
  	unsigned int i;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1683

fdc9d7b28   Johannes Berg   regulatory: remov...
1684
1685
  	if (!sband)
  		return;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1686
1687
  
  	for (i = 0; i < sband->n_channels; i++)
fdc9d7b28   Johannes Berg   regulatory: remov...
1688
  		handle_channel_custom(wiphy, &sband->channels[i], regd);
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1689
1690
1691
1692
1693
1694
  }
  
  /* Used by drivers prior to wiphy registration */
  void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
  				   const struct ieee80211_regdomain *regd)
  {
57fbcce37   Johannes Berg   cfg80211: remove ...
1695
  	enum nl80211_band band;
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1696
  	unsigned int bands_set = 0;
ac46d48e0   Luis R. Rodriguez   cfg80211: fix rac...
1697

a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
1698
1699
1700
1701
  	WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG),
  	     "wiphy should have REGULATORY_CUSTOM_REG
  ");
  	wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
222ea5819   Luis R. Rodriguez   cfg80211: force W...
1702

57fbcce37   Johannes Berg   cfg80211: remove ...
1703
  	for (band = 0; band < NUM_NL80211_BANDS; band++) {
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1704
1705
  		if (!wiphy->bands[band])
  			continue;
fdc9d7b28   Johannes Berg   regulatory: remov...
1706
  		handle_band_custom(wiphy, wiphy->bands[band], regd);
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1707
  		bands_set++;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1708
  	}
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1709
1710
1711
  
  	/*
  	 * no point in calling this if it won't have any effect
1a9193185   Johannes Berg   regulatory: code ...
1712
  	 * on your device's supported bands.
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1713
1714
  	 */
  	WARN_ON(!bands_set);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1715
  }
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1716
  EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1717
1718
1719
  static void reg_set_request_processed(void)
  {
  	bool need_more_processing = false;
c492db370   Johannes Berg   regulatory: use R...
1720
  	struct regulatory_request *lr = get_last_request();
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1721

c492db370   Johannes Berg   regulatory: use R...
1722
  	lr->processed = true;
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1723
1724
1725
1726
1727
  
  	spin_lock(&reg_requests_lock);
  	if (!list_empty(&reg_requests_list))
  		need_more_processing = true;
  	spin_unlock(&reg_requests_lock);
b68630369   Johannes Berg   cfg80211: reg: ma...
1728
  	cancel_crda_timeout();
a90c7a313   Luis R. Rodriguez   cfg80211: add a t...
1729

b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1730
1731
1732
  	if (need_more_processing)
  		schedule_work(&reg_work);
  }
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1733
  /**
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
1734
1735
1736
1737
1738
   * reg_process_hint_core - process core regulatory requests
   * @pending_request: a pending core regulatory request
   *
   * The wireless subsystem can use this function to process
   * a regulatory request issued by the regulatory core.
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
1739
   */
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1740
1741
  static enum reg_request_treatment
  reg_process_hint_core(struct regulatory_request *core_request)
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
1742
  {
cecbb069c   Johannes Berg   cfg80211: reg: re...
1743
  	if (reg_query_database(core_request)) {
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1744
1745
1746
  		core_request->intersect = false;
  		core_request->processed = false;
  		reg_update_last_request(core_request);
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1747
  		return REG_REQ_OK;
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1748
  	}
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1749
1750
  
  	return REG_REQ_IGNORE;
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
1751
  }
0d97a6191   Luis R. Rodriguez   cfg80211: process...
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
  static enum reg_request_treatment
  __reg_process_hint_user(struct regulatory_request *user_request)
  {
  	struct regulatory_request *lr = get_last_request();
  
  	if (reg_request_cell_base(user_request))
  		return reg_ignore_cell_hint(user_request);
  
  	if (reg_request_cell_base(lr))
  		return REG_REQ_IGNORE;
  
  	if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
  		return REG_REQ_INTERSECT;
  	/*
  	 * If the user knows better the user should set the regdom
  	 * to their country before the IE is picked up
  	 */
  	if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
  	    lr->intersect)
  		return REG_REQ_IGNORE;
  	/*
  	 * Process user requests only after previous user/driver/core
  	 * requests have been processed
  	 */
  	if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
  	     lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
  	     lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
  	    regdom_changes(lr->alpha2))
  		return REG_REQ_IGNORE;
  
  	if (!regdom_changes(user_request->alpha2))
  		return REG_REQ_ALREADY_SET;
  
  	return REG_REQ_OK;
  }
  
  /**
   * reg_process_hint_user - process user regulatory requests
   * @user_request: a pending user regulatory request
   *
   * The wireless subsystem can use this function to process
   * a regulatory request initiated by userspace.
0d97a6191   Luis R. Rodriguez   cfg80211: process...
1794
   */
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1795
1796
  static enum reg_request_treatment
  reg_process_hint_user(struct regulatory_request *user_request)
0d97a6191   Luis R. Rodriguez   cfg80211: process...
1797
1798
  {
  	enum reg_request_treatment treatment;
0d97a6191   Luis R. Rodriguez   cfg80211: process...
1799
1800
1801
  
  	treatment = __reg_process_hint_user(user_request);
  	if (treatment == REG_REQ_IGNORE ||
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1802
1803
  	    treatment == REG_REQ_ALREADY_SET)
  		return REG_REQ_IGNORE;
0d97a6191   Luis R. Rodriguez   cfg80211: process...
1804

0d97a6191   Luis R. Rodriguez   cfg80211: process...
1805
1806
  	user_request->intersect = treatment == REG_REQ_INTERSECT;
  	user_request->processed = false;
5ad6ef5e0   Luis R. Rodriguez   cfg80211: add hel...
1807

cecbb069c   Johannes Berg   cfg80211: reg: re...
1808
  	if (reg_query_database(user_request)) {
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1809
1810
1811
  		reg_update_last_request(user_request);
  		user_alpha2[0] = user_request->alpha2[0];
  		user_alpha2[1] = user_request->alpha2[1];
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1812
  		return REG_REQ_OK;
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1813
  	}
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1814
1815
  
  	return REG_REQ_IGNORE;
0d97a6191   Luis R. Rodriguez   cfg80211: process...
1816
  }
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
  static enum reg_request_treatment
  __reg_process_hint_driver(struct regulatory_request *driver_request)
  {
  	struct regulatory_request *lr = get_last_request();
  
  	if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
  		if (regdom_changes(driver_request->alpha2))
  			return REG_REQ_OK;
  		return REG_REQ_ALREADY_SET;
  	}
  
  	/*
  	 * This would happen if you unplug and plug your card
  	 * back in or if you add a new device for which the previously
  	 * loaded card also agrees on the regulatory domain.
  	 */
  	if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
  	    !regdom_changes(driver_request->alpha2))
  		return REG_REQ_ALREADY_SET;
  
  	return REG_REQ_INTERSECT;
  }
  
  /**
   * reg_process_hint_driver - process driver regulatory requests
   * @driver_request: a pending driver regulatory request
   *
   * The wireless subsystem can use this function to process
   * a regulatory request issued by an 802.11 driver.
   *
   * Returns one of the different reg request treatment values.
   */
  static enum reg_request_treatment
  reg_process_hint_driver(struct wiphy *wiphy,
  			struct regulatory_request *driver_request)
  {
34f05f543   Arik Nemtsov   cfg80211: avoid m...
1853
  	const struct ieee80211_regdomain *regd, *tmp;
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1854
  	enum reg_request_treatment treatment;
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1855
1856
1857
1858
1859
1860
1861
  
  	treatment = __reg_process_hint_driver(driver_request);
  
  	switch (treatment) {
  	case REG_REQ_OK:
  		break;
  	case REG_REQ_IGNORE:
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1862
  		return REG_REQ_IGNORE;
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1863
  	case REG_REQ_INTERSECT:
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1864
1865
  	case REG_REQ_ALREADY_SET:
  		regd = reg_copy_regd(get_cfg80211_regdom());
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1866
1867
  		if (IS_ERR(regd))
  			return REG_REQ_IGNORE;
34f05f543   Arik Nemtsov   cfg80211: avoid m...
1868
1869
  
  		tmp = get_wiphy_regdom(wiphy);
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1870
  		rcu_assign_pointer(wiphy->regd, regd);
34f05f543   Arik Nemtsov   cfg80211: avoid m...
1871
  		rcu_free_regdom(tmp);
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1872
  	}
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1873
1874
1875
  
  	driver_request->intersect = treatment == REG_REQ_INTERSECT;
  	driver_request->processed = false;
5ad6ef5e0   Luis R. Rodriguez   cfg80211: add hel...
1876

21636c7fa   Luis R. Rodriguez   cfg80211: process...
1877
1878
1879
1880
1881
1882
1883
  	/*
  	 * Since CRDA will not be called in this case as we already
  	 * have applied the requested regulatory domain before we just
  	 * inform userspace we have processed the request
  	 */
  	if (treatment == REG_REQ_ALREADY_SET) {
  		nl80211_send_reg_change_event(driver_request);
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1884
  		reg_update_last_request(driver_request);
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1885
  		reg_set_request_processed();
480908a7e   Johannes Berg   cfg80211: reg: cl...
1886
  		return REG_REQ_ALREADY_SET;
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1887
  	}
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1888
  	if (reg_query_database(driver_request)) {
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1889
  		reg_update_last_request(driver_request);
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1890
1891
  		return REG_REQ_OK;
  	}
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1892

d34265a3e   Johannes Berg   cfg80211: reg: ce...
1893
  	return REG_REQ_IGNORE;
21636c7fa   Luis R. Rodriguez   cfg80211: process...
1894
  }
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
  static enum reg_request_treatment
  __reg_process_hint_country_ie(struct wiphy *wiphy,
  			      struct regulatory_request *country_ie_request)
  {
  	struct wiphy *last_wiphy = NULL;
  	struct regulatory_request *lr = get_last_request();
  
  	if (reg_request_cell_base(lr)) {
  		/* Trust a Cell base station over the AP's country IE */
  		if (regdom_changes(country_ie_request->alpha2))
  			return REG_REQ_IGNORE;
  		return REG_REQ_ALREADY_SET;
2a901468c   Luis R. Rodriguez   cfg80211: add an ...
1907
1908
1909
  	} else {
  		if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE)
  			return REG_REQ_IGNORE;
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1910
  	}
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1911
1912
  	if (unlikely(!is_an_alpha2(country_ie_request->alpha2)))
  		return -EINVAL;
2f1c6c572   Luis R. Rodriguez   cfg80211: process...
1913
1914
1915
1916
1917
1918
1919
  
  	if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)
  		return REG_REQ_OK;
  
  	last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
  
  	if (last_wiphy != wiphy) {
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1920
  		/*
2f1c6c572   Luis R. Rodriguez   cfg80211: process...
1921
1922
1923
1924
  		 * Two cards with two APs claiming different
  		 * Country IE alpha2s. We could
  		 * intersect them, but that seems unlikely
  		 * to be correct. Reject second one for now.
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1925
  		 */
2f1c6c572   Luis R. Rodriguez   cfg80211: process...
1926
1927
  		if (regdom_changes(country_ie_request->alpha2))
  			return REG_REQ_IGNORE;
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1928
1929
  		return REG_REQ_ALREADY_SET;
  	}
70dcec5a4   Emmanuel Grumbach   cfg80211: don't W...
1930
1931
  
  	if (regdom_changes(country_ie_request->alpha2))
2f1c6c572   Luis R. Rodriguez   cfg80211: process...
1932
1933
  		return REG_REQ_OK;
  	return REG_REQ_ALREADY_SET;
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1934
  }
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
1935
  /**
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1936
1937
   * reg_process_hint_country_ie - process regulatory requests from country IEs
   * @country_ie_request: a regulatory request from a country IE
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1938
   *
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1939
1940
   * The wireless subsystem can use this function to process
   * a regulatory request issued by a country Information Element.
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1941
   *
2f92212b7   Johannes Berg   regulatory: use p...
1942
   * Returns one of the different reg request treatment values.
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1943
   */
2f92212b7   Johannes Berg   regulatory: use p...
1944
  static enum reg_request_treatment
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1945
1946
  reg_process_hint_country_ie(struct wiphy *wiphy,
  			    struct regulatory_request *country_ie_request)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1947
  {
2f92212b7   Johannes Berg   regulatory: use p...
1948
  	enum reg_request_treatment treatment;
761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
1949

b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1950
  	treatment = __reg_process_hint_country_ie(wiphy, country_ie_request);
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1951

2f92212b7   Johannes Berg   regulatory: use p...
1952
  	switch (treatment) {
2f92212b7   Johannes Berg   regulatory: use p...
1953
1954
  	case REG_REQ_OK:
  		break;
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1955
  	case REG_REQ_IGNORE:
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1956
  		return REG_REQ_IGNORE;
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1957
  	case REG_REQ_ALREADY_SET:
c888393b7   Arik Nemtsov   cfg80211: avoid f...
1958
  		reg_free_request(country_ie_request);
480908a7e   Johannes Berg   cfg80211: reg: cl...
1959
  		return REG_REQ_ALREADY_SET;
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1960
  	case REG_REQ_INTERSECT:
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1961
  		/*
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1962
1963
  		 * This doesn't happen yet, not sure we
  		 * ever want to support it for this case.
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1964
  		 */
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1965
  		WARN_ONCE(1, "Unexpected intersection for country IEs");
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1966
  		return REG_REQ_IGNORE;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1967
  	}
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1968

b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
1969
1970
  	country_ie_request->intersect = false;
  	country_ie_request->processed = false;
5ad6ef5e0   Luis R. Rodriguez   cfg80211: add hel...
1971

d34265a3e   Johannes Berg   cfg80211: reg: ce...
1972
  	if (reg_query_database(country_ie_request)) {
25b20dbdc   Johannes Berg   cfg80211: reg: fi...
1973
  		reg_update_last_request(country_ie_request);
d34265a3e   Johannes Berg   cfg80211: reg: ce...
1974
1975
  		return REG_REQ_OK;
  	}
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1976

d34265a3e   Johannes Berg   cfg80211: reg: ce...
1977
  	return REG_REQ_IGNORE;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1978
  }
897667273   Vasanthakumar Thiagarajan   cfg80211: Share C...
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
  bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2)
  {
  	const struct ieee80211_regdomain *wiphy1_regd = NULL;
  	const struct ieee80211_regdomain *wiphy2_regd = NULL;
  	const struct ieee80211_regdomain *cfg80211_regd = NULL;
  	bool dfs_domain_same;
  
  	rcu_read_lock();
  
  	cfg80211_regd = rcu_dereference(cfg80211_regdomain);
  	wiphy1_regd = rcu_dereference(wiphy1->regd);
  	if (!wiphy1_regd)
  		wiphy1_regd = cfg80211_regd;
  
  	wiphy2_regd = rcu_dereference(wiphy2->regd);
  	if (!wiphy2_regd)
  		wiphy2_regd = cfg80211_regd;
  
  	dfs_domain_same = wiphy1_regd->dfs_region == wiphy2_regd->dfs_region;
  
  	rcu_read_unlock();
  
  	return dfs_domain_same;
  }
  
  static void reg_copy_dfs_chan_state(struct ieee80211_channel *dst_chan,
  				    struct ieee80211_channel *src_chan)
  {
  	if (!(dst_chan->flags & IEEE80211_CHAN_RADAR) ||
  	    !(src_chan->flags & IEEE80211_CHAN_RADAR))
  		return;
  
  	if (dst_chan->flags & IEEE80211_CHAN_DISABLED ||
  	    src_chan->flags & IEEE80211_CHAN_DISABLED)
  		return;
  
  	if (src_chan->center_freq == dst_chan->center_freq &&
  	    dst_chan->dfs_state == NL80211_DFS_USABLE) {
  		dst_chan->dfs_state = src_chan->dfs_state;
  		dst_chan->dfs_state_entered = src_chan->dfs_state_entered;
  	}
  }
  
  static void wiphy_share_dfs_chan_state(struct wiphy *dst_wiphy,
  				       struct wiphy *src_wiphy)
  {
  	struct ieee80211_supported_band *src_sband, *dst_sband;
  	struct ieee80211_channel *src_chan, *dst_chan;
  	int i, j, band;
  
  	if (!reg_dfs_domain_same(dst_wiphy, src_wiphy))
  		return;
  
  	for (band = 0; band < NUM_NL80211_BANDS; band++) {
  		dst_sband = dst_wiphy->bands[band];
  		src_sband = src_wiphy->bands[band];
  		if (!dst_sband || !src_sband)
  			continue;
  
  		for (i = 0; i < dst_sband->n_channels; i++) {
  			dst_chan = &dst_sband->channels[i];
  			for (j = 0; j < src_sband->n_channels; j++) {
  				src_chan = &src_sband->channels[j];
  				reg_copy_dfs_chan_state(dst_chan, src_chan);
  			}
  		}
  	}
  }
  
  static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy)
  {
  	struct cfg80211_registered_device *rdev;
  
  	ASSERT_RTNL();
  
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
  		if (wiphy == &rdev->wiphy)
  			continue;
  		wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy);
  	}
  }
30a548c72   Luis R. Rodriguez   cfg80211: fix com...
2060
  /* This processes *all* regulatory hints */
1daa37c7b   Luis R. Rodriguez   cfg80211: remove ...
2061
  static void reg_process_hint(struct regulatory_request *reg_request)
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2062
  {
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2063
  	struct wiphy *wiphy = NULL;
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
2064
  	enum reg_request_treatment treatment;
492a81318   Yu Zhao   cfg80211: fix use...
2065
  	enum nl80211_reg_initiator initiator = reg_request->initiator;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2066

f41737669   Johannes Berg   cfg80211: remove ...
2067
  	if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2068
  		wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
492a81318   Yu Zhao   cfg80211: fix use...
2069
  	switch (initiator) {
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
2070
  	case NL80211_REGDOM_SET_BY_CORE:
d34265a3e   Johannes Berg   cfg80211: reg: ce...
2071
2072
  		treatment = reg_process_hint_core(reg_request);
  		break;
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
2073
  	case NL80211_REGDOM_SET_BY_USER:
d34265a3e   Johannes Berg   cfg80211: reg: ce...
2074
2075
  		treatment = reg_process_hint_user(reg_request);
  		break;
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
2076
  	case NL80211_REGDOM_SET_BY_DRIVER:
772f03893   Ilan Peer   cfg80211: fix few...
2077
2078
  		if (!wiphy)
  			goto out_free;
21636c7fa   Luis R. Rodriguez   cfg80211: process...
2079
2080
  		treatment = reg_process_hint_driver(wiphy, reg_request);
  		break;
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
2081
  	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
772f03893   Ilan Peer   cfg80211: fix few...
2082
2083
  		if (!wiphy)
  			goto out_free;
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
2084
  		treatment = reg_process_hint_country_ie(wiphy, reg_request);
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
2085
2086
  		break;
  	default:
492a81318   Yu Zhao   cfg80211: fix use...
2087
2088
  		WARN(1, "invalid initiator %d
  ", initiator);
772f03893   Ilan Peer   cfg80211: fix few...
2089
  		goto out_free;
b3eb7f3f5   Luis R. Rodriguez   cfg80211: process...
2090
  	}
d34265a3e   Johannes Berg   cfg80211: reg: ce...
2091
2092
  	if (treatment == REG_REQ_IGNORE)
  		goto out_free;
480908a7e   Johannes Berg   cfg80211: reg: cl...
2093
2094
2095
  	WARN(treatment != REG_REQ_OK && treatment != REG_REQ_ALREADY_SET,
  	     "unexpected treatment value %d
  ", treatment);
841b351cf   John Linville   wireless: remove ...
2096
2097
2098
  	/* This is required so that the orig_* parameters are saved.
  	 * NOTE: treatment must be set for any case that reaches here!
  	 */
b23e7a9e6   Luis R. Rodriguez   cfg80211: process...
2099
  	if (treatment == REG_REQ_ALREADY_SET && wiphy &&
ad932f046   Arik Nemtsov   cfg80211: leave i...
2100
  	    wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
492a81318   Yu Zhao   cfg80211: fix use...
2101
  		wiphy_update_regulatory(wiphy, initiator);
897667273   Vasanthakumar Thiagarajan   cfg80211: Share C...
2102
  		wiphy_all_share_dfs_chan_state(wiphy);
ad932f046   Arik Nemtsov   cfg80211: leave i...
2103
2104
  		reg_check_channels();
  	}
772f03893   Ilan Peer   cfg80211: fix few...
2105
2106
2107
2108
  
  	return;
  
  out_free:
c888393b7   Arik Nemtsov   cfg80211: avoid f...
2109
  	reg_free_request(reg_request);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2110
  }
ef51fb1d1   Arik Nemtsov   cfg80211: avoid r...
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
  static bool reg_only_self_managed_wiphys(void)
  {
  	struct cfg80211_registered_device *rdev;
  	struct wiphy *wiphy;
  	bool self_managed_found = false;
  
  	ASSERT_RTNL();
  
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
  		wiphy = &rdev->wiphy;
  		if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
  			self_managed_found = true;
  		else
  			return false;
  	}
  
  	/* make sure at least one self-managed wiphy exists */
  	return self_managed_found;
  }
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
2130
2131
2132
2133
2134
  /*
   * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
   * Regulatory hints come on a first come first serve basis and we
   * must process each one atomically.
   */
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2135
  static void reg_process_pending_hints(void)
b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
2136
  {
c492db370   Johannes Berg   regulatory: use R...
2137
  	struct regulatory_request *reg_request, *lr;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2138

c492db370   Johannes Berg   regulatory: use R...
2139
  	lr = get_last_request();
b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
2140

b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
2141
  	/* When last_request->processed becomes true this will be rescheduled */
c492db370   Johannes Berg   regulatory: use R...
2142
  	if (lr && !lr->processed) {
96cce12ff   Luis R. Rodriguez   cfg80211: fix pro...
2143
  		reg_process_hint(lr);
5fe231e87   Johannes Berg   cfg80211: vastly ...
2144
  		return;
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
2145
  	}
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2146
  	spin_lock(&reg_requests_lock);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2147

b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
2148
  	if (list_empty(&reg_requests_list)) {
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
2149
  		spin_unlock(&reg_requests_lock);
5fe231e87   Johannes Berg   cfg80211: vastly ...
2150
  		return;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2151
  	}
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
2152
2153
2154
2155
2156
  
  	reg_request = list_first_entry(&reg_requests_list,
  				       struct regulatory_request,
  				       list);
  	list_del_init(&reg_request->list);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2157
  	spin_unlock(&reg_requests_lock);
b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
2158

ef51fb1d1   Arik Nemtsov   cfg80211: avoid r...
2159
2160
2161
2162
  	if (reg_only_self_managed_wiphys()) {
  		reg_free_request(reg_request);
  		return;
  	}
1daa37c7b   Luis R. Rodriguez   cfg80211: remove ...
2163
  	reg_process_hint(reg_request);
2e54a6895   Ben   cfg80211: Process...
2164
2165
2166
2167
2168
2169
2170
  
  	lr = get_last_request();
  
  	spin_lock(&reg_requests_lock);
  	if (!list_empty(&reg_requests_list) && lr && lr->processed)
  		schedule_work(&reg_work);
  	spin_unlock(&reg_requests_lock);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2171
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2172
2173
2174
  /* Processes beacon hints -- this has nothing to do with country IEs */
  static void reg_process_pending_beacon_hints(void)
  {
79c97e97a   Johannes Berg   cfg80211: clean u...
2175
  	struct cfg80211_registered_device *rdev;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2176
  	struct reg_beacon *pending_beacon, *tmp;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2177
2178
  	/* This goes through the _pending_ beacon list */
  	spin_lock_bh(&reg_pending_beacons_lock);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2179
2180
  	list_for_each_entry_safe(pending_beacon, tmp,
  				 &reg_pending_beacons, list) {
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2181
2182
2183
  		list_del_init(&pending_beacon->list);
  
  		/* Applies the beacon hint to current wiphys */
79c97e97a   Johannes Berg   cfg80211: clean u...
2184
2185
  		list_for_each_entry(rdev, &cfg80211_rdev_list, list)
  			wiphy_update_new_beacon(&rdev->wiphy, pending_beacon);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2186
2187
2188
2189
2190
2191
  
  		/* Remembers the beacon hint for new wiphys or reg changes */
  		list_add_tail(&pending_beacon->list, &reg_beacon_list);
  	}
  
  	spin_unlock_bh(&reg_pending_beacons_lock);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2192
  }
b0d7aa595   Jonathan Doron   cfg80211: allow w...
2193
2194
2195
2196
2197
2198
  static void reg_process_self_managed_hints(void)
  {
  	struct cfg80211_registered_device *rdev;
  	struct wiphy *wiphy;
  	const struct ieee80211_regdomain *tmp;
  	const struct ieee80211_regdomain *regd;
57fbcce37   Johannes Berg   cfg80211: remove ...
2199
  	enum nl80211_band band;
b0d7aa595   Jonathan Doron   cfg80211: allow w...
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
  	struct regulatory_request request = {};
  
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
  		wiphy = &rdev->wiphy;
  
  		spin_lock(&reg_requests_lock);
  		regd = rdev->requested_regd;
  		rdev->requested_regd = NULL;
  		spin_unlock(&reg_requests_lock);
  
  		if (regd == NULL)
  			continue;
  
  		tmp = get_wiphy_regdom(wiphy);
  		rcu_assign_pointer(wiphy->regd, regd);
  		rcu_free_regdom(tmp);
57fbcce37   Johannes Berg   cfg80211: remove ...
2216
  		for (band = 0; band < NUM_NL80211_BANDS; band++)
b0d7aa595   Jonathan Doron   cfg80211: allow w...
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
  			handle_band_custom(wiphy, wiphy->bands[band], regd);
  
  		reg_process_ht_flags(wiphy);
  
  		request.wiphy_idx = get_wiphy_idx(wiphy);
  		request.alpha2[0] = regd->alpha2[0];
  		request.alpha2[1] = regd->alpha2[1];
  		request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
  
  		nl80211_send_wiphy_reg_change_event(&request);
  	}
  
  	reg_check_channels();
  }
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2231
2232
  static void reg_todo(struct work_struct *work)
  {
5fe231e87   Johannes Berg   cfg80211: vastly ...
2233
  	rtnl_lock();
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2234
  	reg_process_pending_hints();
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2235
  	reg_process_pending_beacon_hints();
b0d7aa595   Jonathan Doron   cfg80211: allow w...
2236
  	reg_process_self_managed_hints();
5fe231e87   Johannes Berg   cfg80211: vastly ...
2237
  	rtnl_unlock();
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2238
  }
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2239
2240
  static void queue_regulatory_request(struct regulatory_request *request)
  {
d4f2c8819   Johannes Berg   regulatory: remov...
2241
2242
  	request->alpha2[0] = toupper(request->alpha2[0]);
  	request->alpha2[1] = toupper(request->alpha2[1]);
c61029c77   John W. Linville   wireless: upcase ...
2243

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2244
2245
2246
2247
2248
2249
  	spin_lock(&reg_requests_lock);
  	list_add_tail(&request->list, &reg_requests_list);
  	spin_unlock(&reg_requests_lock);
  
  	schedule_work(&reg_work);
  }
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2250
2251
2252
2253
  /*
   * Core regulatory hint -- happens during cfg80211_init()
   * and when we restore regulatory settings.
   */
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
2254
2255
2256
  static int regulatory_hint_core(const char *alpha2)
  {
  	struct regulatory_request *request;
1a9193185   Johannes Berg   regulatory: code ...
2257
  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
2258
2259
2260
2261
2262
  	if (!request)
  		return -ENOMEM;
  
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
2263
  	request->initiator = NL80211_REGDOM_SET_BY_CORE;
d46c334f0   Andrei Otcheretianski   cfg80211: reg: In...
2264
  	request->wiphy_idx = WIPHY_IDX_INVALID;
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
2265

31e99729a   Luis R. Rodriguez   cfg80211: put cor...
2266
  	queue_regulatory_request(request);
5078b2e32   Luis R. Rodriguez   cfg80211: fix rac...
2267

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2268
  	return 0;
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
2269
  }
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2270
  /* User hints */
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
2271
2272
  int regulatory_hint_user(const char *alpha2,
  			 enum nl80211_user_reg_hint_type user_reg_hint_type)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2273
  {
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2274
  	struct regulatory_request *request;
fdc9d7b28   Johannes Berg   regulatory: remov...
2275
2276
  	if (WARN_ON(!alpha2))
  		return -EINVAL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2277

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2278
2279
2280
  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
  	if (!request)
  		return -ENOMEM;
f41737669   Johannes Berg   cfg80211: remove ...
2281
  	request->wiphy_idx = WIPHY_IDX_INVALID;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2282
2283
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
e12822e1d   Luis R. Rodriguez   cfg80211: fix syn...
2284
  	request->initiator = NL80211_REGDOM_SET_BY_USER;
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
2285
  	request->user_reg_hint_type = user_reg_hint_type;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2286

c37722bd1   Ilan peer   cfg80211: Stop ca...
2287
  	/* Allow calling CRDA again */
b68630369   Johannes Berg   cfg80211: reg: ma...
2288
  	reset_crda_timeouts();
c37722bd1   Ilan peer   cfg80211: Stop ca...
2289

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2290
2291
2292
2293
  	queue_regulatory_request(request);
  
  	return 0;
  }
7101e4fa0   Nakul Kachhwaha   MLK-17362-01 Chan...
2294
  EXPORT_SYMBOL(regulatory_hint_user);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2295

050507536   Ilan peer   cfg80211: Add API...
2296
  int regulatory_hint_indoor(bool is_indoor, u32 portid)
52616f2b4   Ilan Peer   cfg80211: Add an ...
2297
  {
050507536   Ilan peer   cfg80211: Add API...
2298
  	spin_lock(&reg_indoor_lock);
52616f2b4   Ilan Peer   cfg80211: Add an ...
2299

050507536   Ilan peer   cfg80211: Add API...
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
  	/* It is possible that more than one user space process is trying to
  	 * configure the indoor setting. To handle such cases, clear the indoor
  	 * setting in case that some process does not think that the device
  	 * is operating in an indoor environment. In addition, if a user space
  	 * process indicates that it is controlling the indoor setting, save its
  	 * portid, i.e., make it the owner.
  	 */
  	reg_is_indoor = is_indoor;
  	if (reg_is_indoor) {
  		if (!reg_is_indoor_portid)
  			reg_is_indoor_portid = portid;
  	} else {
  		reg_is_indoor_portid = 0;
  	}
52616f2b4   Ilan Peer   cfg80211: Add an ...
2314

050507536   Ilan peer   cfg80211: Add API...
2315
  	spin_unlock(&reg_indoor_lock);
52616f2b4   Ilan Peer   cfg80211: Add an ...
2316

050507536   Ilan peer   cfg80211: Add API...
2317
2318
  	if (!is_indoor)
  		reg_check_channels();
52616f2b4   Ilan Peer   cfg80211: Add an ...
2319
2320
2321
  
  	return 0;
  }
050507536   Ilan peer   cfg80211: Add API...
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
  void regulatory_netlink_notify(u32 portid)
  {
  	spin_lock(&reg_indoor_lock);
  
  	if (reg_is_indoor_portid != portid) {
  		spin_unlock(&reg_indoor_lock);
  		return;
  	}
  
  	reg_is_indoor = false;
  	reg_is_indoor_portid = 0;
  
  	spin_unlock(&reg_indoor_lock);
  
  	reg_check_channels();
  }
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2338
2339
2340
2341
  /* Driver hints */
  int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
  {
  	struct regulatory_request *request;
fdc9d7b28   Johannes Berg   regulatory: remov...
2342
2343
  	if (WARN_ON(!alpha2 || !wiphy))
  		return -EINVAL;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2344

4f7b91404   Luis R. Rodriguez   cfg80211: make re...
2345
  	wiphy->regulatory_flags &= ~REGULATORY_CUSTOM_REG;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2346
2347
2348
2349
2350
  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
  	if (!request)
  		return -ENOMEM;
  
  	request->wiphy_idx = get_wiphy_idx(wiphy);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2351
2352
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
2353
  	request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2354

c37722bd1   Ilan peer   cfg80211: Stop ca...
2355
  	/* Allow calling CRDA again */
b68630369   Johannes Berg   cfg80211: reg: ma...
2356
  	reset_crda_timeouts();
c37722bd1   Ilan peer   cfg80211: Stop ca...
2357

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2358
2359
2360
  	queue_regulatory_request(request);
  
  	return 0;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2361
2362
  }
  EXPORT_SYMBOL(regulatory_hint);
57fbcce37   Johannes Berg   cfg80211: remove ...
2363
  void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band,
789fd0333   Luis R. Rodriguez   cfg80211: rename ...
2364
  				const u8 *country_ie, u8 country_ie_len)
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2365
  {
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2366
  	char alpha2[2];
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2367
  	enum environment_cap env = ENVIRON_ANY;
db2424c58   Johannes Berg   regulatory: use R...
2368
  	struct regulatory_request *request = NULL, *lr;
d335fe639   Luis R. Rodriguez   cfg80211: protect...
2369

3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2370
2371
  	/* IE len must be evenly divisible by 2 */
  	if (country_ie_len & 0x01)
db2424c58   Johannes Berg   regulatory: use R...
2372
  		return;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2373
2374
  
  	if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
db2424c58   Johannes Berg   regulatory: use R...
2375
2376
2377
2378
2379
  		return;
  
  	request = kzalloc(sizeof(*request), GFP_KERNEL);
  	if (!request)
  		return;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2380

3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2381
2382
2383
2384
2385
2386
2387
  	alpha2[0] = country_ie[0];
  	alpha2[1] = country_ie[1];
  
  	if (country_ie[2] == 'I')
  		env = ENVIRON_INDOOR;
  	else if (country_ie[2] == 'O')
  		env = ENVIRON_OUTDOOR;
db2424c58   Johannes Berg   regulatory: use R...
2388
2389
2390
2391
2392
  	rcu_read_lock();
  	lr = get_last_request();
  
  	if (unlikely(!lr))
  		goto out;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
2393
  	/*
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
2394
  	 * We will run this only upon a successful connection on cfg80211.
4b44c8bc4   Luis R. Rodriguez   cfg80211: do not ...
2395
  	 * We leave conflict resolution to the workqueue, where can hold
5fe231e87   Johannes Berg   cfg80211: vastly ...
2396
  	 * the RTNL.
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
2397
  	 */
c492db370   Johannes Berg   regulatory: use R...
2398
2399
  	if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
  	    lr->wiphy_idx != WIPHY_IDX_INVALID)
4b44c8bc4   Luis R. Rodriguez   cfg80211: do not ...
2400
  		goto out;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2401

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2402
  	request->wiphy_idx = get_wiphy_idx(wiphy);
4f366c5da   John W. Linville   wireless: only us...
2403
2404
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
2405
  	request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2406
  	request->country_ie_env = env;
c37722bd1   Ilan peer   cfg80211: Stop ca...
2407
  	/* Allow calling CRDA again */
b68630369   Johannes Berg   cfg80211: reg: ma...
2408
  	reset_crda_timeouts();
c37722bd1   Ilan peer   cfg80211: Stop ca...
2409

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2410
  	queue_regulatory_request(request);
db2424c58   Johannes Berg   regulatory: use R...
2411
  	request = NULL;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2412
  out:
db2424c58   Johannes Berg   regulatory: use R...
2413
2414
  	kfree(request);
  	rcu_read_unlock();
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2415
  }
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2416

09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
  static void restore_alpha2(char *alpha2, bool reset_user)
  {
  	/* indicates there is no alpha2 to consider for restoration */
  	alpha2[0] = '9';
  	alpha2[1] = '7';
  
  	/* The user setting has precedence over the module parameter */
  	if (is_user_regdom_saved()) {
  		/* Unless we're asked to ignore it and reset it */
  		if (reset_user) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
2427
2428
  			pr_debug("Restoring regulatory settings including user preference
  ");
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2429
2430
2431
2432
2433
2434
2435
2436
2437
  			user_alpha2[0] = '9';
  			user_alpha2[1] = '7';
  
  			/*
  			 * If we're ignoring user settings, we still need to
  			 * check the module parameter to ensure we put things
  			 * back as they were for a full restore.
  			 */
  			if (!is_world_regdom(ieee80211_regdom)) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
2438
2439
2440
  				pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c
  ",
  					 ieee80211_regdom[0], ieee80211_regdom[1]);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2441
2442
2443
2444
  				alpha2[0] = ieee80211_regdom[0];
  				alpha2[1] = ieee80211_regdom[1];
  			}
  		} else {
c799ba6ea   Johannes Berg   cfg80211: remove ...
2445
2446
2447
  			pr_debug("Restoring regulatory settings while preserving user preference for: %c%c
  ",
  				 user_alpha2[0], user_alpha2[1]);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2448
2449
2450
2451
  			alpha2[0] = user_alpha2[0];
  			alpha2[1] = user_alpha2[1];
  		}
  	} else if (!is_world_regdom(ieee80211_regdom)) {
c799ba6ea   Johannes Berg   cfg80211: remove ...
2452
2453
2454
  		pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c
  ",
  			 ieee80211_regdom[0], ieee80211_regdom[1]);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2455
2456
2457
  		alpha2[0] = ieee80211_regdom[0];
  		alpha2[1] = ieee80211_regdom[1];
  	} else
c799ba6ea   Johannes Berg   cfg80211: remove ...
2458
2459
  		pr_debug("Restoring regulatory settings
  ");
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2460
  }
5ce543d14   Rajkumar Manoharan   cfg80211: Restore...
2461
2462
2463
  static void restore_custom_reg_settings(struct wiphy *wiphy)
  {
  	struct ieee80211_supported_band *sband;
57fbcce37   Johannes Berg   cfg80211: remove ...
2464
  	enum nl80211_band band;
5ce543d14   Rajkumar Manoharan   cfg80211: Restore...
2465
2466
  	struct ieee80211_channel *chan;
  	int i;
57fbcce37   Johannes Berg   cfg80211: remove ...
2467
  	for (band = 0; band < NUM_NL80211_BANDS; band++) {
5ce543d14   Rajkumar Manoharan   cfg80211: Restore...
2468
2469
2470
2471
2472
2473
2474
2475
  		sband = wiphy->bands[band];
  		if (!sband)
  			continue;
  		for (i = 0; i < sband->n_channels; i++) {
  			chan = &sband->channels[i];
  			chan->flags = chan->orig_flags;
  			chan->max_antenna_gain = chan->orig_mag;
  			chan->max_power = chan->orig_mpwr;
899852af6   Paul Stewart   cfg80211: Clear "...
2476
  			chan->beacon_found = false;
5ce543d14   Rajkumar Manoharan   cfg80211: Restore...
2477
2478
2479
  		}
  	}
  }
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
  /*
   * Restoring regulatory settings involves ingoring any
   * possibly stale country IE information and user regulatory
   * settings if so desired, this includes any beacon hints
   * learned as we could have traveled outside to another country
   * after disconnection. To restore regulatory settings we do
   * exactly what we did at bootup:
   *
   *   - send a core regulatory hint
   *   - send a user regulatory hint if applicable
   *
   * Device drivers that send a regulatory hint for a specific country
   * keep their own regulatory domain on wiphy->regd so that does does
   * not need to be remembered.
   */
  static void restore_regulatory_settings(bool reset_user)
  {
  	char alpha2[2];
cee0bec58   Dmitry Shmidt   wireless: Protect...
2498
  	char world_alpha2[2];
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2499
  	struct reg_beacon *reg_beacon, *btmp;
146095557   Luis R. Rodriguez   cfg80211: fix reg...
2500
  	LIST_HEAD(tmp_reg_req_list);
5ce543d14   Rajkumar Manoharan   cfg80211: Restore...
2501
  	struct cfg80211_registered_device *rdev;
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2502

5fe231e87   Johannes Berg   cfg80211: vastly ...
2503
  	ASSERT_RTNL();
050507536   Ilan peer   cfg80211: Add API...
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
  	/*
  	 * Clear the indoor setting in case that it is not controlled by user
  	 * space, as otherwise there is no guarantee that the device is still
  	 * operating in an indoor environment.
  	 */
  	spin_lock(&reg_indoor_lock);
  	if (reg_is_indoor && !reg_is_indoor_portid) {
  		reg_is_indoor = false;
  		reg_check_channels();
  	}
  	spin_unlock(&reg_indoor_lock);
52616f2b4   Ilan Peer   cfg80211: Add an ...
2515

2d3198676   Johannes Berg   regulatory: fix r...
2516
  	reset_regdomains(true, &world_regdom);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2517
  	restore_alpha2(alpha2, reset_user);
146095557   Luis R. Rodriguez   cfg80211: fix reg...
2518
2519
2520
2521
2522
2523
2524
  	/*
  	 * If there's any pending requests we simply
  	 * stash them to a temporary pending queue and
  	 * add then after we've restored regulatory
  	 * settings.
  	 */
  	spin_lock(&reg_requests_lock);
eeca9fce1   Ilan peer   cfg80211: Schedul...
2525
  	list_splice_tail_init(&reg_requests_list, &tmp_reg_req_list);
146095557   Luis R. Rodriguez   cfg80211: fix reg...
2526
  	spin_unlock(&reg_requests_lock);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2527
2528
  	/* Clear beacon hints */
  	spin_lock_bh(&reg_pending_beacons_lock);
fea9bcedc   Johannes Berg   regulatory: don't...
2529
2530
2531
  	list_for_each_entry_safe(reg_beacon, btmp, &reg_pending_beacons, list) {
  		list_del(&reg_beacon->list);
  		kfree(reg_beacon);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2532
2533
  	}
  	spin_unlock_bh(&reg_pending_beacons_lock);
fea9bcedc   Johannes Berg   regulatory: don't...
2534
2535
2536
  	list_for_each_entry_safe(reg_beacon, btmp, &reg_beacon_list, list) {
  		list_del(&reg_beacon->list);
  		kfree(reg_beacon);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2537
2538
2539
  	}
  
  	/* First restore to the basic regulatory settings */
379b82f4c   Johannes Berg   regulatory: pass ...
2540
2541
  	world_alpha2[0] = cfg80211_world_regdom->alpha2[0];
  	world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2542

5ce543d14   Rajkumar Manoharan   cfg80211: Restore...
2543
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
b0d7aa595   Jonathan Doron   cfg80211: allow w...
2544
2545
  		if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
  			continue;
a2f73b6c5   Luis R. Rodriguez   cfg80211: move re...
2546
  		if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
5ce543d14   Rajkumar Manoharan   cfg80211: Restore...
2547
2548
  			restore_custom_reg_settings(&rdev->wiphy);
  	}
cee0bec58   Dmitry Shmidt   wireless: Protect...
2549
  	regulatory_hint_core(world_alpha2);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2550
2551
2552
2553
2554
2555
2556
  
  	/*
  	 * This restores the ieee80211_regdom module parameter
  	 * preference or the last user requested regulatory
  	 * settings, user regulatory settings takes precedence.
  	 */
  	if (is_an_alpha2(alpha2))
549cc1c56   Maciej S. Szmigiero   cfg80211: regulat...
2557
  		regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER);
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2558

146095557   Luis R. Rodriguez   cfg80211: fix reg...
2559
  	spin_lock(&reg_requests_lock);
11cff96c0   Johannes Berg   regulatory: simpl...
2560
  	list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
146095557   Luis R. Rodriguez   cfg80211: fix reg...
2561
  	spin_unlock(&reg_requests_lock);
c799ba6ea   Johannes Berg   cfg80211: remove ...
2562
2563
  	pr_debug("Kicking the queue
  ");
146095557   Luis R. Rodriguez   cfg80211: fix reg...
2564
2565
2566
  
  	schedule_work(&reg_work);
  }
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2567
2568
2569
  
  void regulatory_hint_disconnect(void)
  {
c799ba6ea   Johannes Berg   cfg80211: remove ...
2570
2571
  	pr_debug("All devices are disconnected, going to restore regulatory settings
  ");
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2572
2573
  	restore_regulatory_settings(false);
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2574
2575
  static bool freq_is_chan_12_13_14(u16 freq)
  {
57fbcce37   Johannes Berg   cfg80211: remove ...
2576
2577
2578
  	if (freq == ieee80211_channel_to_frequency(12, NL80211_BAND_2GHZ) ||
  	    freq == ieee80211_channel_to_frequency(13, NL80211_BAND_2GHZ) ||
  	    freq == ieee80211_channel_to_frequency(14, NL80211_BAND_2GHZ))
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2579
2580
2581
  		return true;
  	return false;
  }
3ebfa6e76   Luis R. Rodriguez   cfg80211: do not ...
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
  static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan)
  {
  	struct reg_beacon *pending_beacon;
  
  	list_for_each_entry(pending_beacon, &reg_pending_beacons, list)
  		if (beacon_chan->center_freq ==
  		    pending_beacon->chan.center_freq)
  			return true;
  	return false;
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2592
2593
2594
2595
2596
  int regulatory_hint_found_beacon(struct wiphy *wiphy,
  				 struct ieee80211_channel *beacon_chan,
  				 gfp_t gfp)
  {
  	struct reg_beacon *reg_beacon;
3ebfa6e76   Luis R. Rodriguez   cfg80211: do not ...
2597
  	bool processing;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2598

1a9193185   Johannes Berg   regulatory: code ...
2599
2600
  	if (beacon_chan->beacon_found ||
  	    beacon_chan->flags & IEEE80211_CHAN_RADAR ||
57fbcce37   Johannes Berg   cfg80211: remove ...
2601
  	    (beacon_chan->band == NL80211_BAND_2GHZ &&
1a9193185   Johannes Berg   regulatory: code ...
2602
  	     !freq_is_chan_12_13_14(beacon_chan->center_freq)))
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2603
  		return 0;
3ebfa6e76   Luis R. Rodriguez   cfg80211: do not ...
2604
2605
2606
2607
2608
  	spin_lock_bh(&reg_pending_beacons_lock);
  	processing = pending_reg_beacon(beacon_chan);
  	spin_unlock_bh(&reg_pending_beacons_lock);
  
  	if (processing)
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2609
2610
2611
2612
2613
  		return 0;
  
  	reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp);
  	if (!reg_beacon)
  		return -ENOMEM;
c799ba6ea   Johannes Berg   cfg80211: remove ...
2614
2615
2616
2617
2618
  	pr_debug("Found new beacon on frequency: %d MHz (Ch %d) on %s
  ",
  		 beacon_chan->center_freq,
  		 ieee80211_frequency_to_channel(beacon_chan->center_freq),
  		 wiphy_name(wiphy));
4113f7518   Luis R. Rodriguez   cfg80211: add a r...
2619

e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2620
  	memcpy(&reg_beacon->chan, beacon_chan,
1a9193185   Johannes Berg   regulatory: code ...
2621
  	       sizeof(struct ieee80211_channel));
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
  
  	/*
  	 * Since we can be called from BH or and non-BH context
  	 * we must use spin_lock_bh()
  	 */
  	spin_lock_bh(&reg_pending_beacons_lock);
  	list_add_tail(&reg_beacon->list, &reg_pending_beacons);
  	spin_unlock_bh(&reg_pending_beacons_lock);
  
  	schedule_work(&reg_work);
  
  	return 0;
  }
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
2635
  static void print_rd_rules(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2636
2637
  {
  	unsigned int i;
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
2638
2639
2640
  	const struct ieee80211_reg_rule *reg_rule = NULL;
  	const struct ieee80211_freq_range *freq_range = NULL;
  	const struct ieee80211_power_rule *power_rule = NULL;
089027e57   Janusz Dziedzic   cfg80211: regulat...
2641
  	char bw[32], cac_time[32];
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2642

94c4fd641   Dave Young   wireless: change ...
2643
2644
  	pr_debug("  (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)
  ");
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2645
2646
2647
2648
2649
  
  	for (i = 0; i < rd->n_reg_rules; i++) {
  		reg_rule = &rd->reg_rules[i];
  		freq_range = &reg_rule->freq_range;
  		power_rule = &reg_rule->power_rule;
b0dfd2ea1   Janusz Dziedzic   cfg80211: regulat...
2650
2651
2652
  		if (reg_rule->flags & NL80211_RRF_AUTO_BW)
  			snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
  				 freq_range->max_bandwidth_khz,
975248208   Janusz Dziedzic   cfg80211: regulat...
2653
2654
  				 reg_get_max_bandwidth(rd, reg_rule));
  		else
b0dfd2ea1   Janusz Dziedzic   cfg80211: regulat...
2655
  			snprintf(bw, sizeof(bw), "%d KHz",
975248208   Janusz Dziedzic   cfg80211: regulat...
2656
  				 freq_range->max_bandwidth_khz);
089027e57   Janusz Dziedzic   cfg80211: regulat...
2657
2658
2659
2660
2661
  		if (reg_rule->flags & NL80211_RRF_DFS)
  			scnprintf(cac_time, sizeof(cac_time), "%u s",
  				  reg_rule->dfs_cac_ms/1000);
  		else
  			scnprintf(cac_time, sizeof(cac_time), "N/A");
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
2662
2663
2664
2665
  		/*
  		 * There may not be documentation for max antenna gain
  		 * in certain regions
  		 */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2666
  		if (power_rule->max_antenna_gain)
94c4fd641   Dave Young   wireless: change ...
2667
2668
  			pr_debug("  (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)
  ",
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2669
2670
  				freq_range->start_freq_khz,
  				freq_range->end_freq_khz,
975248208   Janusz Dziedzic   cfg80211: regulat...
2671
  				bw,
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2672
  				power_rule->max_antenna_gain,
089027e57   Janusz Dziedzic   cfg80211: regulat...
2673
2674
  				power_rule->max_eirp,
  				cac_time);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2675
  		else
94c4fd641   Dave Young   wireless: change ...
2676
2677
  			pr_debug("  (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)
  ",
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2678
2679
  				freq_range->start_freq_khz,
  				freq_range->end_freq_khz,
975248208   Janusz Dziedzic   cfg80211: regulat...
2680
  				bw,
089027e57   Janusz Dziedzic   cfg80211: regulat...
2681
2682
  				power_rule->max_eirp,
  				cac_time);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2683
2684
  	}
  }
4c7d3982a   Luis R. Rodriguez   cfg80211: use enu...
2685
  bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
8b60b0780   Luis R. Rodriguez   cfg80211: process...
2686
2687
2688
2689
2690
2691
2692
2693
  {
  	switch (dfs_region) {
  	case NL80211_DFS_UNSET:
  	case NL80211_DFS_FCC:
  	case NL80211_DFS_ETSI:
  	case NL80211_DFS_JP:
  		return true;
  	default:
c799ba6ea   Johannes Berg   cfg80211: remove ...
2694
2695
  		pr_debug("Ignoring uknown DFS master region: %d
  ", dfs_region);
8b60b0780   Luis R. Rodriguez   cfg80211: process...
2696
2697
2698
  		return false;
  	}
  }
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
2699
  static void print_regdomain(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2700
  {
c492db370   Johannes Berg   regulatory: use R...
2701
  	struct regulatory_request *lr = get_last_request();
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2702

3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2703
  	if (is_intersected_alpha2(rd->alpha2)) {
c492db370   Johannes Berg   regulatory: use R...
2704
  		if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
79c97e97a   Johannes Berg   cfg80211: clean u...
2705
  			struct cfg80211_registered_device *rdev;
c492db370   Johannes Berg   regulatory: use R...
2706
  			rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx);
79c97e97a   Johannes Berg   cfg80211: clean u...
2707
  			if (rdev) {
94c4fd641   Dave Young   wireless: change ...
2708
2709
  				pr_debug("Current regulatory domain updated by AP to: %c%c
  ",
79c97e97a   Johannes Berg   cfg80211: clean u...
2710
2711
  					rdev->country_ie_alpha2[0],
  					rdev->country_ie_alpha2[1]);
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2712
  			} else
94c4fd641   Dave Young   wireless: change ...
2713
2714
  				pr_debug("Current regulatory domain intersected:
  ");
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2715
  		} else
94c4fd641   Dave Young   wireless: change ...
2716
2717
  			pr_debug("Current regulatory domain intersected:
  ");
1a9193185   Johannes Berg   regulatory: code ...
2718
  	} else if (is_world_regdom(rd->alpha2)) {
94c4fd641   Dave Young   wireless: change ...
2719
2720
  		pr_debug("World regulatory domain updated:
  ");
1a9193185   Johannes Berg   regulatory: code ...
2721
  	} else {
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2722
  		if (is_unknown_alpha2(rd->alpha2))
94c4fd641   Dave Young   wireless: change ...
2723
2724
  			pr_debug("Regulatory domain changed to driver built-in settings (unknown country)
  ");
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
2725
  		else {
c492db370   Johannes Berg   regulatory: use R...
2726
  			if (reg_request_cell_base(lr))
94c4fd641   Dave Young   wireless: change ...
2727
2728
  				pr_debug("Regulatory domain changed to country: %c%c by Cell Station
  ",
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
2729
2730
  					rd->alpha2[0], rd->alpha2[1]);
  			else
94c4fd641   Dave Young   wireless: change ...
2731
2732
  				pr_debug("Regulatory domain changed to country: %c%c
  ",
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
2733
2734
  					rd->alpha2[0], rd->alpha2[1]);
  		}
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2735
  	}
1a9193185   Johannes Berg   regulatory: code ...
2736

94c4fd641   Dave Young   wireless: change ...
2737
  	pr_debug(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2738
2739
  	print_rd_rules(rd);
  }
2df78167a   Johannes Berg   wireless: fix a f...
2740
  static void print_regdomain_info(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2741
  {
94c4fd641   Dave Young   wireless: change ...
2742
2743
  	pr_debug("Regulatory domain: %c%c
  ", rd->alpha2[0], rd->alpha2[1]);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2744
2745
  	print_rd_rules(rd);
  }
3b9e5aca4   Luis R. Rodriguez   cfg80211: set cor...
2746
2747
2748
2749
2750
2751
2752
  static int reg_set_rd_core(const struct ieee80211_regdomain *rd)
  {
  	if (!is_world_regdom(rd->alpha2))
  		return -EINVAL;
  	update_world_regdomain(rd);
  	return 0;
  }
84721d449   Luis R. Rodriguez   cfg80211: set use...
2753
2754
2755
2756
  static int reg_set_rd_user(const struct ieee80211_regdomain *rd,
  			   struct regulatory_request *user_request)
  {
  	const struct ieee80211_regdomain *intersected_rd = NULL;
84721d449   Luis R. Rodriguez   cfg80211: set use...
2757
2758
2759
2760
  	if (!regdom_changes(rd->alpha2))
  		return -EALREADY;
  
  	if (!is_valid_rd(rd)) {
94c4fd641   Dave Young   wireless: change ...
2761
2762
2763
  		pr_err("Invalid regulatory domain detected: %c%c
  ",
  		       rd->alpha2[0], rd->alpha2[1]);
84721d449   Luis R. Rodriguez   cfg80211: set use...
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
  		print_regdomain_info(rd);
  		return -EINVAL;
  	}
  
  	if (!user_request->intersect) {
  		reset_regdomains(false, rd);
  		return 0;
  	}
  
  	intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
  	if (!intersected_rd)
  		return -EINVAL;
  
  	kfree(rd);
  	rd = NULL;
  	reset_regdomains(false, intersected_rd);
  
  	return 0;
  }
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2783
2784
  static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
  			     struct regulatory_request *driver_request)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2785
  {
e9763c3c2   Johannes Berg   regulatory: clean...
2786
  	const struct ieee80211_regdomain *regd;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
2787
  	const struct ieee80211_regdomain *intersected_rd = NULL;
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2788
  	const struct ieee80211_regdomain *tmp;
806a9e396   Luis R. Rodriguez   cfg80211: make re...
2789
  	struct wiphy *request_wiphy;
6913b49a5   Johannes Berg   regulatory: fix r...
2790

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2791
  	if (is_world_regdom(rd->alpha2))
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2792
  		return -EINVAL;
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2793
2794
  	if (!regdom_changes(rd->alpha2))
  		return -EALREADY;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2795

8375af3ba   Luis R. Rodriguez   cfg80211: remove ...
2796
  	if (!is_valid_rd(rd)) {
94c4fd641   Dave Young   wireless: change ...
2797
2798
2799
  		pr_err("Invalid regulatory domain detected: %c%c
  ",
  		       rd->alpha2[0], rd->alpha2[1]);
8375af3ba   Luis R. Rodriguez   cfg80211: remove ...
2800
2801
  		print_regdomain_info(rd);
  		return -EINVAL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2802
  	}
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2803
  	request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
922ec58c7   Johannes Berg   cfg80211: reg: re...
2804
  	if (!request_wiphy)
de3584bd6   Johannes Berg   cfg80211: fix reg...
2805
  		return -ENODEV;
806a9e396   Luis R. Rodriguez   cfg80211: make re...
2806

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2807
  	if (!driver_request->intersect) {
558f6d322   Luis R. Rodriguez   cfg80211: fix for...
2808
2809
  		if (request_wiphy->regd)
  			return -EALREADY;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
2810

e9763c3c2   Johannes Berg   regulatory: clean...
2811
2812
2813
  		regd = reg_copy_regd(rd);
  		if (IS_ERR(regd))
  			return PTR_ERR(regd);
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
2814

458f4f9e9   Johannes Berg   regulatory: use R...
2815
  		rcu_assign_pointer(request_wiphy->regd, regd);
379b82f4c   Johannes Berg   regulatory: pass ...
2816
  		reset_regdomains(false, rd);
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
2817
2818
  		return 0;
  	}
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2819
2820
2821
  	intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
  	if (!intersected_rd)
  		return -EINVAL;
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
2822

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2823
2824
2825
2826
2827
2828
2829
2830
  	/*
  	 * We can trash what CRDA provided now.
  	 * However if a driver requested this specific regulatory
  	 * domain we keep it for its private use
  	 */
  	tmp = get_wiphy_regdom(request_wiphy);
  	rcu_assign_pointer(request_wiphy->regd, rd);
  	rcu_free_regdom(tmp);
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
2831

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2832
  	rd = NULL;
b7566fc36   Larry Finger   cfg80211: Fix mem...
2833

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2834
  	reset_regdomains(false, intersected_rd);
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
2835

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2836
2837
  	return 0;
  }
01992406d   Luis R. Rodriguez   cfg80211: rename ...
2838
2839
  static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
  				 struct regulatory_request *country_ie_request)
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2840
2841
  {
  	struct wiphy *request_wiphy;
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
2842

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2843
2844
2845
  	if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
  	    !is_unknown_alpha2(rd->alpha2))
  		return -EINVAL;
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
2846

f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2847
2848
2849
2850
2851
2852
2853
  	/*
  	 * Lets only bother proceeding on the same alpha2 if the current
  	 * rd is non static (it means CRDA was present and was used last)
  	 * and the pending request came in from a country IE
  	 */
  
  	if (!is_valid_rd(rd)) {
94c4fd641   Dave Young   wireless: change ...
2854
2855
2856
  		pr_err("Invalid regulatory domain detected: %c%c
  ",
  		       rd->alpha2[0], rd->alpha2[1]);
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2857
2858
  		print_regdomain_info(rd);
  		return -EINVAL;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
2859
  	}
01992406d   Luis R. Rodriguez   cfg80211: rename ...
2860
  	request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx);
922ec58c7   Johannes Berg   cfg80211: reg: re...
2861
  	if (!request_wiphy)
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2862
  		return -ENODEV;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2863

01992406d   Luis R. Rodriguez   cfg80211: rename ...
2864
  	if (country_ie_request->intersect)
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2865
2866
2867
2868
2869
  		return -EINVAL;
  
  	reset_regdomains(false, rd);
  	return 0;
  }
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2870

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
2871
2872
  /*
   * Use this call to set the current regulatory domain. Conflicts with
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2873
   * multiple drivers can be ironed out later. Caller must've already
458f4f9e9   Johannes Berg   regulatory: use R...
2874
   * kmalloc'd the rd structure.
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
2875
   */
c37722bd1   Ilan peer   cfg80211: Stop ca...
2876
2877
  int set_regdom(const struct ieee80211_regdomain *rd,
  	       enum ieee80211_regd_source regd_src)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2878
  {
c492db370   Johannes Berg   regulatory: use R...
2879
  	struct regulatory_request *lr;
092008abe   Janusz Dziedzic   cfg80211: regulat...
2880
  	bool user_reset = false;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2881
  	int r;
3b9e5aca4   Luis R. Rodriguez   cfg80211: set cor...
2882
2883
2884
2885
  	if (!reg_is_valid_request(rd->alpha2)) {
  		kfree(rd);
  		return -EINVAL;
  	}
c37722bd1   Ilan peer   cfg80211: Stop ca...
2886
  	if (regd_src == REGD_SOURCE_CRDA)
b68630369   Johannes Berg   cfg80211: reg: ma...
2887
  		reset_crda_timeouts();
c37722bd1   Ilan peer   cfg80211: Stop ca...
2888

c492db370   Johannes Berg   regulatory: use R...
2889
  	lr = get_last_request();
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
2890

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2891
  	/* Note that this doesn't update the wiphys, this is done below */
3b9e5aca4   Luis R. Rodriguez   cfg80211: set cor...
2892
2893
2894
2895
2896
  	switch (lr->initiator) {
  	case NL80211_REGDOM_SET_BY_CORE:
  		r = reg_set_rd_core(rd);
  		break;
  	case NL80211_REGDOM_SET_BY_USER:
84721d449   Luis R. Rodriguez   cfg80211: set use...
2897
  		r = reg_set_rd_user(rd, lr);
092008abe   Janusz Dziedzic   cfg80211: regulat...
2898
  		user_reset = true;
84721d449   Luis R. Rodriguez   cfg80211: set use...
2899
  		break;
3b9e5aca4   Luis R. Rodriguez   cfg80211: set cor...
2900
  	case NL80211_REGDOM_SET_BY_DRIVER:
f5fe32478   Luis R. Rodriguez   cfg80211: set dri...
2901
2902
  		r = reg_set_rd_driver(rd, lr);
  		break;
3b9e5aca4   Luis R. Rodriguez   cfg80211: set cor...
2903
  	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
01992406d   Luis R. Rodriguez   cfg80211: rename ...
2904
  		r = reg_set_rd_country_ie(rd, lr);
3b9e5aca4   Luis R. Rodriguez   cfg80211: set cor...
2905
2906
2907
2908
  		break;
  	default:
  		WARN(1, "invalid initiator %d
  ", lr->initiator);
09d118008   Ola Olsson   nl80211: fix a fe...
2909
  		kfree(rd);
3b9e5aca4   Luis R. Rodriguez   cfg80211: set cor...
2910
2911
  		return -EINVAL;
  	}
d2372b315   Johannes Berg   wireless: make re...
2912
  	if (r) {
092008abe   Janusz Dziedzic   cfg80211: regulat...
2913
2914
  		switch (r) {
  		case -EALREADY:
959085352   Kalle Valo   cfg80211: fix set...
2915
  			reg_set_request_processed();
092008abe   Janusz Dziedzic   cfg80211: regulat...
2916
2917
2918
2919
2920
  			break;
  		default:
  			/* Back to world regulatory in case of errors */
  			restore_regulatory_settings(user_reset);
  		}
959085352   Kalle Valo   cfg80211: fix set...
2921

d2372b315   Johannes Berg   wireless: make re...
2922
  		kfree(rd);
38fd2143f   Johannes Berg   regulatory: remov...
2923
  		return r;
d2372b315   Johannes Berg   wireless: make re...
2924
  	}
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2925

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2926
  	/* This would make this whole thing pointless */
38fd2143f   Johannes Berg   regulatory: remov...
2927
2928
  	if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom()))
  		return -EINVAL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2929
2930
  
  	/* update all wiphys now with the new established regulatory domain */
c492db370   Johannes Berg   regulatory: use R...
2931
  	update_all_wiphy_regulatory(lr->initiator);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2932

458f4f9e9   Johannes Berg   regulatory: use R...
2933
  	print_regdomain(get_cfg80211_regdom());
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2934

c492db370   Johannes Berg   regulatory: use R...
2935
  	nl80211_send_reg_change_event(lr);
73d54c9e7   Luis R. Rodriguez   cfg80211: add reg...
2936

b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
2937
  	reg_set_request_processed();
38fd2143f   Johannes Berg   regulatory: remov...
2938
  	return 0;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2939
  }
2c3e861c9   Arik Nemtsov   cfg80211: introdu...
2940
2941
  static int __regulatory_set_wiphy_regd(struct wiphy *wiphy,
  				       struct ieee80211_regdomain *rd)
b0d7aa595   Jonathan Doron   cfg80211: allow w...
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
  {
  	const struct ieee80211_regdomain *regd;
  	const struct ieee80211_regdomain *prev_regd;
  	struct cfg80211_registered_device *rdev;
  
  	if (WARN_ON(!wiphy || !rd))
  		return -EINVAL;
  
  	if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED),
  		 "wiphy should have REGULATORY_WIPHY_SELF_MANAGED
  "))
  		return -EPERM;
  
  	if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected
  ")) {
  		print_regdomain_info(rd);
  		return -EINVAL;
  	}
  
  	regd = reg_copy_regd(rd);
  	if (IS_ERR(regd))
  		return PTR_ERR(regd);
  
  	rdev = wiphy_to_rdev(wiphy);
  
  	spin_lock(&reg_requests_lock);
  	prev_regd = rdev->requested_regd;
  	rdev->requested_regd = regd;
  	spin_unlock(&reg_requests_lock);
  
  	kfree(prev_regd);
2c3e861c9   Arik Nemtsov   cfg80211: introdu...
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
  	return 0;
  }
  
  int regulatory_set_wiphy_regd(struct wiphy *wiphy,
  			      struct ieee80211_regdomain *rd)
  {
  	int ret = __regulatory_set_wiphy_regd(wiphy, rd);
  
  	if (ret)
  		return ret;
b0d7aa595   Jonathan Doron   cfg80211: allow w...
2983
2984
2985
2986
2987
  
  	schedule_work(&reg_work);
  	return 0;
  }
  EXPORT_SYMBOL(regulatory_set_wiphy_regd);
2c3e861c9   Arik Nemtsov   cfg80211: introdu...
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
  int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
  					struct ieee80211_regdomain *rd)
  {
  	int ret;
  
  	ASSERT_RTNL();
  
  	ret = __regulatory_set_wiphy_regd(wiphy, rd);
  	if (ret)
  		return ret;
  
  	/* process the request immediately */
  	reg_process_self_managed_hints();
  	return 0;
  }
  EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl);
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
3004
3005
  void wiphy_regulatory_register(struct wiphy *wiphy)
  {
23df0b731   Arik Nemtsov   regulatory: use c...
3006
  	struct regulatory_request *lr;
b0d7aa595   Jonathan Doron   cfg80211: allow w...
3007
3008
3009
3010
  	/* self-managed devices ignore external hints */
  	if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
  		wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
  					   REGULATORY_COUNTRY_IE_IGNORE;
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
3011
3012
  	if (!reg_dev_ignore_cell_hint(wiphy))
  		reg_num_devs_support_basehint++;
23df0b731   Arik Nemtsov   regulatory: use c...
3013
3014
  	lr = get_last_request();
  	wiphy_update_regulatory(wiphy, lr->initiator);
897667273   Vasanthakumar Thiagarajan   cfg80211: Share C...
3015
  	wiphy_all_share_dfs_chan_state(wiphy);
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
3016
  }
bfead0808   Luis R. Rodriguez   cfg80211: rename ...
3017
  void wiphy_regulatory_deregister(struct wiphy *wiphy)
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
3018
  {
0ad8acaf4   Luis R. Rodriguez   cfg80211: fix NUL...
3019
  	struct wiphy *request_wiphy = NULL;
c492db370   Johannes Berg   regulatory: use R...
3020
  	struct regulatory_request *lr;
761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
3021

c492db370   Johannes Berg   regulatory: use R...
3022
  	lr = get_last_request();
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
3023

57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
3024
3025
  	if (!reg_dev_ignore_cell_hint(wiphy))
  		reg_num_devs_support_basehint--;
458f4f9e9   Johannes Berg   regulatory: use R...
3026
  	rcu_free_regdom(get_wiphy_regdom(wiphy));
34dd886c1   Monam Agarwal   cfg80211: regulat...
3027
  	RCU_INIT_POINTER(wiphy->regd, NULL);
0ef9ccdd9   Chris Wright   cfg80211: remove ...
3028

c492db370   Johannes Berg   regulatory: use R...
3029
3030
  	if (lr)
  		request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
806a9e396   Luis R. Rodriguez   cfg80211: make re...
3031

0ef9ccdd9   Chris Wright   cfg80211: remove ...
3032
  	if (!request_wiphy || request_wiphy != wiphy)
38fd2143f   Johannes Berg   regulatory: remov...
3033
  		return;
0ef9ccdd9   Chris Wright   cfg80211: remove ...
3034

c492db370   Johannes Berg   regulatory: use R...
3035
3036
  	lr->wiphy_idx = WIPHY_IDX_INVALID;
  	lr->country_ie_env = ENVIRON_ANY;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
3037
  }
174e0cd28   Ilan Peer   cfg80211: Enable ...
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
  /*
   * See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for
   * UNII band definitions
   */
  int cfg80211_get_unii(int freq)
  {
  	/* UNII-1 */
  	if (freq >= 5150 && freq <= 5250)
  		return 0;
  
  	/* UNII-2A */
  	if (freq > 5250 && freq <= 5350)
  		return 1;
  
  	/* UNII-2B */
  	if (freq > 5350 && freq <= 5470)
  		return 2;
  
  	/* UNII-2C */
  	if (freq > 5470 && freq <= 5725)
  		return 3;
  
  	/* UNII-3 */
  	if (freq > 5725 && freq <= 5825)
  		return 4;
  
  	return -EINVAL;
  }
c8866e55a   Ilan Peer   cfg80211: Enable ...
3066
3067
3068
3069
  bool regulatory_indoor_allowed(void)
  {
  	return reg_is_indoor;
  }
b35a51c7d   Vasanthakumar Thiagarajan   cfg80211: Make pr...
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
  bool regulatory_pre_cac_allowed(struct wiphy *wiphy)
  {
  	const struct ieee80211_regdomain *regd = NULL;
  	const struct ieee80211_regdomain *wiphy_regd = NULL;
  	bool pre_cac_allowed = false;
  
  	rcu_read_lock();
  
  	regd = rcu_dereference(cfg80211_regdomain);
  	wiphy_regd = rcu_dereference(wiphy->regd);
  	if (!wiphy_regd) {
  		if (regd->dfs_region == NL80211_DFS_ETSI)
  			pre_cac_allowed = true;
  
  		rcu_read_unlock();
  
  		return pre_cac_allowed;
  	}
  
  	if (regd->dfs_region == wiphy_regd->dfs_region &&
  	    wiphy_regd->dfs_region == NL80211_DFS_ETSI)
  		pre_cac_allowed = true;
  
  	rcu_read_unlock();
  
  	return pre_cac_allowed;
  }
897667273   Vasanthakumar Thiagarajan   cfg80211: Share C...
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
  void regulatory_propagate_dfs_state(struct wiphy *wiphy,
  				    struct cfg80211_chan_def *chandef,
  				    enum nl80211_dfs_state dfs_state,
  				    enum nl80211_radar_event event)
  {
  	struct cfg80211_registered_device *rdev;
  
  	ASSERT_RTNL();
  
  	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
  		return;
897667273   Vasanthakumar Thiagarajan   cfg80211: Share C...
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
  		if (wiphy == &rdev->wiphy)
  			continue;
  
  		if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
  			continue;
  
  		if (!ieee80211_get_channel(&rdev->wiphy,
  					   chandef->chan->center_freq))
  			continue;
  
  		cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state);
  
  		if (event == NL80211_RADAR_DETECTED ||
  		    event == NL80211_RADAR_CAC_FINISHED)
  			cfg80211_sched_dfs_chan_update(rdev);
  
  		nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL);
  	}
  }
2fcc9f731   Uwe Kleine-König   wireless: move re...
3128
  int __init regulatory_init(void)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
3129
  {
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
3130
  	int err = 0;
734366dea   Johannes Berg   cfg80211: clean u...
3131

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
3132
3133
3134
  	reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
  	if (IS_ERR(reg_pdev))
  		return PTR_ERR(reg_pdev);
734366dea   Johannes Berg   cfg80211: clean u...
3135

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
3136
  	spin_lock_init(&reg_requests_lock);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
3137
  	spin_lock_init(&reg_pending_beacons_lock);
050507536   Ilan peer   cfg80211: Add API...
3138
  	spin_lock_init(&reg_indoor_lock);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
3139

80007efef   Luis R. Rodriguez   cfg80211: warn if...
3140
  	reg_regdb_size_check();
458f4f9e9   Johannes Berg   regulatory: use R...
3141
  	rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom);
734366dea   Johannes Berg   cfg80211: clean u...
3142

09d989d17   Luis R. Rodriguez   cfg80211: add reg...
3143
3144
  	user_alpha2[0] = '9';
  	user_alpha2[1] = '7';
ae9e4b0d1   Luis R. Rodriguez   cfg80211: treat i...
3145
  	/* We always try to get an update for the static regdomain */
458f4f9e9   Johannes Berg   regulatory: use R...
3146
  	err = regulatory_hint_core(cfg80211_world_regdom->alpha2);
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
3147
  	if (err) {
09d118008   Ola Olsson   nl80211: fix a fe...
3148
3149
  		if (err == -ENOMEM) {
  			platform_device_unregister(reg_pdev);
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
3150
  			return err;
09d118008   Ola Olsson   nl80211: fix a fe...
3151
  		}
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
3152
3153
3154
3155
3156
3157
3158
  		/*
  		 * N.B. kobject_uevent_env() can fail mainly for when we're out
  		 * memory which is handled and propagated appropriately above
  		 * but it can also fail during a netlink_broadcast() or during
  		 * early boot for call_usermodehelper(). For now treat these
  		 * errors as non-fatal.
  		 */
e9c0268f0   Joe Perches   net/wireless: Use...
3159
3160
  		pr_err("kobject_uevent_env() was unable to call CRDA during init
  ");
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
3161
  	}
734366dea   Johannes Berg   cfg80211: clean u...
3162

ae9e4b0d1   Luis R. Rodriguez   cfg80211: treat i...
3163
3164
3165
3166
3167
  	/*
  	 * Finally, if the user set the module parameter treat it
  	 * as a user hint.
  	 */
  	if (!is_world_regdom(ieee80211_regdom))
57b5ce072   Luis R. Rodriguez   cfg80211: add cel...
3168
3169
  		regulatory_hint_user(ieee80211_regdom,
  				     NL80211_USER_REG_HINT_USER);
ae9e4b0d1   Luis R. Rodriguez   cfg80211: treat i...
3170

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
3171
3172
  	return 0;
  }
1a9193185   Johannes Berg   regulatory: code ...
3173
  void regulatory_exit(void)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
3174
  {
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
3175
  	struct regulatory_request *reg_request, *tmp;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
3176
  	struct reg_beacon *reg_beacon, *btmp;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
3177
3178
  
  	cancel_work_sync(&reg_work);
b68630369   Johannes Berg   cfg80211: reg: ma...
3179
  	cancel_crda_timeout_sync();
ad932f046   Arik Nemtsov   cfg80211: leave i...
3180
  	cancel_delayed_work_sync(&reg_check_chans);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
3181

9027b1493   Johannes Berg   regulatory: remov...
3182
  	/* Lock to suppress warnings */
38fd2143f   Johannes Berg   regulatory: remov...
3183
  	rtnl_lock();
379b82f4c   Johannes Berg   regulatory: pass ...
3184
  	reset_regdomains(true, NULL);
38fd2143f   Johannes Berg   regulatory: remov...
3185
  	rtnl_unlock();
734366dea   Johannes Berg   cfg80211: clean u...
3186

58ebacc66   Luis R. Rodriguez   cfg80211: fix bug...
3187
  	dev_set_uevent_suppress(&reg_pdev->dev, true);
f6037d09e   Johannes Berg   wireless: get rid...
3188

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
3189
  	platform_device_unregister(reg_pdev);
734366dea   Johannes Berg   cfg80211: clean u...
3190

fea9bcedc   Johannes Berg   regulatory: don't...
3191
3192
3193
  	list_for_each_entry_safe(reg_beacon, btmp, &reg_pending_beacons, list) {
  		list_del(&reg_beacon->list);
  		kfree(reg_beacon);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
3194
  	}
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
3195

fea9bcedc   Johannes Berg   regulatory: don't...
3196
3197
3198
  	list_for_each_entry_safe(reg_beacon, btmp, &reg_beacon_list, list) {
  		list_del(&reg_beacon->list);
  		kfree(reg_beacon);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
3199
  	}
fea9bcedc   Johannes Berg   regulatory: don't...
3200
3201
3202
  	list_for_each_entry_safe(reg_request, tmp, &reg_requests_list, list) {
  		list_del(&reg_request->list);
  		kfree(reg_request);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
3203
  	}
8318d78a4   Johannes Berg   cfg80211 API for ...
3204
  }