Blame view

net/wireless/reg.c 56.5 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>
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
5
   * Copyright 2008	Luis R. Rodriguez <lrodriguz@atheros.com>
8318d78a4   Johannes Berg   cfg80211 API for ...
6
7
8
9
10
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
11
12
  /**
   * DOC: Wireless regulatory infrastructure
8318d78a4   Johannes Berg   cfg80211 API for ...
13
14
15
16
17
18
   *
   * 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...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   * 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 ...
33
   */
e9c0268f0   Joe Perches   net/wireless: Use...
34
35
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8318d78a4   Johannes Berg   cfg80211 API for ...
36
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
37
  #include <linux/slab.h>
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
38
39
  #include <linux/list.h>
  #include <linux/random.h>
c61029c77   John W. Linville   wireless: upcase ...
40
  #include <linux/ctype.h>
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
41
42
  #include <linux/nl80211.h>
  #include <linux/platform_device.h>
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
43
  #include <net/cfg80211.h>
8318d78a4   Johannes Berg   cfg80211 API for ...
44
  #include "core.h"
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
45
  #include "reg.h"
3b377ea9d   John W. Linville   wireless: support...
46
  #include "regdb.h"
73d54c9e7   Luis R. Rodriguez   cfg80211: add reg...
47
  #include "nl80211.h"
8318d78a4   Johannes Berg   cfg80211 API for ...
48

4113f7518   Luis R. Rodriguez   cfg80211: add a r...
49
  #ifdef CONFIG_CFG80211_REG_DEBUG
8271195e3   John W. Linville   wireless: fix bui...
50
  #define REG_DBG_PRINT(format, args...) \
4113f7518   Luis R. Rodriguez   cfg80211: add a r...
51
  	do { \
e9c0268f0   Joe Perches   net/wireless: Use...
52
  		printk(KERN_DEBUG pr_fmt(format), ##args);	\
4113f7518   Luis R. Rodriguez   cfg80211: add a r...
53
54
  	} while (0)
  #else
8271195e3   John W. Linville   wireless: fix bui...
55
  #define REG_DBG_PRINT(args...)
4113f7518   Luis R. Rodriguez   cfg80211: add a r...
56
  #endif
5166ccd22   Luis R. Rodriguez   cfg80211: Add kdo...
57
  /* Receipt of information from last regulatory request */
f6037d09e   Johannes Berg   wireless: get rid...
58
  static struct regulatory_request *last_request;
734366dea   Johannes Berg   cfg80211: clean u...
59

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

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
63
64
  /*
   * Central wireless core regulatory domains, we only need two,
734366dea   Johannes Berg   cfg80211: clean u...
65
   * the current one and a world regulatory domain in case we have no
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
66
67
   * information to give us an alpha2
   */
f130347c2   Luis R. Rodriguez   cfg80211: add get...
68
  const struct ieee80211_regdomain *cfg80211_regdomain;
734366dea   Johannes Berg   cfg80211: clean u...
69

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
70
  /*
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
71
72
73
   * Protects static reg.c components:
   *     - cfg80211_world_regdom
   *     - cfg80211_regdom
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
74
75
   *     - last_request
   */
670b7f11f   John W. Linville   wireless: mark re...
76
  static DEFINE_MUTEX(reg_mutex);
46a5ebaf0   Johannes Berg   cfg80211/mac80211...
77
78
79
80
81
  
  static inline void assert_reg_lock(void)
  {
  	lockdep_assert_held(&reg_mutex);
  }
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
82

e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
83
  /* Used to queue up regulatory hints */
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
84
85
  static LIST_HEAD(reg_requests_list);
  static spinlock_t reg_requests_lock;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
86
87
88
89
90
91
92
93
94
95
96
  /* 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;
  };
f333a7a2f   Luis R. Rodriguez   cfg80211: move re...
97
98
  static void reg_todo(struct work_struct *work);
  static DECLARE_WORK(reg_work, reg_todo);
734366dea   Johannes Berg   cfg80211: clean u...
99
100
  /* We keep a static world regulatory domain in case of the absence of CRDA */
  static const struct ieee80211_regdomain world_regdom = {
611b6a82a   Luis R. Rodriguez   cfg80211: Enable ...
101
  	.n_reg_rules = 5,
734366dea   Johannes Berg   cfg80211: clean u...
102
103
  	.alpha2 =  "00",
  	.reg_rules = {
68798a626   Luis R. Rodriguez   cfg80211: enable ...
104
105
  		/* IEEE 802.11b/g, channels 1..11 */
  		REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
611b6a82a   Luis R. Rodriguez   cfg80211: Enable ...
106
107
108
  		/* IEEE 802.11b/g, channels 12..13. No HT40
  		 * channel fits here. */
  		REG_RULE(2467-10, 2472+10, 20, 6, 20,
3fc71f775   Luis R. Rodriguez   cfg80211: enable ...
109
110
  			NL80211_RRF_PASSIVE_SCAN |
  			NL80211_RRF_NO_IBSS),
611b6a82a   Luis R. Rodriguez   cfg80211: Enable ...
111
112
113
114
115
116
117
  		/* IEEE 802.11 channel 14 - Only JP enables
  		 * this and for 802.11b only */
  		REG_RULE(2484-10, 2484+10, 20, 6, 20,
  			NL80211_RRF_PASSIVE_SCAN |
  			NL80211_RRF_NO_IBSS |
  			NL80211_RRF_NO_OFDM),
  		/* IEEE 802.11a, channel 36..48 */
ec329acef   Luis R. Rodriguez   cfg80211: fix max...
118
  		REG_RULE(5180-10, 5240+10, 40, 6, 20,
611b6a82a   Luis R. Rodriguez   cfg80211: Enable ...
119
120
                          NL80211_RRF_PASSIVE_SCAN |
                          NL80211_RRF_NO_IBSS),
3fc71f775   Luis R. Rodriguez   cfg80211: enable ...
121
122
123
124
  
  		/* NB: 5260 MHz - 5700 MHz requies DFS */
  
  		/* IEEE 802.11a, channel 149..165 */
ec329acef   Luis R. Rodriguez   cfg80211: fix max...
125
  		REG_RULE(5745-10, 5825+10, 40, 6, 20,
3fc71f775   Luis R. Rodriguez   cfg80211: enable ...
126
127
  			NL80211_RRF_PASSIVE_SCAN |
  			NL80211_RRF_NO_IBSS),
734366dea   Johannes Berg   cfg80211: clean u...
128
129
  	}
  };
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
130
131
  static const struct ieee80211_regdomain *cfg80211_world_regdom =
  	&world_regdom;
734366dea   Johannes Berg   cfg80211: clean u...
132

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

734366dea   Johannes Berg   cfg80211: clean u...
136
137
  module_param(ieee80211_regdom, charp, 0444);
  MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
734366dea   Johannes Berg   cfg80211: clean u...
138
139
  static void reset_regdomains(void)
  {
942b25cf9   Johannes Berg   cfg80211: clean u...
140
141
142
143
144
145
146
  	/* avoid freeing static information or freeing something twice */
  	if (cfg80211_regdomain == cfg80211_world_regdom)
  		cfg80211_regdomain = NULL;
  	if (cfg80211_world_regdom == &world_regdom)
  		cfg80211_world_regdom = NULL;
  	if (cfg80211_regdomain == &world_regdom)
  		cfg80211_regdomain = NULL;
942b25cf9   Johannes Berg   cfg80211: clean u...
147
148
149
  
  	kfree(cfg80211_regdomain);
  	kfree(cfg80211_world_regdom);
734366dea   Johannes Berg   cfg80211: clean u...
150

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
151
  	cfg80211_world_regdom = &world_regdom;
734366dea   Johannes Berg   cfg80211: clean u...
152
153
  	cfg80211_regdomain = NULL;
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
154
155
156
157
  /*
   * Dynamic world regulatory domain requested by the wireless
   * core upon initialization
   */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
158
  static void update_world_regdomain(const struct ieee80211_regdomain *rd)
734366dea   Johannes Berg   cfg80211: clean u...
159
  {
f6037d09e   Johannes Berg   wireless: get rid...
160
  	BUG_ON(!last_request);
734366dea   Johannes Berg   cfg80211: clean u...
161
162
163
164
165
166
  
  	reset_regdomains();
  
  	cfg80211_world_regdom = rd;
  	cfg80211_regdomain = rd;
  }
734366dea   Johannes Berg   cfg80211: clean u...
167

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
168
  bool is_world_regdom(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
169
170
171
172
173
174
175
  {
  	if (!alpha2)
  		return false;
  	if (alpha2[0] == '0' && alpha2[1] == '0')
  		return true;
  	return false;
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
176

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
177
  static bool is_alpha2_set(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
178
179
180
181
182
183
184
  {
  	if (!alpha2)
  		return false;
  	if (alpha2[0] != 0 && alpha2[1] != 0)
  		return true;
  	return false;
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
185

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
186
  static bool is_unknown_alpha2(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
187
188
189
  {
  	if (!alpha2)
  		return false;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
190
191
192
193
  	/*
  	 * Special case where regulatory domain was built by driver
  	 * but a specific alpha2 cannot be determined
  	 */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
194
195
196
197
  	if (alpha2[0] == '9' && alpha2[1] == '9')
  		return true;
  	return false;
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
198

3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
199
200
201
202
  static bool is_intersected_alpha2(const char *alpha2)
  {
  	if (!alpha2)
  		return false;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
203
204
  	/*
  	 * Special case where regulatory domain is the
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
205
  	 * result of an intersection between two regulatory domain
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
206
207
  	 * structures
  	 */
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
208
209
210
211
  	if (alpha2[0] == '9' && alpha2[1] == '8')
  		return true;
  	return false;
  }
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
212
  static bool is_an_alpha2(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
213
214
215
  {
  	if (!alpha2)
  		return false;
c61029c77   John W. Linville   wireless: upcase ...
216
  	if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
217
218
219
  		return true;
  	return false;
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
220

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
221
  static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
222
223
224
225
226
227
228
229
  {
  	if (!alpha2_x || !alpha2_y)
  		return false;
  	if (alpha2_x[0] == alpha2_y[0] &&
  		alpha2_x[1] == alpha2_y[1])
  		return true;
  	return false;
  }
69b1572bd   Luis R. Rodriguez   cfg80211: rename ...
230
  static bool regdom_changes(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
231
  {
761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
232
  	assert_cfg80211_lock();
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
233
234
235
236
237
238
  	if (!cfg80211_regdomain)
  		return true;
  	if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
  		return false;
  	return true;
  }
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  /*
   * 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 */
  	if (WARN((!is_world_regdom(user_alpha2) &&
  		  !is_an_alpha2(user_alpha2)),
  		 "Unexpected user alpha2: %c%c
  ",
  		 user_alpha2[0],
  	         user_alpha2[1]))
  		return false;
  
  	return true;
  }
3b377ea9d   John W. Linville   wireless: support...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
  static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
  			 const struct ieee80211_regdomain *src_regd)
  {
  	struct ieee80211_regdomain *regd;
  	int size_of_regd = 0;
  	unsigned int i;
  
  	size_of_regd = sizeof(struct ieee80211_regdomain) +
  	  ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
  
  	regd = kzalloc(size_of_regd, GFP_KERNEL);
  	if (!regd)
  		return -ENOMEM;
  
  	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],
  			sizeof(struct ieee80211_reg_rule));
  
  	*dst_regd = regd;
  	return 0;
  }
  
  #ifdef CONFIG_CFG80211_INTERNAL_REGDB
  struct reg_regdb_search_request {
  	char alpha2[2];
  	struct list_head list;
  };
  
  static LIST_HEAD(reg_regdb_search_list);
368d06f5b   John W. Linville   wireless: convert...
291
  static DEFINE_MUTEX(reg_regdb_search_mutex);
3b377ea9d   John W. Linville   wireless: support...
292
293
294
295
296
297
  
  static void reg_regdb_search(struct work_struct *work)
  {
  	struct reg_regdb_search_request *request;
  	const struct ieee80211_regdomain *curdom, *regdom;
  	int i, r;
368d06f5b   John W. Linville   wireless: convert...
298
  	mutex_lock(&reg_regdb_search_mutex);
3b377ea9d   John W. Linville   wireless: support...
299
300
301
302
303
304
305
306
307
308
309
310
311
  	while (!list_empty(&reg_regdb_search_list)) {
  		request = list_first_entry(&reg_regdb_search_list,
  					   struct reg_regdb_search_request,
  					   list);
  		list_del(&request->list);
  
  		for (i=0; i<reg_regdb_size; i++) {
  			curdom = reg_regdb[i];
  
  			if (!memcmp(request->alpha2, curdom->alpha2, 2)) {
  				r = reg_copy_regd(&regdom, curdom);
  				if (r)
  					break;
3b377ea9d   John W. Linville   wireless: support...
312
313
314
  				mutex_lock(&cfg80211_mutex);
  				set_regdom(regdom);
  				mutex_unlock(&cfg80211_mutex);
3b377ea9d   John W. Linville   wireless: support...
315
316
317
318
319
320
  				break;
  			}
  		}
  
  		kfree(request);
  	}
368d06f5b   John W. Linville   wireless: convert...
321
  	mutex_unlock(&reg_regdb_search_mutex);
3b377ea9d   John W. Linville   wireless: support...
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
  }
  
  static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
  
  static void reg_regdb_query(const char *alpha2)
  {
  	struct reg_regdb_search_request *request;
  
  	if (!alpha2)
  		return;
  
  	request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL);
  	if (!request)
  		return;
  
  	memcpy(request->alpha2, alpha2, 2);
368d06f5b   John W. Linville   wireless: convert...
338
  	mutex_lock(&reg_regdb_search_mutex);
3b377ea9d   John W. Linville   wireless: support...
339
  	list_add_tail(&request->list, &reg_regdb_search_list);
368d06f5b   John W. Linville   wireless: convert...
340
  	mutex_unlock(&reg_regdb_search_mutex);
3b377ea9d   John W. Linville   wireless: support...
341
342
343
344
345
346
  
  	schedule_work(&reg_regdb_work);
  }
  #else
  static inline void reg_regdb_query(const char *alpha2) {}
  #endif /* CONFIG_CFG80211_INTERNAL_REGDB */
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
347
348
349
350
  /*
   * This lets us keep regulatory code which is updated on a regulatory
   * basis in userspace.
   */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
351
352
353
354
355
356
357
358
359
  static int call_crda(const char *alpha2)
  {
  	char country_env[9 + 2] = "COUNTRY=";
  	char *envp[] = {
  		country_env,
  		NULL
  	};
  
  	if (!is_world_regdom((char *) alpha2))
e9c0268f0   Joe Perches   net/wireless: Use...
360
361
  		pr_info("Calling CRDA for country: %c%c
  ",
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
362
363
  			alpha2[0], alpha2[1]);
  	else
e9c0268f0   Joe Perches   net/wireless: Use...
364
365
  		pr_info("Calling CRDA to update world regulatory domain
  ");
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
366

3b377ea9d   John W. Linville   wireless: support...
367
368
  	/* query internal regulatory database (if it exists) */
  	reg_regdb_query(alpha2);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
369
370
371
372
373
  	country_env[8] = alpha2[0];
  	country_env[9] = alpha2[1];
  
  	return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, envp);
  }
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
374
  /* Used by nl80211 before kmalloc'ing our regulatory domain */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
375
  bool reg_is_valid_request(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
376
  {
61405e977   Luis R. Rodriguez   cfg80211: fix in ...
377
  	assert_cfg80211_lock();
f6037d09e   Johannes Berg   wireless: get rid...
378
379
380
381
  	if (!last_request)
  		return false;
  
  	return alpha2_equal(last_request->alpha2, alpha2);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
382
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
383

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
384
  /* Sanity check on a regulatory rule */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
385
  static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
8318d78a4   Johannes Berg   cfg80211 API for ...
386
  {
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
387
  	const struct ieee80211_freq_range *freq_range = &rule->freq_range;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
388
  	u32 freq_diff;
91e990041   Luis R. Rodriguez   cfg80211: mark ne...
389
  	if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
390
391
392
393
394
395
  		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...
396
397
  	if (freq_range->end_freq_khz <= freq_range->start_freq_khz ||
  			freq_range->max_bandwidth_khz > freq_diff)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
398
399
400
401
  		return false;
  
  	return true;
  }
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
402
  static bool is_valid_rd(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
403
  {
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
404
  	const struct ieee80211_reg_rule *reg_rule = NULL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
405
  	unsigned int i;
8318d78a4   Johannes Berg   cfg80211 API for ...
406

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

88dc1c3f7   Luis R. Rodriguez   cfg80211: mark re...
410
411
  	if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES))
  		return false;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
412
413
414
415
416
417
418
  	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 ...
419
  }
038659e7c   Luis R. Rodriguez   cfg80211: Process...
420
421
422
  static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
  			    u32 center_freq_khz,
  			    u32 bw_khz)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
423
  {
038659e7c   Luis R. Rodriguez   cfg80211: Process...
424
425
426
427
428
429
430
431
432
433
  	u32 start_freq_khz, end_freq_khz;
  
  	start_freq_khz = center_freq_khz - (bw_khz/2);
  	end_freq_khz = center_freq_khz + (bw_khz/2);
  
  	if (start_freq_khz >= freq_range->start_freq_khz &&
  	    end_freq_khz <= freq_range->end_freq_khz)
  		return true;
  
  	return false;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
434
  }
8318d78a4   Johannes Berg   cfg80211 API for ...
435

0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
  /**
   * 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
   * definitions (the "2.4 GHz band" and the "5 GHz 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.
   * 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,
  	u32 freq_khz)
  {
  #define ONE_GHZ_IN_KHZ	1000000
  	if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
  		return true;
  	if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
  		return true;
  	return false;
  #undef ONE_GHZ_IN_KHZ
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
460
  /*
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
461
462
463
   * Helper for regdom_intersect(), this does the real
   * mathematical intersection fun
   */
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
  static int reg_rules_intersect(
  	const struct ieee80211_reg_rule *rule1,
  	const struct ieee80211_reg_rule *rule2,
  	struct ieee80211_reg_rule *intersected_rule)
  {
  	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;
  	u32 freq_diff;
  
  	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,
  		freq_range2->start_freq_khz);
  	freq_range->end_freq_khz = min(freq_range1->end_freq_khz,
  		freq_range2->end_freq_khz);
  	freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz,
  		freq_range2->max_bandwidth_khz);
  
  	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);
  
  	intersected_rule->flags = (rule1->flags | rule2->flags);
  
  	if (!is_valid_reg_rule(intersected_rule))
  		return -EINVAL;
  
  	return 0;
  }
  
  /**
   * 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.
   */
  static struct ieee80211_regdomain *regdom_intersect(
  	const struct ieee80211_regdomain *rd1,
  	const struct ieee80211_regdomain *rd2)
  {
  	int r, size_of_regd;
  	unsigned int x, y;
  	unsigned int num_rules = 0, rule_idx = 0;
  	const struct ieee80211_reg_rule *rule1, *rule2;
  	struct ieee80211_reg_rule *intersected_rule;
  	struct ieee80211_regdomain *rd;
  	/* This is just a dummy holder to help us count */
  	struct ieee80211_reg_rule irule;
  
  	/* Uses the stack temporarily for counter arithmetic */
  	intersected_rule = &irule;
  
  	memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule));
  
  	if (!rd1 || !rd2)
  		return NULL;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
540
541
  	/*
  	 * First we get a count of the rules we'll need, then we actually
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
542
543
544
  	 * 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...
545
546
  	 * All rules that do check out OK are valid.
  	 */
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  
  	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];
  			if (!reg_rules_intersect(rule1, rule2,
  					intersected_rule))
  				num_rules++;
  			memset(intersected_rule, 0,
  					sizeof(struct ieee80211_reg_rule));
  		}
  	}
  
  	if (!num_rules)
  		return NULL;
  
  	size_of_regd = sizeof(struct ieee80211_regdomain) +
  		((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
  
  	rd = kzalloc(size_of_regd, GFP_KERNEL);
  	if (!rd)
  		return NULL;
  
  	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];
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
574
575
  			/*
  			 * This time around instead of using the stack lets
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
576
  			 * write to the target rule directly saving ourselves
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
577
578
  			 * a memcpy()
  			 */
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
579
580
581
  			intersected_rule = &rd->reg_rules[rule_idx];
  			r = reg_rules_intersect(rule1, rule2,
  				intersected_rule);
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
582
583
584
585
  			/*
  			 * No need to memset here the intersected rule here as
  			 * we're not using the stack anymore
  			 */
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  			if (r)
  				continue;
  			rule_idx++;
  		}
  	}
  
  	if (rule_idx != num_rules) {
  		kfree(rd);
  		return NULL;
  	}
  
  	rd->n_reg_rules = num_rules;
  	rd->alpha2[0] = '9';
  	rd->alpha2[1] = '8';
  
  	return rd;
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
603
604
605
606
  /*
   * 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...
607
608
609
610
611
612
613
614
615
616
617
  static u32 map_regdom_flags(u32 rd_flags)
  {
  	u32 channel_flags = 0;
  	if (rd_flags & NL80211_RRF_PASSIVE_SCAN)
  		channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN;
  	if (rd_flags & NL80211_RRF_NO_IBSS)
  		channel_flags |= IEEE80211_CHAN_NO_IBSS;
  	if (rd_flags & NL80211_RRF_DFS)
  		channel_flags |= IEEE80211_CHAN_RADAR;
  	return channel_flags;
  }
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
618
619
  static int freq_reg_info_regd(struct wiphy *wiphy,
  			      u32 center_freq,
038659e7c   Luis R. Rodriguez   cfg80211: Process...
620
  			      u32 desired_bw_khz,
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
621
622
  			      const struct ieee80211_reg_rule **reg_rule,
  			      const struct ieee80211_regdomain *custom_regd)
8318d78a4   Johannes Berg   cfg80211 API for ...
623
624
  {
  	int i;
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
625
  	bool band_rule_found = false;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
626
  	const struct ieee80211_regdomain *regd;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
627
628
629
630
  	bool bw_fits = false;
  
  	if (!desired_bw_khz)
  		desired_bw_khz = MHZ_TO_KHZ(20);
8318d78a4   Johannes Berg   cfg80211 API for ...
631

1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
632
  	regd = custom_regd ? custom_regd : cfg80211_regdomain;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
633

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
634
635
636
637
  	/*
  	 * Follow the driver's regulatory domain, if present, unless a country
  	 * IE has been processed or a user wants to help complaince further
  	 */
2784fe915   Luis R. Rodriguez   cfg80211: fix nul...
638
639
  	if (!custom_regd &&
  	    last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
640
  	    last_request->initiator != NL80211_REGDOM_SET_BY_USER &&
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
641
642
643
644
  	    wiphy->regd)
  		regd = wiphy->regd;
  
  	if (!regd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
645
  		return -EINVAL;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
646
  	for (i = 0; i < regd->n_reg_rules; i++) {
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
647
648
649
  		const struct ieee80211_reg_rule *rr;
  		const struct ieee80211_freq_range *fr = NULL;
  		const struct ieee80211_power_rule *pr = NULL;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
650
  		rr = &regd->reg_rules[i];
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
651
652
  		fr = &rr->freq_range;
  		pr = &rr->power_rule;
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
653

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
654
655
  		/*
  		 * We only need to know if one frequency rule was
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
656
  		 * was in center_freq's band, that's enough, so lets
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
657
658
  		 * not overwrite it once found
  		 */
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
659
660
  		if (!band_rule_found)
  			band_rule_found = freq_in_rule_band(fr, center_freq);
038659e7c   Luis R. Rodriguez   cfg80211: Process...
661
662
663
  		bw_fits = reg_does_bw_fit(fr,
  					  center_freq,
  					  desired_bw_khz);
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
664

038659e7c   Luis R. Rodriguez   cfg80211: Process...
665
  		if (band_rule_found && bw_fits) {
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
666
  			*reg_rule = rr;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
667
  			return 0;
8318d78a4   Johannes Berg   cfg80211 API for ...
668
669
  		}
  	}
0c7dc45d2   Luis R. Rodriguez   cfg80211: Fix reg...
670
671
  	if (!band_rule_found)
  		return -ERANGE;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
672
  	return -EINVAL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
673
  }
038659e7c   Luis R. Rodriguez   cfg80211: Process...
674
675
676
677
  int freq_reg_info(struct wiphy *wiphy,
  		  u32 center_freq,
  		  u32 desired_bw_khz,
  		  const struct ieee80211_reg_rule **reg_rule)
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
678
  {
ac46d48e0   Luis R. Rodriguez   cfg80211: fix rac...
679
  	assert_cfg80211_lock();
038659e7c   Luis R. Rodriguez   cfg80211: Process...
680
681
682
683
684
  	return freq_reg_info_regd(wiphy,
  				  center_freq,
  				  desired_bw_khz,
  				  reg_rule,
  				  NULL);
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
685
  }
4f366c5da   John W. Linville   wireless: only us...
686
  EXPORT_SYMBOL(freq_reg_info);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
687

926a0a094   Luis R. Rodriguez   cfg80211: add deb...
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  #ifdef CONFIG_CFG80211_REG_DEBUG
  static const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
  {
  	switch (initiator) {
  	case NL80211_REGDOM_SET_BY_CORE:
  		return "Set by core";
  	case NL80211_REGDOM_SET_BY_USER:
  		return "Set by user";
  	case NL80211_REGDOM_SET_BY_DRIVER:
  		return "Set by driver";
  	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
  		return "Set by country IE";
  	default:
  		WARN_ON(1);
  		return "Set by bug";
  	}
  }
e702d3cf2   Luis R. Rodriguez   cfg80211: add deb...
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
  
  static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
  				    u32 desired_bw_khz,
  				    const struct ieee80211_reg_rule *reg_rule)
  {
  	const struct ieee80211_power_rule *power_rule;
  	const struct ieee80211_freq_range *freq_range;
  	char max_antenna_gain[32];
  
  	power_rule = &reg_rule->power_rule;
  	freq_range = &reg_rule->freq_range;
  
  	if (!power_rule->max_antenna_gain)
  		snprintf(max_antenna_gain, 32, "N/A");
  	else
  		snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain);
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
721
  	REG_DBG_PRINT("Updating information on frequency %d MHz "
ff039c6fb   Bob Copeland   cfg80211: fix tra...
722
723
  		      "for a %d MHz width channel with regulatory rule:
  ",
e702d3cf2   Luis R. Rodriguez   cfg80211: add deb...
724
725
  		      chan->center_freq,
  		      KHZ_TO_MHZ(desired_bw_khz));
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
726
727
  	REG_DBG_PRINT("%d KHz - %d KHz @  KHz), (%s mBi, %d mBm)
  ",
e702d3cf2   Luis R. Rodriguez   cfg80211: add deb...
728
729
730
731
732
733
734
735
736
737
738
739
  		      freq_range->start_freq_khz,
  		      freq_range->end_freq_khz,
  		      max_antenna_gain,
  		      power_rule->max_eirp);
  }
  #else
  static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
  				    u32 desired_bw_khz,
  				    const struct ieee80211_reg_rule *reg_rule)
  {
  	return;
  }
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
740
  #endif
038659e7c   Luis R. Rodriguez   cfg80211: Process...
741
742
743
744
745
746
747
748
749
  /*
   * 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). To support
   * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a
   * new ieee80211_channel.target_bw and re run the regulatory check
   * on the wiphy with the target_bw specified. Then we can simply use
   * that below for the desired_bw_khz below.
   */
7ca43d03b   Luis R. Rodriguez   cfg80211: pass th...
750
751
752
  static void handle_channel(struct wiphy *wiphy,
  			   enum nl80211_reg_initiator initiator,
  			   enum ieee80211_band band,
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
753
  			   unsigned int chan_idx)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
754
755
  {
  	int r;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
756
757
  	u32 flags, bw_flags = 0;
  	u32 desired_bw_khz = MHZ_TO_KHZ(20);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
758
759
  	const struct ieee80211_reg_rule *reg_rule = NULL;
  	const struct ieee80211_power_rule *power_rule = NULL;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
760
  	const struct ieee80211_freq_range *freq_range = NULL;
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
761
762
  	struct ieee80211_supported_band *sband;
  	struct ieee80211_channel *chan;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
763
  	struct wiphy *request_wiphy = NULL;
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
764

761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
765
  	assert_cfg80211_lock();
806a9e396   Luis R. Rodriguez   cfg80211: make re...
766
  	request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
767
768
769
770
771
  	sband = wiphy->bands[band];
  	BUG_ON(chan_idx >= sband->n_channels);
  	chan = &sband->channels[chan_idx];
  
  	flags = chan->orig_flags;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
772

038659e7c   Luis R. Rodriguez   cfg80211: Process...
773
774
775
776
  	r = freq_reg_info(wiphy,
  			  MHZ_TO_KHZ(chan->center_freq),
  			  desired_bw_khz,
  			  &reg_rule);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
777

ca4ffe8f2   Luis R. Rodriguez   cfg80211: fix dis...
778
779
780
781
782
783
784
785
786
787
788
789
790
791
  	if (r) {
  		/*
  		 * We will disable all channels that do not match our
  		 * recieved regulatory rule unless the hint is coming
  		 * 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 &&
  		    r == -ERANGE)
  			return;
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
792
793
  		REG_DBG_PRINT("Disabling freq %d MHz
  ", chan->center_freq);
ca4ffe8f2   Luis R. Rodriguez   cfg80211: fix dis...
794
  		chan->flags = IEEE80211_CHAN_DISABLED;
8318d78a4   Johannes Berg   cfg80211 API for ...
795
  		return;
ca4ffe8f2   Luis R. Rodriguez   cfg80211: fix dis...
796
  	}
8318d78a4   Johannes Berg   cfg80211 API for ...
797

e702d3cf2   Luis R. Rodriguez   cfg80211: add deb...
798
  	chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
799
  	power_rule = &reg_rule->power_rule;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
800
801
802
803
  	freq_range = &reg_rule->freq_range;
  
  	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
  		bw_flags = IEEE80211_CHAN_NO_HT40;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
804

7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
805
  	if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
806a9e396   Luis R. Rodriguez   cfg80211: make re...
806
  	    request_wiphy && request_wiphy == wiphy &&
5be83de54   Johannes Berg   cfg80211: convert...
807
  	    request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
808
809
  		/*
  		 * This gaurantees the driver's requested regulatory domain
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
810
  		 * will always be used as a base for further regulatory
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
811
812
  		 * settings
  		 */
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
813
  		chan->flags = chan->orig_flags =
038659e7c   Luis R. Rodriguez   cfg80211: Process...
814
  			map_regdom_flags(reg_rule->flags) | bw_flags;
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
815
816
  		chan->max_antenna_gain = chan->orig_mag =
  			(int) MBI_TO_DBI(power_rule->max_antenna_gain);
f976376de   Luis R. Rodriguez   cfg80211: Allow f...
817
818
819
820
  		chan->max_power = chan->orig_mpwr =
  			(int) MBM_TO_DBM(power_rule->max_eirp);
  		return;
  	}
038659e7c   Luis R. Rodriguez   cfg80211: Process...
821
  	chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
8318d78a4   Johannes Berg   cfg80211 API for ...
822
  	chan->max_antenna_gain = min(chan->orig_mag,
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
823
  		(int) MBI_TO_DBI(power_rule->max_antenna_gain));
253898c41   John W. Linville   cfg80211: default...
824
  	if (chan->orig_mpwr)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
825
826
  		chan->max_power = min(chan->orig_mpwr,
  			(int) MBM_TO_DBM(power_rule->max_eirp));
253898c41   John W. Linville   cfg80211: default...
827
  	else
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
828
  		chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
8318d78a4   Johannes Berg   cfg80211 API for ...
829
  }
7ca43d03b   Luis R. Rodriguez   cfg80211: pass th...
830
831
832
  static void handle_band(struct wiphy *wiphy,
  			enum ieee80211_band band,
  			enum nl80211_reg_initiator initiator)
8318d78a4   Johannes Berg   cfg80211 API for ...
833
  {
a92a3ce72   Luis R. Rodriguez   cfg80211: make ha...
834
835
836
837
838
  	unsigned int i;
  	struct ieee80211_supported_band *sband;
  
  	BUG_ON(!wiphy->bands[band]);
  	sband = wiphy->bands[band];
8318d78a4   Johannes Berg   cfg80211 API for ...
839
840
  
  	for (i = 0; i < sband->n_channels; i++)
7ca43d03b   Luis R. Rodriguez   cfg80211: pass th...
841
  		handle_channel(wiphy, initiator, band, i);
8318d78a4   Johannes Berg   cfg80211 API for ...
842
  }
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
843
844
  static bool ignore_reg_update(struct wiphy *wiphy,
  			      enum nl80211_reg_initiator initiator)
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
845
  {
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
846
  	if (!last_request) {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
847
  		REG_DBG_PRINT("Ignoring regulatory request %s since "
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
848
849
850
  			      "last_request is not set
  ",
  			      reg_initiator_name(initiator));
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
851
  		return true;
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
852
  	}
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
853
  	if (initiator == NL80211_REGDOM_SET_BY_CORE &&
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
854
  	    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
855
  		REG_DBG_PRINT("Ignoring regulatory request %s "
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
856
857
858
  			      "since the driver uses its own custom "
  			      "regulatory domain ",
  			      reg_initiator_name(initiator));
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
859
  		return true;
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
860
  	}
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
861
862
863
864
  	/*
  	 * wiphy->regd will be set once the device has its own
  	 * desired regulatory domain set
  	 */
5be83de54   Johannes Berg   cfg80211: convert...
865
  	if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
749b527b2   Luis R. Rodriguez   cfg80211: fix all...
866
  	    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
867
  	    !is_world_regdom(last_request->alpha2)) {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
868
  		REG_DBG_PRINT("Ignoring regulatory request %s "
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
869
870
871
  			      "since the driver requires its own regulaotry "
  			      "domain to be set first",
  			      reg_initiator_name(initiator));
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
872
  		return true;
926a0a094   Luis R. Rodriguez   cfg80211: add deb...
873
  	}
14b9815af   Luis R. Rodriguez   cfg80211: add sup...
874
875
  	return false;
  }
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
876
  static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
8318d78a4   Johannes Berg   cfg80211 API for ...
877
  {
79c97e97a   Johannes Berg   cfg80211: clean u...
878
  	struct cfg80211_registered_device *rdev;
8318d78a4   Johannes Berg   cfg80211 API for ...
879

79c97e97a   Johannes Berg   cfg80211: clean u...
880
881
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list)
  		wiphy_update_regulatory(&rdev->wiphy, initiator);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
882
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
883
884
885
886
  static void handle_reg_beacon(struct wiphy *wiphy,
  			      unsigned int chan_idx,
  			      struct reg_beacon *reg_beacon)
  {
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
887
888
  	struct ieee80211_supported_band *sband;
  	struct ieee80211_channel *chan;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
889
890
  	bool channel_changed = false;
  	struct ieee80211_channel chan_before;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
891
892
893
894
895
896
897
898
  
  	assert_cfg80211_lock();
  
  	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...
899
900
901
902
  	if (chan->beacon_found)
  		return;
  
  	chan->beacon_found = true;
5be83de54   Johannes Berg   cfg80211: convert...
903
  	if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS)
371842448   Luis R. Rodriguez   cfg80211: fix reg...
904
  		return;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
905
906
  	chan_before.center_freq = chan->center_freq;
  	chan_before.flags = chan->flags;
371842448   Luis R. Rodriguez   cfg80211: fix reg...
907
  	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
908
  		chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
909
  		channel_changed = true;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
910
  	}
371842448   Luis R. Rodriguez   cfg80211: fix reg...
911
  	if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
912
  		chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
6bad87666   Luis R. Rodriguez   cfg80211: send re...
913
  		channel_changed = true;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
914
  	}
6bad87666   Luis R. Rodriguez   cfg80211: send re...
915
916
  	if (channel_changed)
  		nl80211_send_beacon_hint_event(wiphy, &chan_before, chan);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
  }
  
  /*
   * 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;
  
  	assert_cfg80211_lock();
  
  	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;
  
  	assert_cfg80211_lock();
  
  	if (list_empty(&reg_beacon_list))
  		return;
  
  	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);
  	}
  }
  
  static bool reg_is_world_roaming(struct wiphy *wiphy)
  {
  	if (is_world_regdom(cfg80211_regdomain->alpha2) ||
  	    (wiphy->regd && is_world_regdom(wiphy->regd->alpha2)))
  		return true;
b1ed8ddd2   Luis R. Rodriguez   cfg80211: fix bug...
968
969
  	if (last_request &&
  	    last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
5be83de54   Johannes Berg   cfg80211: convert...
970
  	    wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
971
972
973
974
975
976
977
  		return true;
  	return false;
  }
  
  /* Reap the advantages of previously found beacons */
  static void reg_process_beacons(struct wiphy *wiphy)
  {
b1ed8ddd2   Luis R. Rodriguez   cfg80211: fix bug...
978
979
980
981
982
983
  	/*
  	 * 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 ...
984
985
986
987
  	if (!reg_is_world_roaming(wiphy))
  		return;
  	wiphy_update_beacon_reg(wiphy);
  }
038659e7c   Luis R. Rodriguez   cfg80211: Process...
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  static bool is_ht40_not_allowed(struct ieee80211_channel *chan)
  {
  	if (!chan)
  		return true;
  	if (chan->flags & IEEE80211_CHAN_DISABLED)
  		return true;
  	/* This would happen when regulatory rules disallow HT40 completely */
  	if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40)))
  		return true;
  	return false;
  }
  
  static void reg_process_ht_flags_channel(struct wiphy *wiphy,
  					 enum ieee80211_band band,
  					 unsigned int chan_idx)
  {
  	struct ieee80211_supported_band *sband;
  	struct ieee80211_channel *channel;
  	struct ieee80211_channel *channel_before = NULL, *channel_after = NULL;
  	unsigned int i;
  
  	assert_cfg80211_lock();
  
  	sband = wiphy->bands[band];
  	BUG_ON(chan_idx >= sband->n_channels);
  	channel = &sband->channels[chan_idx];
  
  	if (is_ht40_not_allowed(channel)) {
  		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];
  		if (c->center_freq == (channel->center_freq - 20))
  			channel_before = c;
  		if (c->center_freq == (channel->center_freq + 20))
  			channel_after = c;
  	}
  
  	/*
  	 * 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.
  	 */
  	if (is_ht40_not_allowed(channel_before))
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1038
  		channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1039
  	else
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1040
  		channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1041
1042
  
  	if (is_ht40_not_allowed(channel_after))
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1043
  		channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1044
  	else
689da1b3b   Luis R. Rodriguez   wireless: rename ...
1045
  		channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
  }
  
  static void reg_process_ht_flags_band(struct wiphy *wiphy,
  				      enum ieee80211_band band)
  {
  	unsigned int i;
  	struct ieee80211_supported_band *sband;
  
  	BUG_ON(!wiphy->bands[band]);
  	sband = wiphy->bands[band];
  
  	for (i = 0; i < sband->n_channels; i++)
  		reg_process_ht_flags_channel(wiphy, band, i);
  }
  
  static void reg_process_ht_flags(struct wiphy *wiphy)
  {
  	enum ieee80211_band band;
  
  	if (!wiphy)
  		return;
  
  	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
  		if (wiphy->bands[band])
  			reg_process_ht_flags_band(wiphy, band);
  	}
  
  }
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1074
1075
  void wiphy_update_regulatory(struct wiphy *wiphy,
  			     enum nl80211_reg_initiator initiator)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1076
1077
  {
  	enum ieee80211_band band;
d46e5b1d0   Luis R. Rodriguez   cfg80211: move ch...
1078

7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1079
  	if (ignore_reg_update(wiphy, initiator))
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1080
  		goto out;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1081
  	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
8318d78a4   Johannes Berg   cfg80211 API for ...
1082
  		if (wiphy->bands[band])
7ca43d03b   Luis R. Rodriguez   cfg80211: pass th...
1083
  			handle_band(wiphy, band, initiator);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1084
  	}
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1085
1086
  out:
  	reg_process_beacons(wiphy);
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1087
  	reg_process_ht_flags(wiphy);
560e28e14   Luis R. Rodriguez   cfg80211: call re...
1088
  	if (wiphy->reg_notifier)
716f9392e   Luis R. Rodriguez   cfg80211: pass mo...
1089
  		wiphy->reg_notifier(wiphy, last_request);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1090
  }
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1091
1092
1093
1094
1095
1096
  static void handle_channel_custom(struct wiphy *wiphy,
  				  enum ieee80211_band band,
  				  unsigned int chan_idx,
  				  const struct ieee80211_regdomain *regd)
  {
  	int r;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1097
1098
  	u32 desired_bw_khz = MHZ_TO_KHZ(20);
  	u32 bw_flags = 0;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1099
1100
  	const struct ieee80211_reg_rule *reg_rule = NULL;
  	const struct ieee80211_power_rule *power_rule = NULL;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1101
  	const struct ieee80211_freq_range *freq_range = NULL;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1102
1103
  	struct ieee80211_supported_band *sband;
  	struct ieee80211_channel *chan;
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1104
  	assert_reg_lock();
ac46d48e0   Luis R. Rodriguez   cfg80211: fix rac...
1105

1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1106
1107
1108
  	sband = wiphy->bands[band];
  	BUG_ON(chan_idx >= sband->n_channels);
  	chan = &sband->channels[chan_idx];
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1109
1110
1111
1112
1113
  	r = freq_reg_info_regd(wiphy,
  			       MHZ_TO_KHZ(chan->center_freq),
  			       desired_bw_khz,
  			       &reg_rule,
  			       regd);
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1114
1115
  
  	if (r) {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1116
  		REG_DBG_PRINT("Disabling freq %d MHz as custom "
a65185367   Luis R. Rodriguez   cfg80211: add deb...
1117
1118
1119
1120
1121
  			      "regd has no rule that fits a %d MHz "
  			      "wide channel
  ",
  			      chan->center_freq,
  			      KHZ_TO_MHZ(desired_bw_khz));
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1122
1123
1124
  		chan->flags = IEEE80211_CHAN_DISABLED;
  		return;
  	}
e702d3cf2   Luis R. Rodriguez   cfg80211: add deb...
1125
  	chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule);
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1126
  	power_rule = &reg_rule->power_rule;
038659e7c   Luis R. Rodriguez   cfg80211: Process...
1127
1128
1129
1130
  	freq_range = &reg_rule->freq_range;
  
  	if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
  		bw_flags = IEEE80211_CHAN_NO_HT40;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1131

038659e7c   Luis R. Rodriguez   cfg80211: Process...
1132
  	chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1133
  	chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
  	chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
  }
  
  static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band,
  			       const struct ieee80211_regdomain *regd)
  {
  	unsigned int i;
  	struct ieee80211_supported_band *sband;
  
  	BUG_ON(!wiphy->bands[band]);
  	sband = wiphy->bands[band];
  
  	for (i = 0; i < sband->n_channels; i++)
  		handle_channel_custom(wiphy, band, i, regd);
  }
  
  /* Used by drivers prior to wiphy registration */
  void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
  				   const struct ieee80211_regdomain *regd)
  {
  	enum ieee80211_band band;
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1155
  	unsigned int bands_set = 0;
ac46d48e0   Luis R. Rodriguez   cfg80211: fix rac...
1156

abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1157
  	mutex_lock(&reg_mutex);
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1158
  	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1159
1160
1161
1162
  		if (!wiphy->bands[band])
  			continue;
  		handle_band_custom(wiphy, band, regd);
  		bands_set++;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1163
  	}
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1164
  	mutex_unlock(&reg_mutex);
bbcf3f027   Luis R. Rodriguez   cfg80211: warn wh...
1165
1166
1167
1168
1169
1170
  
  	/*
  	 * no point in calling this if it won't have any effect
  	 * on your device's supportd bands.
  	 */
  	WARN_ON(!bands_set);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1171
  }
1fa25e413   Luis R. Rodriguez   cfg80211: add wip...
1172
  EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1173
1174
1175
1176
  /*
   * Return value which can be used by ignore_request() to indicate
   * it has been determined we should intersect two regulatory domains
   */
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1177
  #define REG_INTERSECT	1
84fa4f43c   Johannes Berg   wireless regulato...
1178
1179
  /* This has the logic which determines when a new request
   * should be ignored. */
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1180
1181
  static int ignore_request(struct wiphy *wiphy,
  			  struct regulatory_request *pending_request)
84fa4f43c   Johannes Berg   wireless regulato...
1182
  {
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1183
  	struct wiphy *last_wiphy = NULL;
761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
1184
1185
  
  	assert_cfg80211_lock();
84fa4f43c   Johannes Berg   wireless regulato...
1186
1187
1188
  	/* All initial requests are respected */
  	if (!last_request)
  		return 0;
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1189
  	switch (pending_request->initiator) {
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1190
  	case NL80211_REGDOM_SET_BY_CORE:
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1191
  		return 0;
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1192
  	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1193
1194
  
  		last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1195
  		if (unlikely(!is_an_alpha2(pending_request->alpha2)))
84fa4f43c   Johannes Berg   wireless regulato...
1196
  			return -EINVAL;
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1197
1198
  		if (last_request->initiator ==
  		    NL80211_REGDOM_SET_BY_COUNTRY_IE) {
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1199
  			if (last_wiphy != wiphy) {
84fa4f43c   Johannes Berg   wireless regulato...
1200
1201
  				/*
  				 * Two cards with two APs claiming different
1fe90b033   Thadeu Lima de Souza Cascardo   trivial: remove d...
1202
  				 * Country IE alpha2s. We could
84fa4f43c   Johannes Berg   wireless regulato...
1203
1204
1205
  				 * intersect them, but that seems unlikely
  				 * to be correct. Reject second one for now.
  				 */
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1206
  				if (regdom_changes(pending_request->alpha2))
84fa4f43c   Johannes Berg   wireless regulato...
1207
1208
1209
  					return -EOPNOTSUPP;
  				return -EALREADY;
  			}
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1210
1211
1212
1213
  			/*
  			 * Two consecutive Country IE hints on the same wiphy.
  			 * This should be picked up early by the driver/stack
  			 */
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1214
  			if (WARN_ON(regdom_changes(pending_request->alpha2)))
84fa4f43c   Johannes Berg   wireless regulato...
1215
1216
1217
  				return 0;
  			return -EALREADY;
  		}
a171fba49   Luis R. Rodriguez   cfg80211: fix reg...
1218
  		return 0;
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1219
1220
  	case NL80211_REGDOM_SET_BY_DRIVER:
  		if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) {
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1221
  			if (regdom_changes(pending_request->alpha2))
e74b1e7fb   Luis R. Rodriguez   cfg80211: ignore ...
1222
  				return 0;
84fa4f43c   Johannes Berg   wireless regulato...
1223
  			return -EALREADY;
e74b1e7fb   Luis R. Rodriguez   cfg80211: ignore ...
1224
  		}
fff32c04f   Luis R. Rodriguez   cfg80211: allow d...
1225
1226
1227
1228
1229
1230
  
  		/*
  		 * 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.
  		 */
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1231
  		if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1232
  		    !regdom_changes(pending_request->alpha2))
fff32c04f   Luis R. Rodriguez   cfg80211: allow d...
1233
  			return -EALREADY;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1234
  		return REG_INTERSECT;
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1235
1236
  	case NL80211_REGDOM_SET_BY_USER:
  		if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1237
  			return REG_INTERSECT;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1238
1239
1240
1241
  		/*
  		 * If the user knows better the user should set the regdom
  		 * to their country before the IE is picked up
  		 */
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1242
  		if (last_request->initiator == NL80211_REGDOM_SET_BY_USER &&
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1243
1244
  			  last_request->intersect)
  			return -EOPNOTSUPP;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1245
1246
1247
1248
  		/*
  		 * Process user requests only after previous user/driver/core
  		 * requests have been processed
  		 */
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1249
1250
1251
  		if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE ||
  		    last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
  		    last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
69b1572bd   Luis R. Rodriguez   cfg80211: rename ...
1252
  			if (regdom_changes(last_request->alpha2))
5eebade60   Luis R. Rodriguez   cfg80211: process...
1253
1254
  				return -EAGAIN;
  		}
baeb66fe2   John W. Linville   wireless: remove ...
1255
  		if (!regdom_changes(pending_request->alpha2))
e74b1e7fb   Luis R. Rodriguez   cfg80211: ignore ...
1256
  			return -EALREADY;
84fa4f43c   Johannes Berg   wireless regulato...
1257
1258
1259
1260
1261
  		return 0;
  	}
  
  	return -EINVAL;
  }
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
  static void reg_set_request_processed(void)
  {
  	bool need_more_processing = false;
  
  	last_request->processed = true;
  
  	spin_lock(&reg_requests_lock);
  	if (!list_empty(&reg_requests_list))
  		need_more_processing = true;
  	spin_unlock(&reg_requests_lock);
  
  	if (need_more_processing)
  		schedule_work(&reg_work);
  }
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1276
1277
1278
1279
  /**
   * __regulatory_hint - hint to the wireless core a regulatory domain
   * @wiphy: if the hint comes from country information from an AP, this
   *	is required to be set to the wiphy that received the information
28da32d7c   Luis R. Rodriguez   cfg80211: pass th...
1280
   * @pending_request: the regulatory request currently being processed
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1281
1282
   *
   * The Wireless subsystem can use this function to hint to the wireless core
28da32d7c   Luis R. Rodriguez   cfg80211: pass th...
1283
   * what it believes should be the current regulatory domain.
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1284
1285
1286
1287
   *
   * Returns zero if all went fine, %-EALREADY if a regulatory domain had
   * already been set or other standard error codes.
   *
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1288
   * Caller must hold &cfg80211_mutex and &reg_mutex
d1c96a9a2   Luis R. Rodriguez   cfg80211: make __...
1289
   */
28da32d7c   Luis R. Rodriguez   cfg80211: pass th...
1290
1291
  static int __regulatory_hint(struct wiphy *wiphy,
  			     struct regulatory_request *pending_request)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1292
  {
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1293
  	bool intersect = false;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1294
  	int r = 0;
761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
1295
  	assert_cfg80211_lock();
2f92cd2e5   Luis R. Rodriguez   cfg80211: pass th...
1296
  	r = ignore_request(wiphy, pending_request);
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1297

3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1298
  	if (r == REG_INTERSECT) {
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1299
1300
  		if (pending_request->initiator ==
  		    NL80211_REGDOM_SET_BY_DRIVER) {
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1301
  			r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1302
1303
  			if (r) {
  				kfree(pending_request);
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1304
  				return r;
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1305
  			}
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1306
  		}
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1307
  		intersect = true;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1308
  	} else if (r) {
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1309
1310
  		/*
  		 * If the regulatory domain being requested by the
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1311
  		 * driver has already been set just copy it to the
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1312
1313
  		 * wiphy
  		 */
28da32d7c   Luis R. Rodriguez   cfg80211: pass th...
1314
  		if (r == -EALREADY &&
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1315
1316
  		    pending_request->initiator ==
  		    NL80211_REGDOM_SET_BY_DRIVER) {
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1317
  			r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1318
1319
  			if (r) {
  				kfree(pending_request);
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1320
  				return r;
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1321
  			}
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1322
1323
1324
  			r = -EALREADY;
  			goto new_request;
  		}
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1325
  		kfree(pending_request);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1326
  		return r;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1327
  	}
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1328

3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1329
  new_request:
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1330
  	kfree(last_request);
5203cdb6a   Luis R. Rodriguez   cfg80211: remove ...
1331

d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1332
1333
  	last_request = pending_request;
  	last_request->intersect = intersect;
5203cdb6a   Luis R. Rodriguez   cfg80211: remove ...
1334

d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1335
  	pending_request = NULL;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1336

09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1337
1338
1339
1340
  	if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
  		user_alpha2[0] = last_request->alpha2[0];
  		user_alpha2[1] = last_request->alpha2[1];
  	}
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1341
  	/* When r == REG_INTERSECT we do need to call CRDA */
73d54c9e7   Luis R. Rodriguez   cfg80211: add reg...
1342
1343
1344
1345
1346
1347
  	if (r < 0) {
  		/*
  		 * 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
  		 */
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1348
  		if (r == -EALREADY) {
73d54c9e7   Luis R. Rodriguez   cfg80211: add reg...
1349
  			nl80211_send_reg_change_event(last_request);
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1350
1351
  			reg_set_request_processed();
  		}
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1352
  		return r;
73d54c9e7   Luis R. Rodriguez   cfg80211: add reg...
1353
  	}
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1354

d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1355
  	return call_crda(last_request->alpha2);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1356
  }
30a548c72   Luis R. Rodriguez   cfg80211: fix com...
1357
  /* This processes *all* regulatory hints */
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1358
  static void reg_process_hint(struct regulatory_request *reg_request)
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1359
1360
1361
  {
  	int r = 0;
  	struct wiphy *wiphy = NULL;
c4c322941   Yuri Ershov   cfg80211: Update ...
1362
  	enum nl80211_reg_initiator initiator = reg_request->initiator;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1363
1364
  
  	BUG_ON(!reg_request->alpha2);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1365
1366
  	if (wiphy_idx_valid(reg_request->wiphy_idx))
  		wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1367
  	if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1368
  	    !wiphy) {
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1369
  		kfree(reg_request);
b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
1370
  		return;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1371
  	}
28da32d7c   Luis R. Rodriguez   cfg80211: pass th...
1372
  	r = __regulatory_hint(wiphy, reg_request);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1373
  	/* This is required so that the orig_* parameters are saved */
5be83de54   Johannes Berg   cfg80211: convert...
1374
1375
  	if (r == -EALREADY && wiphy &&
  	    wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
c4c322941   Yuri Ershov   cfg80211: Update ...
1376
  		wiphy_update_regulatory(wiphy, initiator);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1377
  }
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1378
1379
1380
1381
1382
  /*
   * 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...
1383
  static void reg_process_pending_hints(void)
b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
1384
  {
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1385
  	struct regulatory_request *reg_request;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1386

b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
1387
1388
  	mutex_lock(&cfg80211_mutex);
  	mutex_lock(&reg_mutex);
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1389
1390
1391
1392
1393
1394
  	/* When last_request->processed becomes true this will be rescheduled */
  	if (last_request && !last_request->processed) {
  		REG_DBG_PRINT("Pending regulatory request, waiting "
  			      "for it to be processed...");
  		goto out;
  	}
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1395
  	spin_lock(&reg_requests_lock);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1396

b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1397
  	if (list_empty(&reg_requests_list)) {
d951c1dde   Luis R. Rodriguez   cfg80211: do not ...
1398
  		spin_unlock(&reg_requests_lock);
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1399
  		goto out;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1400
  	}
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1401
1402
1403
1404
1405
  
  	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...
1406
  	spin_unlock(&reg_requests_lock);
b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
1407

b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1408
1409
1410
  	reg_process_hint(reg_request);
  
  out:
b0e2880b0   Luis R. Rodriguez   cfg80211: move mu...
1411
1412
  	mutex_unlock(&reg_mutex);
  	mutex_unlock(&cfg80211_mutex);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1413
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1414
1415
1416
  /* 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...
1417
  	struct cfg80211_registered_device *rdev;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1418
  	struct reg_beacon *pending_beacon, *tmp;
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1419
1420
1421
1422
  	/*
  	 * No need to hold the reg_mutex here as we just touch wiphys
  	 * and do not read or access regulatory variables.
  	 */
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
  	mutex_lock(&cfg80211_mutex);
  
  	/* This goes through the _pending_ beacon list */
  	spin_lock_bh(&reg_pending_beacons_lock);
  
  	if (list_empty(&reg_pending_beacons)) {
  		spin_unlock_bh(&reg_pending_beacons_lock);
  		goto out;
  	}
  
  	list_for_each_entry_safe(pending_beacon, tmp,
  				 &reg_pending_beacons, list) {
  
  		list_del_init(&pending_beacon->list);
  
  		/* Applies the beacon hint to current wiphys */
79c97e97a   Johannes Berg   cfg80211: clean u...
1439
1440
  		list_for_each_entry(rdev, &cfg80211_rdev_list, list)
  			wiphy_update_new_beacon(&rdev->wiphy, pending_beacon);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1441
1442
1443
1444
1445
1446
1447
1448
1449
  
  		/* 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);
  out:
  	mutex_unlock(&cfg80211_mutex);
  }
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1450
1451
1452
  static void reg_todo(struct work_struct *work)
  {
  	reg_process_pending_hints();
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1453
  	reg_process_pending_beacon_hints();
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1454
  }
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1455
1456
  static void queue_regulatory_request(struct regulatory_request *request)
  {
c61029c77   John W. Linville   wireless: upcase ...
1457
1458
1459
1460
  	if (isalpha(request->alpha2[0]))
  		request->alpha2[0] = toupper(request->alpha2[0]);
  	if (isalpha(request->alpha2[1]))
  		request->alpha2[1] = toupper(request->alpha2[1]);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1461
1462
1463
1464
1465
1466
  	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...
1467
1468
1469
1470
  /*
   * Core regulatory hint -- happens during cfg80211_init()
   * and when we restore regulatory settings.
   */
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
1471
1472
1473
  static int regulatory_hint_core(const char *alpha2)
  {
  	struct regulatory_request *request;
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1474
1475
  	kfree(last_request);
  	last_request = NULL;
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
1476
1477
1478
1479
1480
1481
1482
1483
  
  	request = kzalloc(sizeof(struct regulatory_request),
  			  GFP_KERNEL);
  	if (!request)
  		return -ENOMEM;
  
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1484
  	request->initiator = NL80211_REGDOM_SET_BY_CORE;
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
1485

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

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1488
  	return 0;
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
1489
  }
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1490
1491
  /* User hints */
  int regulatory_hint_user(const char *alpha2)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1492
  {
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1493
  	struct regulatory_request *request;
be3d48106   Johannes Berg   wireless: remove ...
1494
  	BUG_ON(!alpha2);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1495

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1496
1497
1498
1499
1500
1501
1502
  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
  	if (!request)
  		return -ENOMEM;
  
  	request->wiphy_idx = WIPHY_IDX_STALE;
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
e12822e1d   Luis R. Rodriguez   cfg80211: fix syn...
1503
  	request->initiator = NL80211_REGDOM_SET_BY_USER;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
  
  	queue_regulatory_request(request);
  
  	return 0;
  }
  
  /* Driver hints */
  int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
  {
  	struct regulatory_request *request;
  
  	BUG_ON(!alpha2);
  	BUG_ON(!wiphy);
  
  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
  	if (!request)
  		return -ENOMEM;
  
  	request->wiphy_idx = get_wiphy_idx(wiphy);
  
  	/* Must have registered wiphy first */
  	BUG_ON(!wiphy_idx_valid(request->wiphy_idx));
  
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1529
  	request->initiator = NL80211_REGDOM_SET_BY_DRIVER;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1530
1531
1532
1533
  
  	queue_regulatory_request(request);
  
  	return 0;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1534
1535
  }
  EXPORT_SYMBOL(regulatory_hint);
4b44c8bc4   Luis R. Rodriguez   cfg80211: do not ...
1536
1537
1538
1539
  /*
   * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and
   * therefore cannot iterate over the rdev list here.
   */
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1540
  void regulatory_hint_11d(struct wiphy *wiphy,
84920e3e4   Luis R. Rodriguez   cfg80211: make re...
1541
1542
1543
  			 enum ieee80211_band band,
  			 u8 *country_ie,
  			 u8 country_ie_len)
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1544
  {
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1545
  	char alpha2[2];
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1546
  	enum environment_cap env = ENVIRON_ANY;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1547
  	struct regulatory_request *request;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1548

abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1549
  	mutex_lock(&reg_mutex);
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1550

9828b0170   Luis R. Rodriguez   cfg80211: use got...
1551
1552
  	if (unlikely(!last_request))
  		goto out;
d335fe639   Luis R. Rodriguez   cfg80211: protect...
1553

3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1554
1555
1556
1557
1558
1559
  	/* IE len must be evenly divisible by 2 */
  	if (country_ie_len & 0x01)
  		goto out;
  
  	if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
  		goto out;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1560
1561
1562
1563
1564
1565
1566
  	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;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1567
  	/*
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
1568
  	 * We will run this only upon a successful connection on cfg80211.
4b44c8bc4   Luis R. Rodriguez   cfg80211: do not ...
1569
1570
  	 * We leave conflict resolution to the workqueue, where can hold
  	 * cfg80211_mutex.
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1571
  	 */
cc0b6fe88   Luis R. Rodriguez   cfg80211: fix inc...
1572
1573
  	if (likely(last_request->initiator ==
  	    NL80211_REGDOM_SET_BY_COUNTRY_IE &&
4b44c8bc4   Luis R. Rodriguez   cfg80211: do not ...
1574
1575
  	    wiphy_idx_valid(last_request->wiphy_idx)))
  		goto out;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1576

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1577
1578
  	request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
  	if (!request)
f9f9b6e3e   Dan Carpenter   wireless: remove ...
1579
  		goto out;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1580

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1581
  	request->wiphy_idx = get_wiphy_idx(wiphy);
4f366c5da   John W. Linville   wireless: only us...
1582
1583
  	request->alpha2[0] = alpha2[0];
  	request->alpha2[1] = alpha2[1];
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1584
  	request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1585
  	request->country_ie_env = env;
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1586
  	mutex_unlock(&reg_mutex);
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1587

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
1588
1589
1590
  	queue_regulatory_request(request);
  
  	return;
0441d6ffc   Luis R. Rodriguez   cfg80211: free rd...
1591

3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1592
  out:
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1593
  	mutex_unlock(&reg_mutex);
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1594
  }
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1595

09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
  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) {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1606
  			REG_DBG_PRINT("Restoring regulatory settings "
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
  			       "including user preference
  ");
  			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)) {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1618
  				REG_DBG_PRINT("Keeping preference on "
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1619
1620
1621
1622
1623
1624
1625
1626
  				       "module parameter ieee80211_regdom: %c%c
  ",
  				       ieee80211_regdom[0],
  				       ieee80211_regdom[1]);
  				alpha2[0] = ieee80211_regdom[0];
  				alpha2[1] = ieee80211_regdom[1];
  			}
  		} else {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1627
  			REG_DBG_PRINT("Restoring regulatory settings "
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1628
1629
1630
1631
1632
1633
1634
1635
  			       "while preserving user preference for: %c%c
  ",
  			       user_alpha2[0],
  			       user_alpha2[1]);
  			alpha2[0] = user_alpha2[0];
  			alpha2[1] = user_alpha2[1];
  		}
  	} else if (!is_world_regdom(ieee80211_regdom)) {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1636
  		REG_DBG_PRINT("Keeping preference on "
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1637
1638
1639
1640
1641
1642
1643
  		       "module parameter ieee80211_regdom: %c%c
  ",
  		       ieee80211_regdom[0],
  		       ieee80211_regdom[1]);
  		alpha2[0] = ieee80211_regdom[0];
  		alpha2[1] = ieee80211_regdom[1];
  	} else
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1644
1645
  		REG_DBG_PRINT("Restoring regulatory settings
  ");
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
  }
  
  /*
   * 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];
  	struct reg_beacon *reg_beacon, *btmp;
  
  	mutex_lock(&cfg80211_mutex);
  	mutex_lock(&reg_mutex);
  
  	reset_regdomains();
  	restore_alpha2(alpha2, reset_user);
  
  	/* Clear beacon hints */
  	spin_lock_bh(&reg_pending_beacons_lock);
  	if (!list_empty(&reg_pending_beacons)) {
  		list_for_each_entry_safe(reg_beacon, btmp,
  					 &reg_pending_beacons, list) {
  			list_del(&reg_beacon->list);
  			kfree(reg_beacon);
  		}
  	}
  	spin_unlock_bh(&reg_pending_beacons_lock);
  
  	if (!list_empty(&reg_beacon_list)) {
  		list_for_each_entry_safe(reg_beacon, btmp,
  					 &reg_beacon_list, list) {
  			list_del(&reg_beacon->list);
  			kfree(reg_beacon);
  		}
  	}
  
  	/* First restore to the basic regulatory settings */
  	cfg80211_regdomain = cfg80211_world_regdom;
  
  	mutex_unlock(&reg_mutex);
  	mutex_unlock(&cfg80211_mutex);
  
  	regulatory_hint_core(cfg80211_regdomain->alpha2);
  
  	/*
  	 * 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))
  		regulatory_hint_user(user_alpha2);
  }
  
  
  void regulatory_hint_disconnect(void)
  {
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1713
  	REG_DBG_PRINT("All devices are disconnected, going to "
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
1714
1715
1716
1717
  		      "restore regulatory settings
  ");
  	restore_regulatory_settings(false);
  }
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1718
1719
  static bool freq_is_chan_12_13_14(u16 freq)
  {
59eb21a65   Bruno Randolf   cfg80211: Extend ...
1720
1721
1722
  	if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) ||
  	    freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) ||
  	    freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ))
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
  		return true;
  	return false;
  }
  
  int regulatory_hint_found_beacon(struct wiphy *wiphy,
  				 struct ieee80211_channel *beacon_chan,
  				 gfp_t gfp)
  {
  	struct reg_beacon *reg_beacon;
  
  	if (likely((beacon_chan->beacon_found ||
  	    (beacon_chan->flags & IEEE80211_CHAN_RADAR) ||
  	    (beacon_chan->band == IEEE80211_BAND_2GHZ &&
  	     !freq_is_chan_12_13_14(beacon_chan->center_freq)))))
  		return 0;
  
  	reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp);
  	if (!reg_beacon)
  		return -ENOMEM;
d91e41b69   Luis R. Rodriguez   cfg80211: prefix ...
1742
  	REG_DBG_PRINT("Found new beacon on "
4113f7518   Luis R. Rodriguez   cfg80211: add a r...
1743
1744
1745
1746
1747
  		      "frequency: %d MHz (Ch %d) on %s
  ",
  		      beacon_chan->center_freq,
  		      ieee80211_frequency_to_channel(beacon_chan->center_freq),
  		      wiphy_name(wiphy));
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
  	memcpy(&reg_beacon->chan, beacon_chan,
  		sizeof(struct ieee80211_channel));
  
  
  	/*
  	 * 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...
1764
  static void print_rd_rules(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1765
1766
  {
  	unsigned int i;
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
1767
1768
1769
  	const struct ieee80211_reg_rule *reg_rule = NULL;
  	const struct ieee80211_freq_range *freq_range = NULL;
  	const struct ieee80211_power_rule *power_rule = NULL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1770

e9c0268f0   Joe Perches   net/wireless: Use...
1771
1772
  	pr_info("    (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)
  ");
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1773
1774
1775
1776
1777
  
  	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;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1778
1779
1780
1781
  		/*
  		 * There may not be documentation for max antenna gain
  		 * in certain regions
  		 */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1782
  		if (power_rule->max_antenna_gain)
e9c0268f0   Joe Perches   net/wireless: Use...
1783
1784
  			pr_info("    (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)
  ",
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1785
1786
1787
1788
1789
1790
  				freq_range->start_freq_khz,
  				freq_range->end_freq_khz,
  				freq_range->max_bandwidth_khz,
  				power_rule->max_antenna_gain,
  				power_rule->max_eirp);
  		else
e9c0268f0   Joe Perches   net/wireless: Use...
1791
1792
  			pr_info("    (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)
  ",
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1793
1794
1795
1796
1797
1798
  				freq_range->start_freq_khz,
  				freq_range->end_freq_khz,
  				freq_range->max_bandwidth_khz,
  				power_rule->max_eirp);
  	}
  }
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
1799
  static void print_regdomain(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1800
  {
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1801
  	if (is_intersected_alpha2(rd->alpha2)) {
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1802

7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1803
1804
  		if (last_request->initiator ==
  		    NL80211_REGDOM_SET_BY_COUNTRY_IE) {
79c97e97a   Johannes Berg   cfg80211: clean u...
1805
1806
  			struct cfg80211_registered_device *rdev;
  			rdev = cfg80211_rdev_by_wiphy_idx(
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1807
  				last_request->wiphy_idx);
79c97e97a   Johannes Berg   cfg80211: clean u...
1808
  			if (rdev) {
e9c0268f0   Joe Perches   net/wireless: Use...
1809
1810
  				pr_info("Current regulatory domain updated by AP to: %c%c
  ",
79c97e97a   Johannes Berg   cfg80211: clean u...
1811
1812
  					rdev->country_ie_alpha2[0],
  					rdev->country_ie_alpha2[1]);
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1813
  			} else
e9c0268f0   Joe Perches   net/wireless: Use...
1814
1815
  				pr_info("Current regulatory domain intersected:
  ");
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1816
  		} else
e9c0268f0   Joe Perches   net/wireless: Use...
1817
1818
  			pr_info("Current regulatory domain intersected:
  ");
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1819
  	} else if (is_world_regdom(rd->alpha2))
e9c0268f0   Joe Perches   net/wireless: Use...
1820
1821
  		pr_info("World regulatory domain updated:
  ");
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1822
1823
  	else {
  		if (is_unknown_alpha2(rd->alpha2))
e9c0268f0   Joe Perches   net/wireless: Use...
1824
1825
  			pr_info("Regulatory domain changed to driver built-in settings (unknown country)
  ");
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1826
  		else
e9c0268f0   Joe Perches   net/wireless: Use...
1827
1828
  			pr_info("Regulatory domain changed to country: %c%c
  ",
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1829
1830
1831
1832
  				rd->alpha2[0], rd->alpha2[1]);
  	}
  	print_rd_rules(rd);
  }
2df78167a   Johannes Berg   wireless: fix a f...
1833
  static void print_regdomain_info(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1834
  {
e9c0268f0   Joe Perches   net/wireless: Use...
1835
1836
  	pr_info("Regulatory domain: %c%c
  ", rd->alpha2[0], rd->alpha2[1]);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1837
1838
  	print_rd_rules(rd);
  }
d2372b315   Johannes Berg   wireless: make re...
1839
  /* Takes ownership of rd only if it doesn't fail */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
1840
  static int __set_regdom(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1841
  {
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1842
  	const struct ieee80211_regdomain *intersected_rd = NULL;
79c97e97a   Johannes Berg   cfg80211: clean u...
1843
  	struct cfg80211_registered_device *rdev = NULL;
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1844
  	struct wiphy *request_wiphy;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1845
  	/* Some basic sanity checks first */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1846
  	if (is_world_regdom(rd->alpha2)) {
f6037d09e   Johannes Berg   wireless: get rid...
1847
  		if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1848
1849
1850
1851
  			return -EINVAL;
  		update_world_regdomain(rd);
  		return 0;
  	}
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1852
1853
1854
1855
  
  	if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
  			!is_unknown_alpha2(rd->alpha2))
  		return -EINVAL;
f6037d09e   Johannes Berg   wireless: get rid...
1856
  	if (!last_request)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1857
  		return -EINVAL;
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1858
1859
  	/*
  	 * Lets only bother proceeding on the same alpha2 if the current
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1860
  	 * rd is non static (it means CRDA was present and was used last)
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1861
1862
  	 * and the pending request came in from a country IE
  	 */
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1863
  	if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1864
1865
1866
1867
  		/*
  		 * If someone else asked us to change the rd lets only bother
  		 * checking if the alpha2 changes if CRDA was already called
  		 */
baeb66fe2   John W. Linville   wireless: remove ...
1868
  		if (!regdom_changes(rd->alpha2))
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1869
1870
  			return -EINVAL;
  	}
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1871
1872
  	/*
  	 * Now lets set the regulatory domain, update all driver channels
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1873
1874
  	 * and finally inform them of what we have done, in case they want
  	 * to review or adjust their own settings based on their own
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1875
1876
  	 * internal EEPROM data
  	 */
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1877

f6037d09e   Johannes Berg   wireless: get rid...
1878
  	if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1879
  		return -EINVAL;
8375af3ba   Luis R. Rodriguez   cfg80211: remove ...
1880
  	if (!is_valid_rd(rd)) {
e9c0268f0   Joe Perches   net/wireless: Use...
1881
1882
  		pr_err("Invalid regulatory domain detected:
  ");
8375af3ba   Luis R. Rodriguez   cfg80211: remove ...
1883
1884
  		print_regdomain_info(rd);
  		return -EINVAL;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1885
  	}
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1886
  	request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
1887
  	if (!last_request->intersect) {
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1888
  		int r;
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1889
  		if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1890
1891
1892
1893
  			reset_regdomains();
  			cfg80211_regdomain = rd;
  			return 0;
  		}
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1894
1895
1896
1897
  		/*
  		 * For a driver hint, lets copy the regulatory domain the
  		 * driver wanted to the wiphy to deal with conflicts
  		 */
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1898

558f6d322   Luis R. Rodriguez   cfg80211: fix for...
1899
1900
1901
1902
1903
1904
  		/*
  		 * Userspace could have sent two replies with only
  		 * one kernel request.
  		 */
  		if (request_wiphy->regd)
  			return -EALREADY;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1905

806a9e396   Luis R. Rodriguez   cfg80211: make re...
1906
  		r = reg_copy_regd(&request_wiphy->regd, rd);
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1907
1908
  		if (r)
  			return r;
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
1909
1910
1911
1912
1913
1914
  		reset_regdomains();
  		cfg80211_regdomain = rd;
  		return 0;
  	}
  
  	/* Intersection requires a bit more work */
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1915
  	if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
1916

9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1917
1918
1919
  		intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
  		if (!intersected_rd)
  			return -EINVAL;
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
1920

fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1921
1922
  		/*
  		 * We can trash what CRDA provided now.
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1923
  		 * However if a driver requested this specific regulatory
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1924
1925
  		 * domain we keep it for its private use
  		 */
7db90f4a2   Luis R. Rodriguez   cfg80211: move en...
1926
  		if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER)
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1927
  			request_wiphy->regd = rd;
3e0c3ff36   Luis R. Rodriguez   cfg80211: allow m...
1928
1929
  		else
  			kfree(rd);
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
1930
1931
1932
1933
1934
1935
  		rd = NULL;
  
  		reset_regdomains();
  		cfg80211_regdomain = intersected_rd;
  
  		return 0;
9c96477d1   Luis R. Rodriguez   cfg80211: Add reg...
1936
  	}
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1937
1938
  	if (!intersected_rd)
  		return -EINVAL;
79c97e97a   Johannes Berg   cfg80211: clean u...
1939
  	rdev = wiphy_to_dev(request_wiphy);
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1940

79c97e97a   Johannes Berg   cfg80211: clean u...
1941
1942
1943
  	rdev->country_ie_alpha2[0] = rd->alpha2[0];
  	rdev->country_ie_alpha2[1] = rd->alpha2[1];
  	rdev->env = last_request->country_ie_env;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1944
1945
1946
1947
1948
  
  	BUG_ON(intersected_rd == rd);
  
  	kfree(rd);
  	rd = NULL;
b8295acdc   Luis R. Rodriguez   cfg80211: separat...
1949
  	reset_regdomains();
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1950
  	cfg80211_regdomain = intersected_rd;
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1951
1952
1953
  
  	return 0;
  }
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1954
1955
  /*
   * Use this call to set the current regulatory domain. Conflicts with
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1956
   * multiple drivers can be ironed out later. Caller must've already
fb1fc7add   Luis R. Rodriguez   cfg80211: comment...
1957
1958
   * kmalloc'd the rd structure. Caller must hold cfg80211_mutex
   */
a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
1959
  int set_regdom(const struct ieee80211_regdomain *rd)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1960
  {
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1961
  	int r;
761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
1962
  	assert_cfg80211_lock();
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1963
  	mutex_lock(&reg_mutex);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1964
1965
  	/* Note that this doesn't update the wiphys, this is done below */
  	r = __set_regdom(rd);
d2372b315   Johannes Berg   wireless: make re...
1966
1967
  	if (r) {
  		kfree(rd);
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1968
  		mutex_unlock(&reg_mutex);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1969
  		return r;
d2372b315   Johannes Berg   wireless: make re...
1970
  	}
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1971

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1972
  	/* This would make this whole thing pointless */
a01ddafd4   Luis R. Rodriguez   cfg80211: expect ...
1973
1974
  	if (!last_request->intersect)
  		BUG_ON(rd != cfg80211_regdomain);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1975
1976
  
  	/* update all wiphys now with the new established regulatory domain */
f6037d09e   Johannes Berg   wireless: get rid...
1977
  	update_all_wiphy_regulatory(last_request->initiator);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1978

a01ddafd4   Luis R. Rodriguez   cfg80211: expect ...
1979
  	print_regdomain(cfg80211_regdomain);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1980

73d54c9e7   Luis R. Rodriguez   cfg80211: add reg...
1981
  	nl80211_send_reg_change_event(last_request);
b2e253cf3   Luis R. Rodriguez   cfg80211: Fix reg...
1982
  	reg_set_request_processed();
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1983
  	mutex_unlock(&reg_mutex);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
1984
1985
  	return r;
  }
a1794390f   Luis R. Rodriguez   cfg80211: rename ...
1986
  /* Caller must hold cfg80211_mutex */
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
1987
1988
  void reg_device_remove(struct wiphy *wiphy)
  {
0ad8acaf4   Luis R. Rodriguez   cfg80211: fix NUL...
1989
  	struct wiphy *request_wiphy = NULL;
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1990

761cf7ecf   Luis R. Rodriguez   cfg80211: add ass...
1991
  	assert_cfg80211_lock();
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1992
  	mutex_lock(&reg_mutex);
0ef9ccdd9   Chris Wright   cfg80211: remove ...
1993
  	kfree(wiphy->regd);
0ad8acaf4   Luis R. Rodriguez   cfg80211: fix NUL...
1994
1995
  	if (last_request)
  		request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
806a9e396   Luis R. Rodriguez   cfg80211: make re...
1996

0ef9ccdd9   Chris Wright   cfg80211: remove ...
1997
  	if (!request_wiphy || request_wiphy != wiphy)
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
1998
  		goto out;
0ef9ccdd9   Chris Wright   cfg80211: remove ...
1999

806a9e396   Luis R. Rodriguez   cfg80211: make re...
2000
  	last_request->wiphy_idx = WIPHY_IDX_STALE;
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2001
  	last_request->country_ie_env = ENVIRON_ANY;
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
2002
2003
  out:
  	mutex_unlock(&reg_mutex);
3f2355cb9   Luis R. Rodriguez   cfg80211/mac80211...
2004
  }
2fcc9f731   Uwe Kleine-König   wireless: move re...
2005
  int __init regulatory_init(void)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2006
  {
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
2007
  	int err = 0;
734366dea   Johannes Berg   cfg80211: clean u...
2008

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2009
2010
2011
  	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...
2012

fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2013
  	spin_lock_init(&reg_requests_lock);
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2014
  	spin_lock_init(&reg_pending_beacons_lock);
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2015

a3d2eaf0d   Johannes Berg   cfg80211: fix reg...
2016
  	cfg80211_regdomain = cfg80211_world_regdom;
734366dea   Johannes Berg   cfg80211: clean u...
2017

09d989d17   Luis R. Rodriguez   cfg80211: add reg...
2018
2019
  	user_alpha2[0] = '9';
  	user_alpha2[1] = '7';
ae9e4b0d1   Luis R. Rodriguez   cfg80211: treat i...
2020
2021
  	/* We always try to get an update for the static regdomain */
  	err = regulatory_hint_core(cfg80211_regdomain->alpha2);
ba25c1414   Luis R. Rodriguez   cfg80211: add reg...
2022
  	if (err) {
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
2023
2024
2025
2026
2027
2028
2029
2030
2031
  		if (err == -ENOMEM)
  			return err;
  		/*
  		 * 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...
2032
2033
  		pr_err("kobject_uevent_env() was unable to call CRDA during init
  ");
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
2034
2035
2036
  #ifdef CONFIG_CFG80211_REG_DEBUG
  		/* We want to find out exactly why when debugging */
  		WARN_ON(err);
734366dea   Johannes Berg   cfg80211: clean u...
2037
  #endif
bcf4f99b7   Luis R. Rodriguez   cfg80211: propaga...
2038
  	}
734366dea   Johannes Berg   cfg80211: clean u...
2039

ae9e4b0d1   Luis R. Rodriguez   cfg80211: treat i...
2040
2041
2042
2043
2044
2045
  	/*
  	 * Finally, if the user set the module parameter treat it
  	 * as a user hint.
  	 */
  	if (!is_world_regdom(ieee80211_regdom))
  		regulatory_hint_user(ieee80211_regdom);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2046
2047
  	return 0;
  }
2fcc9f731   Uwe Kleine-König   wireless: move re...
2048
  void /* __init_or_exit */ regulatory_exit(void)
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2049
  {
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2050
  	struct regulatory_request *reg_request, *tmp;
e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2051
  	struct reg_beacon *reg_beacon, *btmp;
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2052
2053
  
  	cancel_work_sync(&reg_work);
a1794390f   Luis R. Rodriguez   cfg80211: rename ...
2054
  	mutex_lock(&cfg80211_mutex);
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
2055
  	mutex_lock(&reg_mutex);
734366dea   Johannes Berg   cfg80211: clean u...
2056

b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2057
  	reset_regdomains();
734366dea   Johannes Berg   cfg80211: clean u...
2058

f6037d09e   Johannes Berg   wireless: get rid...
2059
  	kfree(last_request);
b2e1b3029   Luis R. Rodriguez   cfg80211: Add new...
2060
  	platform_device_unregister(reg_pdev);
734366dea   Johannes Berg   cfg80211: clean u...
2061

e38f8a7a8   Luis R. Rodriguez   cfg80211: Add AP ...
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
  	spin_lock_bh(&reg_pending_beacons_lock);
  	if (!list_empty(&reg_pending_beacons)) {
  		list_for_each_entry_safe(reg_beacon, btmp,
  					 &reg_pending_beacons, list) {
  			list_del(&reg_beacon->list);
  			kfree(reg_beacon);
  		}
  	}
  	spin_unlock_bh(&reg_pending_beacons_lock);
  
  	if (!list_empty(&reg_beacon_list)) {
  		list_for_each_entry_safe(reg_beacon, btmp,
  					 &reg_beacon_list, list) {
  			list_del(&reg_beacon->list);
  			kfree(reg_beacon);
  		}
  	}
fe33eb390   Luis R. Rodriguez   cfg80211: move al...
2079
2080
2081
2082
2083
2084
2085
2086
2087
  	spin_lock(&reg_requests_lock);
  	if (!list_empty(&reg_requests_list)) {
  		list_for_each_entry_safe(reg_request, tmp,
  					 &reg_requests_list, list) {
  			list_del(&reg_request->list);
  			kfree(reg_request);
  		}
  	}
  	spin_unlock(&reg_requests_lock);
abc7381bc   Luis R. Rodriguez   cfg80211: decoupl...
2088
  	mutex_unlock(&reg_mutex);
a1794390f   Luis R. Rodriguez   cfg80211: rename ...
2089
  	mutex_unlock(&cfg80211_mutex);
8318d78a4   Johannes Berg   cfg80211 API for ...
2090
  }