diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-02-18 13:47:13 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-02-18 13:47:13 -0500 |
commit | 98d5fac2330779e6eea6431a90b44c7476260dcc (patch) | |
tree | 99870656d835fc6c12093bc67517956cc7b3d6ec /net/wireless | |
parent | 4153577a8d318ae02b3791341e10e78416de402f (diff) | |
parent | 9e97d14b4923da524d202f2e005d5d30b70db9d6 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Conflicts:
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/spi.c
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/chan.c | 142 | ||||
-rw-r--r-- | net/wireless/core.c | 8 | ||||
-rw-r--r-- | net/wireless/core.h | 35 | ||||
-rw-r--r-- | net/wireless/ibss.c | 4 | ||||
-rw-r--r-- | net/wireless/mlme.c | 136 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 783 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 7 | ||||
-rw-r--r-- | net/wireless/reg.c | 20 | ||||
-rw-r--r-- | net/wireless/scan.c | 630 | ||||
-rw-r--r-- | net/wireless/sme.c | 16 | ||||
-rw-r--r-- | net/wireless/sysfs.c | 2 | ||||
-rw-r--r-- | net/wireless/trace.h | 80 | ||||
-rw-r--r-- | net/wireless/util.c | 2 |
13 files changed, 1486 insertions, 379 deletions
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 396373f3ec26..fd556ac05fdb 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -147,6 +147,32 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c, | |||
147 | } | 147 | } |
148 | } | 148 | } |
149 | 149 | ||
150 | static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c) | ||
151 | { | ||
152 | int width; | ||
153 | |||
154 | switch (c->width) { | ||
155 | case NL80211_CHAN_WIDTH_20: | ||
156 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
157 | width = 20; | ||
158 | break; | ||
159 | case NL80211_CHAN_WIDTH_40: | ||
160 | width = 40; | ||
161 | break; | ||
162 | case NL80211_CHAN_WIDTH_80P80: | ||
163 | case NL80211_CHAN_WIDTH_80: | ||
164 | width = 80; | ||
165 | break; | ||
166 | case NL80211_CHAN_WIDTH_160: | ||
167 | width = 160; | ||
168 | break; | ||
169 | default: | ||
170 | WARN_ON_ONCE(1); | ||
171 | return -1; | ||
172 | } | ||
173 | return width; | ||
174 | } | ||
175 | |||
150 | const struct cfg80211_chan_def * | 176 | const struct cfg80211_chan_def * |
151 | cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, | 177 | cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, |
152 | const struct cfg80211_chan_def *c2) | 178 | const struct cfg80211_chan_def *c2) |
@@ -192,6 +218,93 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, | |||
192 | } | 218 | } |
193 | EXPORT_SYMBOL(cfg80211_chandef_compatible); | 219 | EXPORT_SYMBOL(cfg80211_chandef_compatible); |
194 | 220 | ||
221 | static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq, | ||
222 | u32 bandwidth, | ||
223 | enum nl80211_dfs_state dfs_state) | ||
224 | { | ||
225 | struct ieee80211_channel *c; | ||
226 | u32 freq; | ||
227 | |||
228 | for (freq = center_freq - bandwidth/2 + 10; | ||
229 | freq <= center_freq + bandwidth/2 - 10; | ||
230 | freq += 20) { | ||
231 | c = ieee80211_get_channel(wiphy, freq); | ||
232 | if (!c || !(c->flags & IEEE80211_CHAN_RADAR)) | ||
233 | continue; | ||
234 | |||
235 | c->dfs_state = dfs_state; | ||
236 | c->dfs_state_entered = jiffies; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | void cfg80211_set_dfs_state(struct wiphy *wiphy, | ||
241 | const struct cfg80211_chan_def *chandef, | ||
242 | enum nl80211_dfs_state dfs_state) | ||
243 | { | ||
244 | int width; | ||
245 | |||
246 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) | ||
247 | return; | ||
248 | |||
249 | width = cfg80211_chandef_get_width(chandef); | ||
250 | if (width < 0) | ||
251 | return; | ||
252 | |||
253 | cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1, | ||
254 | width, dfs_state); | ||
255 | |||
256 | if (!chandef->center_freq2) | ||
257 | return; | ||
258 | cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2, | ||
259 | width, dfs_state); | ||
260 | } | ||
261 | |||
262 | static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy, | ||
263 | u32 center_freq, | ||
264 | u32 bandwidth) | ||
265 | { | ||
266 | struct ieee80211_channel *c; | ||
267 | u32 freq; | ||
268 | |||
269 | for (freq = center_freq - bandwidth/2 + 10; | ||
270 | freq <= center_freq + bandwidth/2 - 10; | ||
271 | freq += 20) { | ||
272 | c = ieee80211_get_channel(wiphy, freq); | ||
273 | if (!c) | ||
274 | return -EINVAL; | ||
275 | |||
276 | if (c->flags & IEEE80211_CHAN_RADAR) | ||
277 | return 1; | ||
278 | } | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | |||
283 | int cfg80211_chandef_dfs_required(struct wiphy *wiphy, | ||
284 | const struct cfg80211_chan_def *chandef) | ||
285 | { | ||
286 | int width; | ||
287 | int r; | ||
288 | |||
289 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) | ||
290 | return -EINVAL; | ||
291 | |||
292 | width = cfg80211_chandef_get_width(chandef); | ||
293 | if (width < 0) | ||
294 | return -EINVAL; | ||
295 | |||
296 | r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1, | ||
297 | width); | ||
298 | if (r) | ||
299 | return r; | ||
300 | |||
301 | if (!chandef->center_freq2) | ||
302 | return 0; | ||
303 | |||
304 | return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, | ||
305 | width); | ||
306 | } | ||
307 | |||
195 | static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, | 308 | static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, |
196 | u32 center_freq, u32 bandwidth, | 309 | u32 center_freq, u32 bandwidth, |
197 | u32 prohibited_flags) | 310 | u32 prohibited_flags) |
@@ -203,7 +316,16 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, | |||
203 | freq <= center_freq + bandwidth/2 - 10; | 316 | freq <= center_freq + bandwidth/2 - 10; |
204 | freq += 20) { | 317 | freq += 20) { |
205 | c = ieee80211_get_channel(wiphy, freq); | 318 | c = ieee80211_get_channel(wiphy, freq); |
206 | if (!c || c->flags & prohibited_flags) | 319 | if (!c) |
320 | return false; | ||
321 | |||
322 | /* check for radar flags */ | ||
323 | if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) && | ||
324 | (c->dfs_state != NL80211_DFS_AVAILABLE)) | ||
325 | return false; | ||
326 | |||
327 | /* check for the other flags */ | ||
328 | if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR) | ||
207 | return false; | 329 | return false; |
208 | } | 330 | } |
209 | 331 | ||
@@ -253,6 +375,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, | |||
253 | case NL80211_CHAN_WIDTH_80: | 375 | case NL80211_CHAN_WIDTH_80: |
254 | if (!vht_cap->vht_supported) | 376 | if (!vht_cap->vht_supported) |
255 | return false; | 377 | return false; |
378 | prohibited_flags |= IEEE80211_CHAN_NO_80MHZ; | ||
256 | width = 80; | 379 | width = 80; |
257 | break; | 380 | break; |
258 | case NL80211_CHAN_WIDTH_160: | 381 | case NL80211_CHAN_WIDTH_160: |
@@ -260,6 +383,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, | |||
260 | return false; | 383 | return false; |
261 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) | 384 | if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) |
262 | return false; | 385 | return false; |
386 | prohibited_flags |= IEEE80211_CHAN_NO_160MHZ; | ||
263 | width = 160; | 387 | width = 160; |
264 | break; | 388 | break; |
265 | default: | 389 | default: |
@@ -267,7 +391,16 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, | |||
267 | return false; | 391 | return false; |
268 | } | 392 | } |
269 | 393 | ||
270 | /* TODO: missing regulatory check on 80/160 bandwidth */ | 394 | /* |
395 | * TODO: What if there are only certain 80/160/80+80 MHz channels | ||
396 | * allowed by the driver, or only certain combinations? | ||
397 | * For 40 MHz the driver can set the NO_HT40 flags, but for | ||
398 | * 80/160 MHz and in particular 80+80 MHz this isn't really | ||
399 | * feasible and we only have NO_80MHZ/NO_160MHZ so far but | ||
400 | * no way to cover 80+80 MHz or more complex restrictions. | ||
401 | * Note that such restrictions also need to be advertised to | ||
402 | * userspace, for example for P2P channel selection. | ||
403 | */ | ||
271 | 404 | ||
272 | if (width > 20) | 405 | if (width > 20) |
273 | prohibited_flags |= IEEE80211_CHAN_NO_OFDM; | 406 | prohibited_flags |= IEEE80211_CHAN_NO_OFDM; |
@@ -344,7 +477,10 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, | |||
344 | break; | 477 | break; |
345 | case NL80211_IFTYPE_AP: | 478 | case NL80211_IFTYPE_AP: |
346 | case NL80211_IFTYPE_P2P_GO: | 479 | case NL80211_IFTYPE_P2P_GO: |
347 | if (wdev->beacon_interval) { | 480 | if (wdev->cac_started) { |
481 | *chan = wdev->channel; | ||
482 | *chanmode = CHAN_MODE_SHARED; | ||
483 | } else if (wdev->beacon_interval) { | ||
348 | *chan = wdev->channel; | 484 | *chan = wdev->channel; |
349 | *chanmode = CHAN_MODE_SHARED; | 485 | *chanmode = CHAN_MODE_SHARED; |
350 | } | 486 | } |
diff --git a/net/wireless/core.c b/net/wireless/core.c index 40dbe37cfbf6..5ffff039b017 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -324,6 +324,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
324 | INIT_LIST_HEAD(&rdev->bss_list); | 324 | INIT_LIST_HEAD(&rdev->bss_list); |
325 | INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); | 325 | INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); |
326 | INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); | 326 | INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); |
327 | INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk, | ||
328 | cfg80211_dfs_channels_update_work); | ||
327 | #ifdef CONFIG_CFG80211_WEXT | 329 | #ifdef CONFIG_CFG80211_WEXT |
328 | rdev->wiphy.wext = &cfg80211_wext_handler; | 330 | rdev->wiphy.wext = &cfg80211_wext_handler; |
329 | #endif | 331 | #endif |
@@ -365,7 +367,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
365 | rdev->wiphy.rts_threshold = (u32) -1; | 367 | rdev->wiphy.rts_threshold = (u32) -1; |
366 | rdev->wiphy.coverage_class = 0; | 368 | rdev->wiphy.coverage_class = 0; |
367 | 369 | ||
368 | rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH; | 370 | rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH | |
371 | NL80211_FEATURE_ADVERTISE_CHAN_LIMITS; | ||
369 | 372 | ||
370 | return &rdev->wiphy; | 373 | return &rdev->wiphy; |
371 | } | 374 | } |
@@ -695,6 +698,7 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
695 | flush_work(&rdev->scan_done_wk); | 698 | flush_work(&rdev->scan_done_wk); |
696 | cancel_work_sync(&rdev->conn_work); | 699 | cancel_work_sync(&rdev->conn_work); |
697 | flush_work(&rdev->event_work); | 700 | flush_work(&rdev->event_work); |
701 | cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); | ||
698 | 702 | ||
699 | if (rdev->wowlan && rdev->ops->set_wakeup) | 703 | if (rdev->wowlan && rdev->ops->set_wakeup) |
700 | rdev_set_wakeup(rdev, false); | 704 | rdev_set_wakeup(rdev, false); |
@@ -715,7 +719,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev) | |||
715 | kfree(reg); | 719 | kfree(reg); |
716 | } | 720 | } |
717 | list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) | 721 | list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) |
718 | cfg80211_put_bss(&scan->pub); | 722 | cfg80211_put_bss(&rdev->wiphy, &scan->pub); |
719 | kfree(rdev); | 723 | kfree(rdev); |
720 | } | 724 | } |
721 | 725 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index 8396f7671c8d..3aec0e429d8a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -8,7 +8,6 @@ | |||
8 | #include <linux/mutex.h> | 8 | #include <linux/mutex.h> |
9 | #include <linux/list.h> | 9 | #include <linux/list.h> |
10 | #include <linux/netdevice.h> | 10 | #include <linux/netdevice.h> |
11 | #include <linux/kref.h> | ||
12 | #include <linux/rbtree.h> | 11 | #include <linux/rbtree.h> |
13 | #include <linux/debugfs.h> | 12 | #include <linux/debugfs.h> |
14 | #include <linux/rfkill.h> | 13 | #include <linux/rfkill.h> |
@@ -87,6 +86,8 @@ struct cfg80211_registered_device { | |||
87 | 86 | ||
88 | struct cfg80211_wowlan *wowlan; | 87 | struct cfg80211_wowlan *wowlan; |
89 | 88 | ||
89 | struct delayed_work dfs_update_channels_wk; | ||
90 | |||
90 | /* must be last because of the way we do wiphy_priv(), | 91 | /* must be last because of the way we do wiphy_priv(), |
91 | * and it should at least be aligned to NETDEV_ALIGN */ | 92 | * and it should at least be aligned to NETDEV_ALIGN */ |
92 | struct wiphy wiphy __aligned(NETDEV_ALIGN); | 93 | struct wiphy wiphy __aligned(NETDEV_ALIGN); |
@@ -109,6 +110,9 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) | |||
109 | for (i = 0; i < rdev->wowlan->n_patterns; i++) | 110 | for (i = 0; i < rdev->wowlan->n_patterns; i++) |
110 | kfree(rdev->wowlan->patterns[i].mask); | 111 | kfree(rdev->wowlan->patterns[i].mask); |
111 | kfree(rdev->wowlan->patterns); | 112 | kfree(rdev->wowlan->patterns); |
113 | if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock) | ||
114 | sock_release(rdev->wowlan->tcp->sock); | ||
115 | kfree(rdev->wowlan->tcp); | ||
112 | kfree(rdev->wowlan); | 116 | kfree(rdev->wowlan); |
113 | } | 117 | } |
114 | 118 | ||
@@ -124,9 +128,10 @@ static inline void assert_cfg80211_lock(void) | |||
124 | 128 | ||
125 | struct cfg80211_internal_bss { | 129 | struct cfg80211_internal_bss { |
126 | struct list_head list; | 130 | struct list_head list; |
131 | struct list_head hidden_list; | ||
127 | struct rb_node rbn; | 132 | struct rb_node rbn; |
128 | unsigned long ts; | 133 | unsigned long ts; |
129 | struct kref ref; | 134 | unsigned long refcount; |
130 | atomic_t hold; | 135 | atomic_t hold; |
131 | 136 | ||
132 | /* must be last because of priv member */ | 137 | /* must be last because of priv member */ |
@@ -428,6 +433,22 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
428 | enum cfg80211_chan_mode chanmode, | 433 | enum cfg80211_chan_mode chanmode, |
429 | u8 radar_detect); | 434 | u8 radar_detect); |
430 | 435 | ||
436 | /** | ||
437 | * cfg80211_chandef_dfs_required - checks if radar detection is required | ||
438 | * @wiphy: the wiphy to validate against | ||
439 | * @chandef: the channel definition to check | ||
440 | * Return: 1 if radar detection is required, 0 if it is not, < 0 on error | ||
441 | */ | ||
442 | int cfg80211_chandef_dfs_required(struct wiphy *wiphy, | ||
443 | const struct cfg80211_chan_def *c); | ||
444 | |||
445 | void cfg80211_set_dfs_state(struct wiphy *wiphy, | ||
446 | const struct cfg80211_chan_def *chandef, | ||
447 | enum nl80211_dfs_state dfs_state); | ||
448 | |||
449 | void cfg80211_dfs_channels_update_work(struct work_struct *work); | ||
450 | |||
451 | |||
431 | static inline int | 452 | static inline int |
432 | cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | 453 | cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, |
433 | struct wireless_dev *wdev, | 454 | struct wireless_dev *wdev, |
@@ -454,6 +475,16 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, | |||
454 | chan, chanmode, 0); | 475 | chan, chanmode, 0); |
455 | } | 476 | } |
456 | 477 | ||
478 | static inline unsigned int elapsed_jiffies_msecs(unsigned long start) | ||
479 | { | ||
480 | unsigned long end = jiffies; | ||
481 | |||
482 | if (end >= start) | ||
483 | return jiffies_to_msecs(end - start); | ||
484 | |||
485 | return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); | ||
486 | } | ||
487 | |||
457 | void | 488 | void |
458 | cfg80211_get_chan_state(struct wireless_dev *wdev, | 489 | cfg80211_get_chan_state(struct wireless_dev *wdev, |
459 | struct ieee80211_channel **chan, | 490 | struct ieee80211_channel **chan, |
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 9b9551e4a6f9..d80e47194d49 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c | |||
@@ -37,7 +37,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) | |||
37 | 37 | ||
38 | if (wdev->current_bss) { | 38 | if (wdev->current_bss) { |
39 | cfg80211_unhold_bss(wdev->current_bss); | 39 | cfg80211_unhold_bss(wdev->current_bss); |
40 | cfg80211_put_bss(&wdev->current_bss->pub); | 40 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
41 | } | 41 | } |
42 | 42 | ||
43 | cfg80211_hold_bss(bss_from_pub(bss)); | 43 | cfg80211_hold_bss(bss_from_pub(bss)); |
@@ -182,7 +182,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) | |||
182 | 182 | ||
183 | if (wdev->current_bss) { | 183 | if (wdev->current_bss) { |
184 | cfg80211_unhold_bss(wdev->current_bss); | 184 | cfg80211_unhold_bss(wdev->current_bss); |
185 | cfg80211_put_bss(&wdev->current_bss->pub); | 185 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
186 | } | 186 | } |
187 | 187 | ||
188 | wdev->current_bss = NULL; | 188 | wdev->current_bss = NULL; |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 461e692cdfec..caddca35d686 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -58,7 +58,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, | |||
58 | */ | 58 | */ |
59 | if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && | 59 | if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && |
60 | cfg80211_sme_failed_reassoc(wdev)) { | 60 | cfg80211_sme_failed_reassoc(wdev)) { |
61 | cfg80211_put_bss(bss); | 61 | cfg80211_put_bss(wiphy, bss); |
62 | goto out; | 62 | goto out; |
63 | } | 63 | } |
64 | 64 | ||
@@ -70,7 +70,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, | |||
70 | * do not call connect_result() now because the | 70 | * do not call connect_result() now because the |
71 | * sme will schedule work that does it later. | 71 | * sme will schedule work that does it later. |
72 | */ | 72 | */ |
73 | cfg80211_put_bss(bss); | 73 | cfg80211_put_bss(wiphy, bss); |
74 | goto out; | 74 | goto out; |
75 | } | 75 | } |
76 | 76 | ||
@@ -108,7 +108,7 @@ void __cfg80211_send_deauth(struct net_device *dev, | |||
108 | if (wdev->current_bss && | 108 | if (wdev->current_bss && |
109 | ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { | 109 | ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { |
110 | cfg80211_unhold_bss(wdev->current_bss); | 110 | cfg80211_unhold_bss(wdev->current_bss); |
111 | cfg80211_put_bss(&wdev->current_bss->pub); | 111 | cfg80211_put_bss(wiphy, &wdev->current_bss->pub); |
112 | wdev->current_bss = NULL; | 112 | wdev->current_bss = NULL; |
113 | was_current = true; | 113 | was_current = true; |
114 | } | 114 | } |
@@ -164,7 +164,7 @@ void __cfg80211_send_disassoc(struct net_device *dev, | |||
164 | ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { | 164 | ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) { |
165 | cfg80211_sme_disassoc(dev, wdev->current_bss); | 165 | cfg80211_sme_disassoc(dev, wdev->current_bss); |
166 | cfg80211_unhold_bss(wdev->current_bss); | 166 | cfg80211_unhold_bss(wdev->current_bss); |
167 | cfg80211_put_bss(&wdev->current_bss->pub); | 167 | cfg80211_put_bss(wiphy, &wdev->current_bss->pub); |
168 | wdev->current_bss = NULL; | 168 | wdev->current_bss = NULL; |
169 | } else | 169 | } else |
170 | WARN_ON(1); | 170 | WARN_ON(1); |
@@ -324,7 +324,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, | |||
324 | err = rdev_auth(rdev, dev, &req); | 324 | err = rdev_auth(rdev, dev, &req); |
325 | 325 | ||
326 | out: | 326 | out: |
327 | cfg80211_put_bss(req.bss); | 327 | cfg80211_put_bss(&rdev->wiphy, req.bss); |
328 | return err; | 328 | return err; |
329 | } | 329 | } |
330 | 330 | ||
@@ -432,7 +432,7 @@ out: | |||
432 | if (err) { | 432 | if (err) { |
433 | if (was_connected) | 433 | if (was_connected) |
434 | wdev->sme_state = CFG80211_SME_CONNECTED; | 434 | wdev->sme_state = CFG80211_SME_CONNECTED; |
435 | cfg80211_put_bss(req.bss); | 435 | cfg80211_put_bss(&rdev->wiphy, req.bss); |
436 | } | 436 | } |
437 | 437 | ||
438 | return err; | 438 | return err; |
@@ -514,7 +514,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, | |||
514 | if (wdev->sme_state != CFG80211_SME_CONNECTED) | 514 | if (wdev->sme_state != CFG80211_SME_CONNECTED) |
515 | return -ENOTCONN; | 515 | return -ENOTCONN; |
516 | 516 | ||
517 | if (WARN_ON(!wdev->current_bss)) | 517 | if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state)) |
518 | return -ENOTCONN; | 518 | return -ENOTCONN; |
519 | 519 | ||
520 | memset(&req, 0, sizeof(req)); | 520 | memset(&req, 0, sizeof(req)); |
@@ -572,7 +572,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | |||
572 | 572 | ||
573 | if (wdev->current_bss) { | 573 | if (wdev->current_bss) { |
574 | cfg80211_unhold_bss(wdev->current_bss); | 574 | cfg80211_unhold_bss(wdev->current_bss); |
575 | cfg80211_put_bss(&wdev->current_bss->pub); | 575 | cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub); |
576 | wdev->current_bss = NULL; | 576 | wdev->current_bss = NULL; |
577 | } | 577 | } |
578 | } | 578 | } |
@@ -987,3 +987,123 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, | |||
987 | nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); | 987 | nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); |
988 | } | 988 | } |
989 | EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); | 989 | EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); |
990 | |||
991 | void cfg80211_dfs_channels_update_work(struct work_struct *work) | ||
992 | { | ||
993 | struct delayed_work *delayed_work; | ||
994 | struct cfg80211_registered_device *rdev; | ||
995 | struct cfg80211_chan_def chandef; | ||
996 | struct ieee80211_supported_band *sband; | ||
997 | struct ieee80211_channel *c; | ||
998 | struct wiphy *wiphy; | ||
999 | bool check_again = false; | ||
1000 | unsigned long timeout, next_time = 0; | ||
1001 | int bandid, i; | ||
1002 | |||
1003 | delayed_work = container_of(work, struct delayed_work, work); | ||
1004 | rdev = container_of(delayed_work, struct cfg80211_registered_device, | ||
1005 | dfs_update_channels_wk); | ||
1006 | wiphy = &rdev->wiphy; | ||
1007 | |||
1008 | mutex_lock(&cfg80211_mutex); | ||
1009 | for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) { | ||
1010 | sband = wiphy->bands[bandid]; | ||
1011 | if (!sband) | ||
1012 | continue; | ||
1013 | |||
1014 | for (i = 0; i < sband->n_channels; i++) { | ||
1015 | c = &sband->channels[i]; | ||
1016 | |||
1017 | if (c->dfs_state != NL80211_DFS_UNAVAILABLE) | ||
1018 | continue; | ||
1019 | |||
1020 | timeout = c->dfs_state_entered + | ||
1021 | IEEE80211_DFS_MIN_NOP_TIME_MS; | ||
1022 | |||
1023 | if (time_after_eq(jiffies, timeout)) { | ||
1024 | c->dfs_state = NL80211_DFS_USABLE; | ||
1025 | cfg80211_chandef_create(&chandef, c, | ||
1026 | NL80211_CHAN_NO_HT); | ||
1027 | |||
1028 | nl80211_radar_notify(rdev, &chandef, | ||
1029 | NL80211_RADAR_NOP_FINISHED, | ||
1030 | NULL, GFP_ATOMIC); | ||
1031 | continue; | ||
1032 | } | ||
1033 | |||
1034 | if (!check_again) | ||
1035 | next_time = timeout - jiffies; | ||
1036 | else | ||
1037 | next_time = min(next_time, timeout - jiffies); | ||
1038 | check_again = true; | ||
1039 | } | ||
1040 | } | ||
1041 | mutex_unlock(&cfg80211_mutex); | ||
1042 | |||
1043 | /* reschedule if there are other channels waiting to be cleared again */ | ||
1044 | if (check_again) | ||
1045 | queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, | ||
1046 | next_time); | ||
1047 | } | ||
1048 | |||
1049 | |||
1050 | void cfg80211_radar_event(struct wiphy *wiphy, | ||
1051 | struct cfg80211_chan_def *chandef, | ||
1052 | gfp_t gfp) | ||
1053 | { | ||
1054 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
1055 | unsigned long timeout; | ||
1056 | |||
1057 | trace_cfg80211_radar_event(wiphy, chandef); | ||
1058 | |||
1059 | /* only set the chandef supplied channel to unavailable, in | ||
1060 | * case the radar is detected on only one of multiple channels | ||
1061 | * spanned by the chandef. | ||
1062 | */ | ||
1063 | cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); | ||
1064 | |||
1065 | timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS); | ||
1066 | queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, | ||
1067 | timeout); | ||
1068 | |||
1069 | nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); | ||
1070 | } | ||
1071 | EXPORT_SYMBOL(cfg80211_radar_event); | ||
1072 | |||
1073 | void cfg80211_cac_event(struct net_device *netdev, | ||
1074 | enum nl80211_radar_event event, gfp_t gfp) | ||
1075 | { | ||
1076 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | ||
1077 | struct wiphy *wiphy = wdev->wiphy; | ||
1078 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
1079 | struct cfg80211_chan_def chandef; | ||
1080 | unsigned long timeout; | ||
1081 | |||
1082 | trace_cfg80211_cac_event(netdev, event); | ||
1083 | |||
1084 | if (WARN_ON(!wdev->cac_started)) | ||
1085 | return; | ||
1086 | |||
1087 | if (WARN_ON(!wdev->channel)) | ||
1088 | return; | ||
1089 | |||
1090 | cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT); | ||
1091 | |||
1092 | switch (event) { | ||
1093 | case NL80211_RADAR_CAC_FINISHED: | ||
1094 | timeout = wdev->cac_start_time + | ||
1095 | msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS); | ||
1096 | WARN_ON(!time_after_eq(jiffies, timeout)); | ||
1097 | cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE); | ||
1098 | break; | ||
1099 | case NL80211_RADAR_CAC_ABORTED: | ||
1100 | break; | ||
1101 | default: | ||
1102 | WARN_ON(1); | ||
1103 | return; | ||
1104 | } | ||
1105 | wdev->cac_started = false; | ||
1106 | |||
1107 | nl80211_radar_notify(rdev, &chandef, event, netdev, gfp); | ||
1108 | } | ||
1109 | EXPORT_SYMBOL(cfg80211_cac_event); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b5978ab4ad7a..580ffeaef3d5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <net/genetlink.h> | 19 | #include <net/genetlink.h> |
20 | #include <net/cfg80211.h> | 20 | #include <net/cfg80211.h> |
21 | #include <net/sock.h> | 21 | #include <net/sock.h> |
22 | #include <net/inet_connection_sock.h> | ||
22 | #include "core.h" | 23 | #include "core.h" |
23 | #include "nl80211.h" | 24 | #include "nl80211.h" |
24 | #include "reg.h" | 25 | #include "reg.h" |
@@ -367,6 +368,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
367 | [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, | 368 | [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 }, |
368 | [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, | 369 | [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, |
369 | [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, | 370 | [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, |
371 | [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, | ||
372 | [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, }, | ||
370 | }; | 373 | }; |
371 | 374 | ||
372 | /* policy for the key attributes */ | 375 | /* policy for the key attributes */ |
@@ -399,6 +402,26 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { | |||
399 | [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, | 402 | [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, |
400 | [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, | 403 | [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, |
401 | [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, | 404 | [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, |
405 | [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, | ||
406 | }; | ||
407 | |||
408 | static const struct nla_policy | ||
409 | nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { | ||
410 | [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 }, | ||
411 | [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 }, | ||
412 | [NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN }, | ||
413 | [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 }, | ||
414 | [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 }, | ||
415 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 }, | ||
416 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = { | ||
417 | .len = sizeof(struct nl80211_wowlan_tcp_data_seq) | ||
418 | }, | ||
419 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = { | ||
420 | .len = sizeof(struct nl80211_wowlan_tcp_data_token) | ||
421 | }, | ||
422 | [NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 }, | ||
423 | [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 }, | ||
424 | [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, | ||
402 | }; | 425 | }; |
403 | 426 | ||
404 | /* policy for GTK rekey offload attributes */ | 427 | /* policy for GTK rekey offload attributes */ |
@@ -531,8 +554,27 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, | |||
531 | if ((chan->flags & IEEE80211_CHAN_NO_IBSS) && | 554 | if ((chan->flags & IEEE80211_CHAN_NO_IBSS) && |
532 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS)) | 555 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS)) |
533 | goto nla_put_failure; | 556 | goto nla_put_failure; |
534 | if ((chan->flags & IEEE80211_CHAN_RADAR) && | 557 | if (chan->flags & IEEE80211_CHAN_RADAR) { |
535 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR)) | 558 | u32 time = elapsed_jiffies_msecs(chan->dfs_state_entered); |
559 | if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR)) | ||
560 | goto nla_put_failure; | ||
561 | if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE, | ||
562 | chan->dfs_state)) | ||
563 | goto nla_put_failure; | ||
564 | if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time)) | ||
565 | goto nla_put_failure; | ||
566 | } | ||
567 | if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) && | ||
568 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS)) | ||
569 | goto nla_put_failure; | ||
570 | if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) && | ||
571 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS)) | ||
572 | goto nla_put_failure; | ||
573 | if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) && | ||
574 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ)) | ||
575 | goto nla_put_failure; | ||
576 | if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) && | ||
577 | nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ)) | ||
536 | goto nla_put_failure; | 578 | goto nla_put_failure; |
537 | 579 | ||
538 | if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, | 580 | if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, |
@@ -872,6 +914,48 @@ nla_put_failure: | |||
872 | return -ENOBUFS; | 914 | return -ENOBUFS; |
873 | } | 915 | } |
874 | 916 | ||
917 | #ifdef CONFIG_PM | ||
918 | static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev, | ||
919 | struct sk_buff *msg) | ||
920 | { | ||
921 | const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp; | ||
922 | struct nlattr *nl_tcp; | ||
923 | |||
924 | if (!tcp) | ||
925 | return 0; | ||
926 | |||
927 | nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION); | ||
928 | if (!nl_tcp) | ||
929 | return -ENOBUFS; | ||
930 | |||
931 | if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, | ||
932 | tcp->data_payload_max)) | ||
933 | return -ENOBUFS; | ||
934 | |||
935 | if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, | ||
936 | tcp->data_payload_max)) | ||
937 | return -ENOBUFS; | ||
938 | |||
939 | if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ)) | ||
940 | return -ENOBUFS; | ||
941 | |||
942 | if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, | ||
943 | sizeof(*tcp->tok), tcp->tok)) | ||
944 | return -ENOBUFS; | ||
945 | |||
946 | if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL, | ||
947 | tcp->data_interval_max)) | ||
948 | return -ENOBUFS; | ||
949 | |||
950 | if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD, | ||
951 | tcp->wake_payload_max)) | ||
952 | return -ENOBUFS; | ||
953 | |||
954 | nla_nest_end(msg, nl_tcp); | ||
955 | return 0; | ||
956 | } | ||
957 | #endif | ||
958 | |||
875 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags, | 959 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags, |
876 | struct cfg80211_registered_device *dev) | 960 | struct cfg80211_registered_device *dev) |
877 | { | 961 | { |
@@ -1238,12 +1322,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag | |||
1238 | dev->wiphy.wowlan.pattern_min_len, | 1322 | dev->wiphy.wowlan.pattern_min_len, |
1239 | .max_pattern_len = | 1323 | .max_pattern_len = |
1240 | dev->wiphy.wowlan.pattern_max_len, | 1324 | dev->wiphy.wowlan.pattern_max_len, |
1325 | .max_pkt_offset = | ||
1326 | dev->wiphy.wowlan.max_pkt_offset, | ||
1241 | }; | 1327 | }; |
1242 | if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, | 1328 | if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, |
1243 | sizeof(pat), &pat)) | 1329 | sizeof(pat), &pat)) |
1244 | goto nla_put_failure; | 1330 | goto nla_put_failure; |
1245 | } | 1331 | } |
1246 | 1332 | ||
1333 | if (nl80211_send_wowlan_tcp_caps(dev, msg)) | ||
1334 | goto nla_put_failure; | ||
1335 | |||
1247 | nla_nest_end(msg, nl_wowlan); | 1336 | nla_nest_end(msg, nl_wowlan); |
1248 | } | 1337 | } |
1249 | #endif | 1338 | #endif |
@@ -1276,6 +1365,15 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag | |||
1276 | dev->wiphy.max_acl_mac_addrs)) | 1365 | dev->wiphy.max_acl_mac_addrs)) |
1277 | goto nla_put_failure; | 1366 | goto nla_put_failure; |
1278 | 1367 | ||
1368 | if (dev->wiphy.extended_capabilities && | ||
1369 | (nla_put(msg, NL80211_ATTR_EXT_CAPA, | ||
1370 | dev->wiphy.extended_capabilities_len, | ||
1371 | dev->wiphy.extended_capabilities) || | ||
1372 | nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK, | ||
1373 | dev->wiphy.extended_capabilities_len, | ||
1374 | dev->wiphy.extended_capabilities_mask))) | ||
1375 | goto nla_put_failure; | ||
1376 | |||
1279 | return genlmsg_end(msg, hdr); | 1377 | return genlmsg_end(msg, hdr); |
1280 | 1378 | ||
1281 | nla_put_failure: | 1379 | nla_put_failure: |
@@ -2707,6 +2805,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | |||
2707 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 2805 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
2708 | struct cfg80211_ap_settings params; | 2806 | struct cfg80211_ap_settings params; |
2709 | int err; | 2807 | int err; |
2808 | u8 radar_detect_width = 0; | ||
2710 | 2809 | ||
2711 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2810 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
2712 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 2811 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
@@ -2825,9 +2924,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | |||
2825 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) | 2924 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) |
2826 | return -EINVAL; | 2925 | return -EINVAL; |
2827 | 2926 | ||
2927 | err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); | ||
2928 | if (err < 0) | ||
2929 | return err; | ||
2930 | if (err) { | ||
2931 | radar_detect_width = BIT(params.chandef.width); | ||
2932 | params.radar_required = true; | ||
2933 | } | ||
2934 | |||
2828 | mutex_lock(&rdev->devlist_mtx); | 2935 | mutex_lock(&rdev->devlist_mtx); |
2829 | err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan, | 2936 | err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, |
2830 | CHAN_MODE_SHARED); | 2937 | params.chandef.chan, |
2938 | CHAN_MODE_SHARED, | ||
2939 | radar_detect_width); | ||
2831 | mutex_unlock(&rdev->devlist_mtx); | 2940 | mutex_unlock(&rdev->devlist_mtx); |
2832 | 2941 | ||
2833 | if (err) | 2942 | if (err) |
@@ -3057,12 +3166,22 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, | |||
3057 | nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME, | 3166 | nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME, |
3058 | sinfo->inactive_time)) | 3167 | sinfo->inactive_time)) |
3059 | goto nla_put_failure; | 3168 | goto nla_put_failure; |
3060 | if ((sinfo->filled & STATION_INFO_RX_BYTES) && | 3169 | if ((sinfo->filled & (STATION_INFO_RX_BYTES | |
3170 | STATION_INFO_RX_BYTES64)) && | ||
3061 | nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES, | 3171 | nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES, |
3062 | sinfo->rx_bytes)) | 3172 | (u32)sinfo->rx_bytes)) |
3063 | goto nla_put_failure; | 3173 | goto nla_put_failure; |
3064 | if ((sinfo->filled & STATION_INFO_TX_BYTES) && | 3174 | if ((sinfo->filled & (STATION_INFO_TX_BYTES | |
3175 | NL80211_STA_INFO_TX_BYTES64)) && | ||
3065 | nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES, | 3176 | nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES, |
3177 | (u32)sinfo->tx_bytes)) | ||
3178 | goto nla_put_failure; | ||
3179 | if ((sinfo->filled & STATION_INFO_RX_BYTES64) && | ||
3180 | nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64, | ||
3181 | sinfo->rx_bytes)) | ||
3182 | goto nla_put_failure; | ||
3183 | if ((sinfo->filled & STATION_INFO_TX_BYTES64) && | ||
3184 | nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64, | ||
3066 | sinfo->tx_bytes)) | 3185 | sinfo->tx_bytes)) |
3067 | goto nla_put_failure; | 3186 | goto nla_put_failure; |
3068 | if ((sinfo->filled & STATION_INFO_LLID) && | 3187 | if ((sinfo->filled & STATION_INFO_LLID) && |
@@ -3290,6 +3409,63 @@ static struct net_device *get_vlan(struct genl_info *info, | |||
3290 | return ERR_PTR(ret); | 3409 | return ERR_PTR(ret); |
3291 | } | 3410 | } |
3292 | 3411 | ||
3412 | static struct nla_policy | ||
3413 | nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { | ||
3414 | [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, | ||
3415 | [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, | ||
3416 | }; | ||
3417 | |||
3418 | static int nl80211_set_station_tdls(struct genl_info *info, | ||
3419 | struct station_parameters *params) | ||
3420 | { | ||
3421 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
3422 | struct nlattr *tb[NL80211_STA_WME_MAX + 1]; | ||
3423 | struct nlattr *nla; | ||
3424 | int err; | ||
3425 | |||
3426 | /* Can only set if TDLS ... */ | ||
3427 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)) | ||
3428 | return -EOPNOTSUPP; | ||
3429 | |||
3430 | /* ... with external setup is supported */ | ||
3431 | if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) | ||
3432 | return -EOPNOTSUPP; | ||
3433 | |||
3434 | /* Dummy STA entry gets updated once the peer capabilities are known */ | ||
3435 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | ||
3436 | params->ht_capa = | ||
3437 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); | ||
3438 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) | ||
3439 | params->vht_capa = | ||
3440 | nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); | ||
3441 | |||
3442 | /* parse WME attributes if present */ | ||
3443 | if (!info->attrs[NL80211_ATTR_STA_WME]) | ||
3444 | return 0; | ||
3445 | |||
3446 | nla = info->attrs[NL80211_ATTR_STA_WME]; | ||
3447 | err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, | ||
3448 | nl80211_sta_wme_policy); | ||
3449 | if (err) | ||
3450 | return err; | ||
3451 | |||
3452 | if (tb[NL80211_STA_WME_UAPSD_QUEUES]) | ||
3453 | params->uapsd_queues = nla_get_u8( | ||
3454 | tb[NL80211_STA_WME_UAPSD_QUEUES]); | ||
3455 | if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) | ||
3456 | return -EINVAL; | ||
3457 | |||
3458 | if (tb[NL80211_STA_WME_MAX_SP]) | ||
3459 | params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); | ||
3460 | |||
3461 | if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) | ||
3462 | return -EINVAL; | ||
3463 | |||
3464 | params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; | ||
3465 | |||
3466 | return 0; | ||
3467 | } | ||
3468 | |||
3293 | static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | 3469 | static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) |
3294 | { | 3470 | { |
3295 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 3471 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
@@ -3318,8 +3494,20 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3318 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 3494 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
3319 | } | 3495 | } |
3320 | 3496 | ||
3321 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] || | 3497 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { |
3322 | info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 3498 | params.capability = |
3499 | nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]); | ||
3500 | params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; | ||
3501 | } | ||
3502 | |||
3503 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { | ||
3504 | params.ext_capab = | ||
3505 | nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | ||
3506 | params.ext_capab_len = | ||
3507 | nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | ||
3508 | } | ||
3509 | |||
3510 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | ||
3323 | return -EINVAL; | 3511 | return -EINVAL; |
3324 | 3512 | ||
3325 | if (!rdev->ops->change_station) | 3513 | if (!rdev->ops->change_station) |
@@ -3388,6 +3576,13 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3388 | /* reject other things that can't change */ | 3576 | /* reject other things that can't change */ |
3389 | if (params.supported_rates) | 3577 | if (params.supported_rates) |
3390 | return -EINVAL; | 3578 | return -EINVAL; |
3579 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) | ||
3580 | return -EINVAL; | ||
3581 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) | ||
3582 | return -EINVAL; | ||
3583 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || | ||
3584 | info->attrs[NL80211_ATTR_VHT_CAPABILITY]) | ||
3585 | return -EINVAL; | ||
3391 | 3586 | ||
3392 | /* must be last in here for error handling */ | 3587 | /* must be last in here for error handling */ |
3393 | params.vlan = get_vlan(info, rdev); | 3588 | params.vlan = get_vlan(info, rdev); |
@@ -3403,13 +3598,29 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3403 | * to change the flag. | 3598 | * to change the flag. |
3404 | */ | 3599 | */ |
3405 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); | 3600 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); |
3406 | /* fall through */ | 3601 | /* Include parameters for TDLS peer (driver will check) */ |
3602 | err = nl80211_set_station_tdls(info, ¶ms); | ||
3603 | if (err) | ||
3604 | return err; | ||
3605 | /* disallow things sta doesn't support */ | ||
3606 | if (params.plink_action) | ||
3607 | return -EINVAL; | ||
3608 | if (params.local_pm) | ||
3609 | return -EINVAL; | ||
3610 | /* reject any changes other than AUTHORIZED or WME (for TDLS) */ | ||
3611 | if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | | ||
3612 | BIT(NL80211_STA_FLAG_WME))) | ||
3613 | return -EINVAL; | ||
3614 | break; | ||
3407 | case NL80211_IFTYPE_ADHOC: | 3615 | case NL80211_IFTYPE_ADHOC: |
3408 | /* disallow things sta doesn't support */ | 3616 | /* disallow things sta doesn't support */ |
3409 | if (params.plink_action) | 3617 | if (params.plink_action) |
3410 | return -EINVAL; | 3618 | return -EINVAL; |
3411 | if (params.local_pm) | 3619 | if (params.local_pm) |
3412 | return -EINVAL; | 3620 | return -EINVAL; |
3621 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || | ||
3622 | info->attrs[NL80211_ATTR_VHT_CAPABILITY]) | ||
3623 | return -EINVAL; | ||
3413 | /* reject any changes other than AUTHORIZED */ | 3624 | /* reject any changes other than AUTHORIZED */ |
3414 | if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) | 3625 | if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) |
3415 | return -EINVAL; | 3626 | return -EINVAL; |
@@ -3420,6 +3631,13 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3420 | return -EINVAL; | 3631 | return -EINVAL; |
3421 | if (params.supported_rates) | 3632 | if (params.supported_rates) |
3422 | return -EINVAL; | 3633 | return -EINVAL; |
3634 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) | ||
3635 | return -EINVAL; | ||
3636 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) | ||
3637 | return -EINVAL; | ||
3638 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY] || | ||
3639 | info->attrs[NL80211_ATTR_VHT_CAPABILITY]) | ||
3640 | return -EINVAL; | ||
3423 | /* | 3641 | /* |
3424 | * No special handling for TDLS here -- the userspace | 3642 | * No special handling for TDLS here -- the userspace |
3425 | * mesh code doesn't have this bug. | 3643 | * mesh code doesn't have this bug. |
@@ -3444,12 +3662,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3444 | return err; | 3662 | return err; |
3445 | } | 3663 | } |
3446 | 3664 | ||
3447 | static struct nla_policy | ||
3448 | nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { | ||
3449 | [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, | ||
3450 | [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, | ||
3451 | }; | ||
3452 | |||
3453 | static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | 3665 | static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) |
3454 | { | 3666 | { |
3455 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 3667 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
@@ -3484,6 +3696,19 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
3484 | if (!params.aid || params.aid > IEEE80211_MAX_AID) | 3696 | if (!params.aid || params.aid > IEEE80211_MAX_AID) |
3485 | return -EINVAL; | 3697 | return -EINVAL; |
3486 | 3698 | ||
3699 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { | ||
3700 | params.capability = | ||
3701 | nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]); | ||
3702 | params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; | ||
3703 | } | ||
3704 | |||
3705 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { | ||
3706 | params.ext_capab = | ||
3707 | nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | ||
3708 | params.ext_capab_len = | ||
3709 | nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | ||
3710 | } | ||
3711 | |||
3487 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 3712 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
3488 | params.ht_capa = | 3713 | params.ht_capa = |
3489 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); | 3714 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); |
@@ -4977,6 +5202,54 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb, | |||
4977 | return err; | 5202 | return err; |
4978 | } | 5203 | } |
4979 | 5204 | ||
5205 | static int nl80211_start_radar_detection(struct sk_buff *skb, | ||
5206 | struct genl_info *info) | ||
5207 | { | ||
5208 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
5209 | struct net_device *dev = info->user_ptr[1]; | ||
5210 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
5211 | struct cfg80211_chan_def chandef; | ||
5212 | int err; | ||
5213 | |||
5214 | err = nl80211_parse_chandef(rdev, info, &chandef); | ||
5215 | if (err) | ||
5216 | return err; | ||
5217 | |||
5218 | if (wdev->cac_started) | ||
5219 | return -EBUSY; | ||
5220 | |||
5221 | err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef); | ||
5222 | if (err < 0) | ||
5223 | return err; | ||
5224 | |||
5225 | if (err == 0) | ||
5226 | return -EINVAL; | ||
5227 | |||
5228 | if (chandef.chan->dfs_state != NL80211_DFS_USABLE) | ||
5229 | return -EINVAL; | ||
5230 | |||
5231 | if (!rdev->ops->start_radar_detection) | ||
5232 | return -EOPNOTSUPP; | ||
5233 | |||
5234 | mutex_lock(&rdev->devlist_mtx); | ||
5235 | err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, | ||
5236 | chandef.chan, CHAN_MODE_SHARED, | ||
5237 | BIT(chandef.width)); | ||
5238 | if (err) | ||
5239 | goto err_locked; | ||
5240 | |||
5241 | err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef); | ||
5242 | if (!err) { | ||
5243 | wdev->channel = chandef.chan; | ||
5244 | wdev->cac_started = true; | ||
5245 | wdev->cac_start_time = jiffies; | ||
5246 | } | ||
5247 | err_locked: | ||
5248 | mutex_unlock(&rdev->devlist_mtx); | ||
5249 | |||
5250 | return err; | ||
5251 | } | ||
5252 | |||
4980 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | 5253 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, |
4981 | u32 seq, int flags, | 5254 | u32 seq, int flags, |
4982 | struct cfg80211_registered_device *rdev, | 5255 | struct cfg80211_registered_device *rdev, |
@@ -4987,6 +5260,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | |||
4987 | const struct cfg80211_bss_ies *ies; | 5260 | const struct cfg80211_bss_ies *ies; |
4988 | void *hdr; | 5261 | void *hdr; |
4989 | struct nlattr *bss; | 5262 | struct nlattr *bss; |
5263 | bool tsf = false; | ||
4990 | 5264 | ||
4991 | ASSERT_WDEV_LOCK(wdev); | 5265 | ASSERT_WDEV_LOCK(wdev); |
4992 | 5266 | ||
@@ -5010,22 +5284,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | |||
5010 | 5284 | ||
5011 | rcu_read_lock(); | 5285 | rcu_read_lock(); |
5012 | ies = rcu_dereference(res->ies); | 5286 | ies = rcu_dereference(res->ies); |
5013 | if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, | 5287 | if (ies) { |
5014 | ies->len, ies->data)) { | 5288 | if (nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) |
5015 | rcu_read_unlock(); | 5289 | goto fail_unlock_rcu; |
5016 | goto nla_put_failure; | 5290 | tsf = true; |
5291 | if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS, | ||
5292 | ies->len, ies->data)) | ||
5293 | goto fail_unlock_rcu; | ||
5017 | } | 5294 | } |
5018 | ies = rcu_dereference(res->beacon_ies); | 5295 | ies = rcu_dereference(res->beacon_ies); |
5019 | if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, | 5296 | if (ies) { |
5020 | ies->len, ies->data)) { | 5297 | if (!tsf && nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf)) |
5021 | rcu_read_unlock(); | 5298 | goto fail_unlock_rcu; |
5022 | goto nla_put_failure; | 5299 | if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES, |
5300 | ies->len, ies->data)) | ||
5301 | goto fail_unlock_rcu; | ||
5023 | } | 5302 | } |
5024 | rcu_read_unlock(); | 5303 | rcu_read_unlock(); |
5025 | 5304 | ||
5026 | if (res->tsf && | ||
5027 | nla_put_u64(msg, NL80211_BSS_TSF, res->tsf)) | ||
5028 | goto nla_put_failure; | ||
5029 | if (res->beacon_interval && | 5305 | if (res->beacon_interval && |
5030 | nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval)) | 5306 | nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval)) |
5031 | goto nla_put_failure; | 5307 | goto nla_put_failure; |
@@ -5070,6 +5346,8 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | |||
5070 | 5346 | ||
5071 | return genlmsg_end(msg, hdr); | 5347 | return genlmsg_end(msg, hdr); |
5072 | 5348 | ||
5349 | fail_unlock_rcu: | ||
5350 | rcu_read_unlock(); | ||
5073 | nla_put_failure: | 5351 | nla_put_failure: |
5074 | genlmsg_cancel(msg, hdr); | 5352 | genlmsg_cancel(msg, hdr); |
5075 | return -EMSGSIZE; | 5353 | return -EMSGSIZE; |
@@ -6880,16 +7158,100 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) | |||
6880 | } | 7158 | } |
6881 | 7159 | ||
6882 | #ifdef CONFIG_PM | 7160 | #ifdef CONFIG_PM |
7161 | static int nl80211_send_wowlan_patterns(struct sk_buff *msg, | ||
7162 | struct cfg80211_registered_device *rdev) | ||
7163 | { | ||
7164 | struct nlattr *nl_pats, *nl_pat; | ||
7165 | int i, pat_len; | ||
7166 | |||
7167 | if (!rdev->wowlan->n_patterns) | ||
7168 | return 0; | ||
7169 | |||
7170 | nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN); | ||
7171 | if (!nl_pats) | ||
7172 | return -ENOBUFS; | ||
7173 | |||
7174 | for (i = 0; i < rdev->wowlan->n_patterns; i++) { | ||
7175 | nl_pat = nla_nest_start(msg, i + 1); | ||
7176 | if (!nl_pat) | ||
7177 | return -ENOBUFS; | ||
7178 | pat_len = rdev->wowlan->patterns[i].pattern_len; | ||
7179 | if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, | ||
7180 | DIV_ROUND_UP(pat_len, 8), | ||
7181 | rdev->wowlan->patterns[i].mask) || | ||
7182 | nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, | ||
7183 | pat_len, rdev->wowlan->patterns[i].pattern) || | ||
7184 | nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET, | ||
7185 | rdev->wowlan->patterns[i].pkt_offset)) | ||
7186 | return -ENOBUFS; | ||
7187 | nla_nest_end(msg, nl_pat); | ||
7188 | } | ||
7189 | nla_nest_end(msg, nl_pats); | ||
7190 | |||
7191 | return 0; | ||
7192 | } | ||
7193 | |||
7194 | static int nl80211_send_wowlan_tcp(struct sk_buff *msg, | ||
7195 | struct cfg80211_wowlan_tcp *tcp) | ||
7196 | { | ||
7197 | struct nlattr *nl_tcp; | ||
7198 | |||
7199 | if (!tcp) | ||
7200 | return 0; | ||
7201 | |||
7202 | nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION); | ||
7203 | if (!nl_tcp) | ||
7204 | return -ENOBUFS; | ||
7205 | |||
7206 | if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) || | ||
7207 | nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) || | ||
7208 | nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) || | ||
7209 | nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) || | ||
7210 | nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) || | ||
7211 | nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD, | ||
7212 | tcp->payload_len, tcp->payload) || | ||
7213 | nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL, | ||
7214 | tcp->data_interval) || | ||
7215 | nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD, | ||
7216 | tcp->wake_len, tcp->wake_data) || | ||
7217 | nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK, | ||
7218 | DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask)) | ||
7219 | return -ENOBUFS; | ||
7220 | |||
7221 | if (tcp->payload_seq.len && | ||
7222 | nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, | ||
7223 | sizeof(tcp->payload_seq), &tcp->payload_seq)) | ||
7224 | return -ENOBUFS; | ||
7225 | |||
7226 | if (tcp->payload_tok.len && | ||
7227 | nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, | ||
7228 | sizeof(tcp->payload_tok) + tcp->tokens_size, | ||
7229 | &tcp->payload_tok)) | ||
7230 | return -ENOBUFS; | ||
7231 | |||
7232 | return 0; | ||
7233 | } | ||
7234 | |||
6883 | static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) | 7235 | static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) |
6884 | { | 7236 | { |
6885 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 7237 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
6886 | struct sk_buff *msg; | 7238 | struct sk_buff *msg; |
6887 | void *hdr; | 7239 | void *hdr; |
7240 | u32 size = NLMSG_DEFAULT_SIZE; | ||
6888 | 7241 | ||
6889 | if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) | 7242 | if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns && |
7243 | !rdev->wiphy.wowlan.tcp) | ||
6890 | return -EOPNOTSUPP; | 7244 | return -EOPNOTSUPP; |
6891 | 7245 | ||
6892 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 7246 | if (rdev->wowlan && rdev->wowlan->tcp) { |
7247 | /* adjust size to have room for all the data */ | ||
7248 | size += rdev->wowlan->tcp->tokens_size + | ||
7249 | rdev->wowlan->tcp->payload_len + | ||
7250 | rdev->wowlan->tcp->wake_len + | ||
7251 | rdev->wowlan->tcp->wake_len / 8; | ||
7252 | } | ||
7253 | |||
7254 | msg = nlmsg_new(size, GFP_KERNEL); | ||
6893 | if (!msg) | 7255 | if (!msg) |
6894 | return -ENOMEM; | 7256 | return -ENOMEM; |
6895 | 7257 | ||
@@ -6920,31 +7282,12 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
6920 | (rdev->wowlan->rfkill_release && | 7282 | (rdev->wowlan->rfkill_release && |
6921 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) | 7283 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) |
6922 | goto nla_put_failure; | 7284 | goto nla_put_failure; |
6923 | if (rdev->wowlan->n_patterns) { | ||
6924 | struct nlattr *nl_pats, *nl_pat; | ||
6925 | int i, pat_len; | ||
6926 | 7285 | ||
6927 | nl_pats = nla_nest_start(msg, | 7286 | if (nl80211_send_wowlan_patterns(msg, rdev)) |
6928 | NL80211_WOWLAN_TRIG_PKT_PATTERN); | 7287 | goto nla_put_failure; |
6929 | if (!nl_pats) | ||
6930 | goto nla_put_failure; | ||
6931 | 7288 | ||
6932 | for (i = 0; i < rdev->wowlan->n_patterns; i++) { | 7289 | if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp)) |
6933 | nl_pat = nla_nest_start(msg, i + 1); | 7290 | goto nla_put_failure; |
6934 | if (!nl_pat) | ||
6935 | goto nla_put_failure; | ||
6936 | pat_len = rdev->wowlan->patterns[i].pattern_len; | ||
6937 | if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, | ||
6938 | DIV_ROUND_UP(pat_len, 8), | ||
6939 | rdev->wowlan->patterns[i].mask) || | ||
6940 | nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, | ||
6941 | pat_len, | ||
6942 | rdev->wowlan->patterns[i].pattern)) | ||
6943 | goto nla_put_failure; | ||
6944 | nla_nest_end(msg, nl_pat); | ||
6945 | } | ||
6946 | nla_nest_end(msg, nl_pats); | ||
6947 | } | ||
6948 | 7291 | ||
6949 | nla_nest_end(msg, nl_wowlan); | 7292 | nla_nest_end(msg, nl_wowlan); |
6950 | } | 7293 | } |
@@ -6957,6 +7300,150 @@ nla_put_failure: | |||
6957 | return -ENOBUFS; | 7300 | return -ENOBUFS; |
6958 | } | 7301 | } |
6959 | 7302 | ||
7303 | static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, | ||
7304 | struct nlattr *attr, | ||
7305 | struct cfg80211_wowlan *trig) | ||
7306 | { | ||
7307 | struct nlattr *tb[NUM_NL80211_WOWLAN_TCP]; | ||
7308 | struct cfg80211_wowlan_tcp *cfg; | ||
7309 | struct nl80211_wowlan_tcp_data_token *tok = NULL; | ||
7310 | struct nl80211_wowlan_tcp_data_seq *seq = NULL; | ||
7311 | u32 size; | ||
7312 | u32 data_size, wake_size, tokens_size = 0, wake_mask_size; | ||
7313 | int err, port; | ||
7314 | |||
7315 | if (!rdev->wiphy.wowlan.tcp) | ||
7316 | return -EINVAL; | ||
7317 | |||
7318 | err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP, | ||
7319 | nla_data(attr), nla_len(attr), | ||
7320 | nl80211_wowlan_tcp_policy); | ||
7321 | if (err) | ||
7322 | return err; | ||
7323 | |||
7324 | if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] || | ||
7325 | !tb[NL80211_WOWLAN_TCP_DST_IPV4] || | ||
7326 | !tb[NL80211_WOWLAN_TCP_DST_MAC] || | ||
7327 | !tb[NL80211_WOWLAN_TCP_DST_PORT] || | ||
7328 | !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] || | ||
7329 | !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] || | ||
7330 | !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] || | ||
7331 | !tb[NL80211_WOWLAN_TCP_WAKE_MASK]) | ||
7332 | return -EINVAL; | ||
7333 | |||
7334 | data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]); | ||
7335 | if (data_size > rdev->wiphy.wowlan.tcp->data_payload_max) | ||
7336 | return -EINVAL; | ||
7337 | |||
7338 | if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) > | ||
7339 | rdev->wiphy.wowlan.tcp->data_interval_max) | ||
7340 | return -EINVAL; | ||
7341 | |||
7342 | wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]); | ||
7343 | if (wake_size > rdev->wiphy.wowlan.tcp->wake_payload_max) | ||
7344 | return -EINVAL; | ||
7345 | |||
7346 | wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]); | ||
7347 | if (wake_mask_size != DIV_ROUND_UP(wake_size, 8)) | ||
7348 | return -EINVAL; | ||
7349 | |||
7350 | if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) { | ||
7351 | u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); | ||
7352 | |||
7353 | tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); | ||
7354 | tokens_size = tokln - sizeof(*tok); | ||
7355 | |||
7356 | if (!tok->len || tokens_size % tok->len) | ||
7357 | return -EINVAL; | ||
7358 | if (!rdev->wiphy.wowlan.tcp->tok) | ||
7359 | return -EINVAL; | ||
7360 | if (tok->len > rdev->wiphy.wowlan.tcp->tok->max_len) | ||
7361 | return -EINVAL; | ||
7362 | if (tok->len < rdev->wiphy.wowlan.tcp->tok->min_len) | ||
7363 | return -EINVAL; | ||
7364 | if (tokens_size > rdev->wiphy.wowlan.tcp->tok->bufsize) | ||
7365 | return -EINVAL; | ||
7366 | if (tok->offset + tok->len > data_size) | ||
7367 | return -EINVAL; | ||
7368 | } | ||
7369 | |||
7370 | if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) { | ||
7371 | seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]); | ||
7372 | if (!rdev->wiphy.wowlan.tcp->seq) | ||
7373 | return -EINVAL; | ||
7374 | if (seq->len == 0 || seq->len > 4) | ||
7375 | return -EINVAL; | ||
7376 | if (seq->len + seq->offset > data_size) | ||
7377 | return -EINVAL; | ||
7378 | } | ||
7379 | |||
7380 | size = sizeof(*cfg); | ||
7381 | size += data_size; | ||
7382 | size += wake_size + wake_mask_size; | ||
7383 | size += tokens_size; | ||
7384 | |||
7385 | cfg = kzalloc(size, GFP_KERNEL); | ||
7386 | if (!cfg) | ||
7387 | return -ENOMEM; | ||
7388 | cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]); | ||
7389 | cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]); | ||
7390 | memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]), | ||
7391 | ETH_ALEN); | ||
7392 | if (tb[NL80211_WOWLAN_TCP_SRC_PORT]) | ||
7393 | port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]); | ||
7394 | else | ||
7395 | port = 0; | ||
7396 | #ifdef CONFIG_INET | ||
7397 | /* allocate a socket and port for it and use it */ | ||
7398 | err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM, | ||
7399 | IPPROTO_TCP, &cfg->sock, 1); | ||
7400 | if (err) { | ||
7401 | kfree(cfg); | ||
7402 | return err; | ||
7403 | } | ||
7404 | if (inet_csk_get_port(cfg->sock->sk, port)) { | ||
7405 | sock_release(cfg->sock); | ||
7406 | kfree(cfg); | ||
7407 | return -EADDRINUSE; | ||
7408 | } | ||
7409 | cfg->src_port = inet_sk(cfg->sock->sk)->inet_num; | ||
7410 | #else | ||
7411 | if (!port) { | ||
7412 | kfree(cfg); | ||
7413 | return -EINVAL; | ||
7414 | } | ||
7415 | cfg->src_port = port; | ||
7416 | #endif | ||
7417 | |||
7418 | cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]); | ||
7419 | cfg->payload_len = data_size; | ||
7420 | cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size; | ||
7421 | memcpy((void *)cfg->payload, | ||
7422 | nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]), | ||
7423 | data_size); | ||
7424 | if (seq) | ||
7425 | cfg->payload_seq = *seq; | ||
7426 | cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]); | ||
7427 | cfg->wake_len = wake_size; | ||
7428 | cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size; | ||
7429 | memcpy((void *)cfg->wake_data, | ||
7430 | nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]), | ||
7431 | wake_size); | ||
7432 | cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size + | ||
7433 | data_size + wake_size; | ||
7434 | memcpy((void *)cfg->wake_mask, | ||
7435 | nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]), | ||
7436 | wake_mask_size); | ||
7437 | if (tok) { | ||
7438 | cfg->tokens_size = tokens_size; | ||
7439 | memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size); | ||
7440 | } | ||
7441 | |||
7442 | trig->tcp = cfg; | ||
7443 | |||
7444 | return 0; | ||
7445 | } | ||
7446 | |||
6960 | static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | 7447 | static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) |
6961 | { | 7448 | { |
6962 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 7449 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
@@ -6967,7 +7454,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
6967 | int err, i; | 7454 | int err, i; |
6968 | bool prev_enabled = rdev->wowlan; | 7455 | bool prev_enabled = rdev->wowlan; |
6969 | 7456 | ||
6970 | if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) | 7457 | if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns && |
7458 | !rdev->wiphy.wowlan.tcp) | ||
6971 | return -EOPNOTSUPP; | 7459 | return -EOPNOTSUPP; |
6972 | 7460 | ||
6973 | if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { | 7461 | if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { |
@@ -7031,7 +7519,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
7031 | if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { | 7519 | if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { |
7032 | struct nlattr *pat; | 7520 | struct nlattr *pat; |
7033 | int n_patterns = 0; | 7521 | int n_patterns = 0; |
7034 | int rem, pat_len, mask_len; | 7522 | int rem, pat_len, mask_len, pkt_offset; |
7035 | struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; | 7523 | struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; |
7036 | 7524 | ||
7037 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], | 7525 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], |
@@ -7066,6 +7554,15 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
7066 | pat_len < wowlan->pattern_min_len) | 7554 | pat_len < wowlan->pattern_min_len) |
7067 | goto error; | 7555 | goto error; |
7068 | 7556 | ||
7557 | if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]) | ||
7558 | pkt_offset = 0; | ||
7559 | else | ||
7560 | pkt_offset = nla_get_u32( | ||
7561 | pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]); | ||
7562 | if (pkt_offset > wowlan->max_pkt_offset) | ||
7563 | goto error; | ||
7564 | new_triggers.patterns[i].pkt_offset = pkt_offset; | ||
7565 | |||
7069 | new_triggers.patterns[i].mask = | 7566 | new_triggers.patterns[i].mask = |
7070 | kmalloc(mask_len + pat_len, GFP_KERNEL); | 7567 | kmalloc(mask_len + pat_len, GFP_KERNEL); |
7071 | if (!new_triggers.patterns[i].mask) { | 7568 | if (!new_triggers.patterns[i].mask) { |
@@ -7085,6 +7582,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
7085 | } | 7582 | } |
7086 | } | 7583 | } |
7087 | 7584 | ||
7585 | if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) { | ||
7586 | err = nl80211_parse_wowlan_tcp( | ||
7587 | rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION], | ||
7588 | &new_triggers); | ||
7589 | if (err) | ||
7590 | goto error; | ||
7591 | } | ||
7592 | |||
7088 | ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); | 7593 | ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); |
7089 | if (!ntrig) { | 7594 | if (!ntrig) { |
7090 | err = -ENOMEM; | 7595 | err = -ENOMEM; |
@@ -7102,6 +7607,9 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | |||
7102 | for (i = 0; i < new_triggers.n_patterns; i++) | 7607 | for (i = 0; i < new_triggers.n_patterns; i++) |
7103 | kfree(new_triggers.patterns[i].mask); | 7608 | kfree(new_triggers.patterns[i].mask); |
7104 | kfree(new_triggers.patterns); | 7609 | kfree(new_triggers.patterns); |
7610 | if (new_triggers.tcp && new_triggers.tcp->sock) | ||
7611 | sock_release(new_triggers.tcp->sock); | ||
7612 | kfree(new_triggers.tcp); | ||
7105 | return err; | 7613 | return err; |
7106 | } | 7614 | } |
7107 | #endif | 7615 | #endif |
@@ -7992,6 +8500,14 @@ static struct genl_ops nl80211_ops[] = { | |||
7992 | .internal_flags = NL80211_FLAG_NEED_NETDEV | | 8500 | .internal_flags = NL80211_FLAG_NEED_NETDEV | |
7993 | NL80211_FLAG_NEED_RTNL, | 8501 | NL80211_FLAG_NEED_RTNL, |
7994 | }, | 8502 | }, |
8503 | { | ||
8504 | .cmd = NL80211_CMD_RADAR_DETECT, | ||
8505 | .doit = nl80211_start_radar_detection, | ||
8506 | .policy = nl80211_policy, | ||
8507 | .flags = GENL_ADMIN_PERM, | ||
8508 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
8509 | NL80211_FLAG_NEED_RTNL, | ||
8510 | }, | ||
7995 | }; | 8511 | }; |
7996 | 8512 | ||
7997 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 8513 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
@@ -9189,6 +9705,57 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev, | |||
9189 | } | 9705 | } |
9190 | 9706 | ||
9191 | void | 9707 | void |
9708 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, | ||
9709 | struct cfg80211_chan_def *chandef, | ||
9710 | enum nl80211_radar_event event, | ||
9711 | struct net_device *netdev, gfp_t gfp) | ||
9712 | { | ||
9713 | struct sk_buff *msg; | ||
9714 | void *hdr; | ||
9715 | |||
9716 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
9717 | if (!msg) | ||
9718 | return; | ||
9719 | |||
9720 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT); | ||
9721 | if (!hdr) { | ||
9722 | nlmsg_free(msg); | ||
9723 | return; | ||
9724 | } | ||
9725 | |||
9726 | if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) | ||
9727 | goto nla_put_failure; | ||
9728 | |||
9729 | /* NOP and radar events don't need a netdev parameter */ | ||
9730 | if (netdev) { | ||
9731 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | ||
9732 | |||
9733 | if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || | ||
9734 | nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) | ||
9735 | goto nla_put_failure; | ||
9736 | } | ||
9737 | |||
9738 | if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event)) | ||
9739 | goto nla_put_failure; | ||
9740 | |||
9741 | if (nl80211_send_chandef(msg, chandef)) | ||
9742 | goto nla_put_failure; | ||
9743 | |||
9744 | if (genlmsg_end(msg, hdr) < 0) { | ||
9745 | nlmsg_free(msg); | ||
9746 | return; | ||
9747 | } | ||
9748 | |||
9749 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
9750 | nl80211_mlme_mcgrp.id, gfp); | ||
9751 | return; | ||
9752 | |||
9753 | nla_put_failure: | ||
9754 | genlmsg_cancel(msg, hdr); | ||
9755 | nlmsg_free(msg); | ||
9756 | } | ||
9757 | |||
9758 | void | ||
9192 | nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, | 9759 | nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, |
9193 | struct net_device *netdev, const u8 *peer, | 9760 | struct net_device *netdev, const u8 *peer, |
9194 | u32 num_packets, gfp_t gfp) | 9761 | u32 num_packets, gfp_t gfp) |
@@ -9323,6 +9890,114 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, | |||
9323 | } | 9890 | } |
9324 | EXPORT_SYMBOL(cfg80211_report_obss_beacon); | 9891 | EXPORT_SYMBOL(cfg80211_report_obss_beacon); |
9325 | 9892 | ||
9893 | #ifdef CONFIG_PM | ||
9894 | void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, | ||
9895 | struct cfg80211_wowlan_wakeup *wakeup, | ||
9896 | gfp_t gfp) | ||
9897 | { | ||
9898 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | ||
9899 | struct sk_buff *msg; | ||
9900 | void *hdr; | ||
9901 | int err, size = 200; | ||
9902 | |||
9903 | trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup); | ||
9904 | |||
9905 | if (wakeup) | ||
9906 | size += wakeup->packet_present_len; | ||
9907 | |||
9908 | msg = nlmsg_new(size, gfp); | ||
9909 | if (!msg) | ||
9910 | return; | ||
9911 | |||
9912 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN); | ||
9913 | if (!hdr) | ||
9914 | goto free_msg; | ||
9915 | |||
9916 | if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || | ||
9917 | nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev))) | ||
9918 | goto free_msg; | ||
9919 | |||
9920 | if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX, | ||
9921 | wdev->netdev->ifindex)) | ||
9922 | goto free_msg; | ||
9923 | |||
9924 | if (wakeup) { | ||
9925 | struct nlattr *reasons; | ||
9926 | |||
9927 | reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); | ||
9928 | |||
9929 | if (wakeup->disconnect && | ||
9930 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) | ||
9931 | goto free_msg; | ||
9932 | if (wakeup->magic_pkt && | ||
9933 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) | ||
9934 | goto free_msg; | ||
9935 | if (wakeup->gtk_rekey_failure && | ||
9936 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) | ||
9937 | goto free_msg; | ||
9938 | if (wakeup->eap_identity_req && | ||
9939 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) | ||
9940 | goto free_msg; | ||
9941 | if (wakeup->four_way_handshake && | ||
9942 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) | ||
9943 | goto free_msg; | ||
9944 | if (wakeup->rfkill_release && | ||
9945 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)) | ||
9946 | goto free_msg; | ||
9947 | |||
9948 | if (wakeup->pattern_idx >= 0 && | ||
9949 | nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, | ||
9950 | wakeup->pattern_idx)) | ||
9951 | goto free_msg; | ||
9952 | |||
9953 | if (wakeup->tcp_match) | ||
9954 | nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH); | ||
9955 | |||
9956 | if (wakeup->tcp_connlost) | ||
9957 | nla_put_flag(msg, | ||
9958 | NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST); | ||
9959 | |||
9960 | if (wakeup->tcp_nomoretokens) | ||
9961 | nla_put_flag(msg, | ||
9962 | NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS); | ||
9963 | |||
9964 | if (wakeup->packet) { | ||
9965 | u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; | ||
9966 | u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN; | ||
9967 | |||
9968 | if (!wakeup->packet_80211) { | ||
9969 | pkt_attr = | ||
9970 | NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023; | ||
9971 | len_attr = | ||
9972 | NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN; | ||
9973 | } | ||
9974 | |||
9975 | if (wakeup->packet_len && | ||
9976 | nla_put_u32(msg, len_attr, wakeup->packet_len)) | ||
9977 | goto free_msg; | ||
9978 | |||
9979 | if (nla_put(msg, pkt_attr, wakeup->packet_present_len, | ||
9980 | wakeup->packet)) | ||
9981 | goto free_msg; | ||
9982 | } | ||
9983 | |||
9984 | nla_nest_end(msg, reasons); | ||
9985 | } | ||
9986 | |||
9987 | err = genlmsg_end(msg, hdr); | ||
9988 | if (err < 0) | ||
9989 | goto free_msg; | ||
9990 | |||
9991 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
9992 | nl80211_mlme_mcgrp.id, gfp); | ||
9993 | return; | ||
9994 | |||
9995 | free_msg: | ||
9996 | nlmsg_free(msg); | ||
9997 | } | ||
9998 | EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup); | ||
9999 | #endif | ||
10000 | |||
9326 | void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, | 10001 | void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, |
9327 | enum nl80211_tdls_operation oper, | 10002 | enum nl80211_tdls_operation oper, |
9328 | u16 reason_code, gfp_t gfp) | 10003 | u16 reason_code, gfp_t gfp) |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 2acba8477e9d..b061da4919e1 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -108,6 +108,13 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, | |||
108 | struct net_device *netdev, | 108 | struct net_device *netdev, |
109 | enum nl80211_cqm_rssi_threshold_event rssi_event, | 109 | enum nl80211_cqm_rssi_threshold_event rssi_event, |
110 | gfp_t gfp); | 110 | gfp_t gfp); |
111 | |||
112 | void | ||
113 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, | ||
114 | struct cfg80211_chan_def *chandef, | ||
115 | enum nl80211_radar_event event, | ||
116 | struct net_device *netdev, gfp_t gfp); | ||
117 | |||
111 | void | 118 | void |
112 | nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, | 119 | nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, |
113 | struct net_device *netdev, const u8 *peer, | 120 | struct net_device *netdev, const u8 *peer, |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index de02d633c212..98532c00242d 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -866,6 +866,10 @@ static void handle_channel(struct wiphy *wiphy, | |||
866 | 866 | ||
867 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | 867 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) |
868 | bw_flags = IEEE80211_CHAN_NO_HT40; | 868 | bw_flags = IEEE80211_CHAN_NO_HT40; |
869 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) | ||
870 | bw_flags |= IEEE80211_CHAN_NO_80MHZ; | ||
871 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) | ||
872 | bw_flags |= IEEE80211_CHAN_NO_160MHZ; | ||
869 | 873 | ||
870 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 874 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
871 | request_wiphy && request_wiphy == wiphy && | 875 | request_wiphy && request_wiphy == wiphy && |
@@ -884,6 +888,9 @@ static void handle_channel(struct wiphy *wiphy, | |||
884 | return; | 888 | return; |
885 | } | 889 | } |
886 | 890 | ||
891 | chan->dfs_state = NL80211_DFS_USABLE; | ||
892 | chan->dfs_state_entered = jiffies; | ||
893 | |||
887 | chan->beacon_found = false; | 894 | chan->beacon_found = false; |
888 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); | 895 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
889 | chan->max_antenna_gain = | 896 | chan->max_antenna_gain = |
@@ -1261,6 +1268,10 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1261 | 1268 | ||
1262 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | 1269 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) |
1263 | bw_flags = IEEE80211_CHAN_NO_HT40; | 1270 | bw_flags = IEEE80211_CHAN_NO_HT40; |
1271 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80)) | ||
1272 | bw_flags |= IEEE80211_CHAN_NO_80MHZ; | ||
1273 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160)) | ||
1274 | bw_flags |= IEEE80211_CHAN_NO_160MHZ; | ||
1264 | 1275 | ||
1265 | chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; | 1276 | chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; |
1266 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); | 1277 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); |
@@ -2189,10 +2200,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2189 | * However if a driver requested this specific regulatory | 2200 | * However if a driver requested this specific regulatory |
2190 | * domain we keep it for its private use | 2201 | * domain we keep it for its private use |
2191 | */ | 2202 | */ |
2192 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) | 2203 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) { |
2204 | const struct ieee80211_regdomain *tmp; | ||
2205 | |||
2206 | tmp = get_wiphy_regdom(request_wiphy); | ||
2193 | rcu_assign_pointer(request_wiphy->regd, rd); | 2207 | rcu_assign_pointer(request_wiphy->regd, rd); |
2194 | else | 2208 | rcu_free_regdom(tmp); |
2209 | } else { | ||
2195 | kfree(rd); | 2210 | kfree(rd); |
2211 | } | ||
2196 | 2212 | ||
2197 | rd = NULL; | 2213 | rd = NULL; |
2198 | 2214 | ||
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 45f1618c8e23..674aadca0079 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -19,55 +19,142 @@ | |||
19 | #include "wext-compat.h" | 19 | #include "wext-compat.h" |
20 | #include "rdev-ops.h" | 20 | #include "rdev-ops.h" |
21 | 21 | ||
22 | /** | ||
23 | * DOC: BSS tree/list structure | ||
24 | * | ||
25 | * At the top level, the BSS list is kept in both a list in each | ||
26 | * registered device (@bss_list) as well as an RB-tree for faster | ||
27 | * lookup. In the RB-tree, entries can be looked up using their | ||
28 | * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID | ||
29 | * for other BSSes. | ||
30 | * | ||
31 | * Due to the possibility of hidden SSIDs, there's a second level | ||
32 | * structure, the "hidden_list" and "hidden_beacon_bss" pointer. | ||
33 | * The hidden_list connects all BSSes belonging to a single AP | ||
34 | * that has a hidden SSID, and connects beacon and probe response | ||
35 | * entries. For a probe response entry for a hidden SSID, the | ||
36 | * hidden_beacon_bss pointer points to the BSS struct holding the | ||
37 | * beacon's information. | ||
38 | * | ||
39 | * Reference counting is done for all these references except for | ||
40 | * the hidden_list, so that a beacon BSS struct that is otherwise | ||
41 | * not referenced has one reference for being on the bss_list and | ||
42 | * one for each probe response entry that points to it using the | ||
43 | * hidden_beacon_bss pointer. When a BSS struct that has such a | ||
44 | * pointer is get/put, the refcount update is also propagated to | ||
45 | * the referenced struct, this ensure that it cannot get removed | ||
46 | * while somebody is using the probe response version. | ||
47 | * | ||
48 | * Note that the hidden_beacon_bss pointer never changes, due to | ||
49 | * the reference counting. Therefore, no locking is needed for | ||
50 | * it. | ||
51 | * | ||
52 | * Also note that the hidden_beacon_bss pointer is only relevant | ||
53 | * if the driver uses something other than the IEs, e.g. private | ||
54 | * data stored stored in the BSS struct, since the beacon IEs are | ||
55 | * also linked into the probe response struct. | ||
56 | */ | ||
57 | |||
22 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) | 58 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) |
23 | 59 | ||
24 | static void bss_release(struct kref *ref) | 60 | static void bss_free(struct cfg80211_internal_bss *bss) |
25 | { | 61 | { |
26 | struct cfg80211_bss_ies *ies; | 62 | struct cfg80211_bss_ies *ies; |
27 | struct cfg80211_internal_bss *bss; | ||
28 | |||
29 | bss = container_of(ref, struct cfg80211_internal_bss, ref); | ||
30 | 63 | ||
31 | if (WARN_ON(atomic_read(&bss->hold))) | 64 | if (WARN_ON(atomic_read(&bss->hold))) |
32 | return; | 65 | return; |
33 | 66 | ||
34 | if (bss->pub.free_priv) | ||
35 | bss->pub.free_priv(&bss->pub); | ||
36 | |||
37 | ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); | 67 | ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); |
38 | if (ies) | 68 | if (ies && !bss->pub.hidden_beacon_bss) |
39 | kfree_rcu(ies, rcu_head); | 69 | kfree_rcu(ies, rcu_head); |
40 | ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); | 70 | ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); |
41 | if (ies) | 71 | if (ies) |
42 | kfree_rcu(ies, rcu_head); | 72 | kfree_rcu(ies, rcu_head); |
43 | 73 | ||
74 | /* | ||
75 | * This happens when the module is removed, it doesn't | ||
76 | * really matter any more save for completeness | ||
77 | */ | ||
78 | if (!list_empty(&bss->hidden_list)) | ||
79 | list_del(&bss->hidden_list); | ||
80 | |||
44 | kfree(bss); | 81 | kfree(bss); |
45 | } | 82 | } |
46 | 83 | ||
47 | /* must hold dev->bss_lock! */ | 84 | static inline void bss_ref_get(struct cfg80211_registered_device *dev, |
48 | static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, | 85 | struct cfg80211_internal_bss *bss) |
86 | { | ||
87 | lockdep_assert_held(&dev->bss_lock); | ||
88 | |||
89 | bss->refcount++; | ||
90 | if (bss->pub.hidden_beacon_bss) { | ||
91 | bss = container_of(bss->pub.hidden_beacon_bss, | ||
92 | struct cfg80211_internal_bss, | ||
93 | pub); | ||
94 | bss->refcount++; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | static inline void bss_ref_put(struct cfg80211_registered_device *dev, | ||
99 | struct cfg80211_internal_bss *bss) | ||
100 | { | ||
101 | lockdep_assert_held(&dev->bss_lock); | ||
102 | |||
103 | if (bss->pub.hidden_beacon_bss) { | ||
104 | struct cfg80211_internal_bss *hbss; | ||
105 | hbss = container_of(bss->pub.hidden_beacon_bss, | ||
106 | struct cfg80211_internal_bss, | ||
107 | pub); | ||
108 | hbss->refcount--; | ||
109 | if (hbss->refcount == 0) | ||
110 | bss_free(hbss); | ||
111 | } | ||
112 | bss->refcount--; | ||
113 | if (bss->refcount == 0) | ||
114 | bss_free(bss); | ||
115 | } | ||
116 | |||
117 | static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, | ||
49 | struct cfg80211_internal_bss *bss) | 118 | struct cfg80211_internal_bss *bss) |
50 | { | 119 | { |
120 | lockdep_assert_held(&dev->bss_lock); | ||
121 | |||
122 | if (!list_empty(&bss->hidden_list)) { | ||
123 | /* | ||
124 | * don't remove the beacon entry if it has | ||
125 | * probe responses associated with it | ||
126 | */ | ||
127 | if (!bss->pub.hidden_beacon_bss) | ||
128 | return false; | ||
129 | /* | ||
130 | * if it's a probe response entry break its | ||
131 | * link to the other entries in the group | ||
132 | */ | ||
133 | list_del_init(&bss->hidden_list); | ||
134 | } | ||
135 | |||
51 | list_del_init(&bss->list); | 136 | list_del_init(&bss->list); |
52 | rb_erase(&bss->rbn, &dev->bss_tree); | 137 | rb_erase(&bss->rbn, &dev->bss_tree); |
53 | kref_put(&bss->ref, bss_release); | 138 | bss_ref_put(dev, bss); |
139 | return true; | ||
54 | } | 140 | } |
55 | 141 | ||
56 | /* must hold dev->bss_lock! */ | ||
57 | static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, | 142 | static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, |
58 | unsigned long expire_time) | 143 | unsigned long expire_time) |
59 | { | 144 | { |
60 | struct cfg80211_internal_bss *bss, *tmp; | 145 | struct cfg80211_internal_bss *bss, *tmp; |
61 | bool expired = false; | 146 | bool expired = false; |
62 | 147 | ||
148 | lockdep_assert_held(&dev->bss_lock); | ||
149 | |||
63 | list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { | 150 | list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { |
64 | if (atomic_read(&bss->hold)) | 151 | if (atomic_read(&bss->hold)) |
65 | continue; | 152 | continue; |
66 | if (!time_after(expire_time, bss->ts)) | 153 | if (!time_after(expire_time, bss->ts)) |
67 | continue; | 154 | continue; |
68 | 155 | ||
69 | __cfg80211_unlink_bss(dev, bss); | 156 | if (__cfg80211_unlink_bss(dev, bss)) |
70 | expired = true; | 157 | expired = true; |
71 | } | 158 | } |
72 | 159 | ||
73 | if (expired) | 160 | if (expired) |
@@ -234,15 +321,16 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, | |||
234 | return 0; | 321 | return 0; |
235 | } | 322 | } |
236 | 323 | ||
237 | /* must hold dev->bss_lock! */ | ||
238 | void cfg80211_bss_age(struct cfg80211_registered_device *dev, | 324 | void cfg80211_bss_age(struct cfg80211_registered_device *dev, |
239 | unsigned long age_secs) | 325 | unsigned long age_secs) |
240 | { | 326 | { |
241 | struct cfg80211_internal_bss *bss; | 327 | struct cfg80211_internal_bss *bss; |
242 | unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); | 328 | unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); |
243 | 329 | ||
330 | spin_lock_bh(&dev->bss_lock); | ||
244 | list_for_each_entry(bss, &dev->bss_list, list) | 331 | list_for_each_entry(bss, &dev->bss_list, list) |
245 | bss->ts -= age_jiffies; | 332 | bss->ts -= age_jiffies; |
333 | spin_unlock_bh(&dev->bss_lock); | ||
246 | } | 334 | } |
247 | 335 | ||
248 | void cfg80211_bss_expire(struct cfg80211_registered_device *dev) | 336 | void cfg80211_bss_expire(struct cfg80211_registered_device *dev) |
@@ -277,40 +365,24 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, | |||
277 | if (!pos) | 365 | if (!pos) |
278 | return NULL; | 366 | return NULL; |
279 | 367 | ||
280 | if (end - pos < sizeof(*ie)) | ||
281 | return NULL; | ||
282 | |||
283 | ie = (struct ieee80211_vendor_ie *)pos; | 368 | ie = (struct ieee80211_vendor_ie *)pos; |
369 | |||
370 | /* make sure we can access ie->len */ | ||
371 | BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1); | ||
372 | |||
373 | if (ie->len < sizeof(*ie)) | ||
374 | goto cont; | ||
375 | |||
284 | ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; | 376 | ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; |
285 | if (ie_oui == oui && ie->oui_type == oui_type) | 377 | if (ie_oui == oui && ie->oui_type == oui_type) |
286 | return pos; | 378 | return pos; |
287 | 379 | cont: | |
288 | pos += 2 + ie->len; | 380 | pos += 2 + ie->len; |
289 | } | 381 | } |
290 | return NULL; | 382 | return NULL; |
291 | } | 383 | } |
292 | EXPORT_SYMBOL(cfg80211_find_vendor_ie); | 384 | EXPORT_SYMBOL(cfg80211_find_vendor_ie); |
293 | 385 | ||
294 | static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2) | ||
295 | { | ||
296 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); | ||
297 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); | ||
298 | |||
299 | /* equal if both missing */ | ||
300 | if (!ie1 && !ie2) | ||
301 | return 0; | ||
302 | /* sort missing IE before (left of) present IE */ | ||
303 | if (!ie1) | ||
304 | return -1; | ||
305 | if (!ie2) | ||
306 | return 1; | ||
307 | |||
308 | /* sort by length first, then by contents */ | ||
309 | if (ie1[1] != ie2[1]) | ||
310 | return ie2[1] - ie1[1]; | ||
311 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
312 | } | ||
313 | |||
314 | static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, | 386 | static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, |
315 | const u8 *ssid, size_t ssid_len) | 387 | const u8 *ssid, size_t ssid_len) |
316 | { | 388 | { |
@@ -334,109 +406,30 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, | |||
334 | return memcmp(ssidie + 2, ssid, ssid_len) == 0; | 406 | return memcmp(ssidie + 2, ssid, ssid_len) == 0; |
335 | } | 407 | } |
336 | 408 | ||
337 | static bool is_mesh_bss(struct cfg80211_bss *a) | 409 | /** |
338 | { | 410 | * enum bss_compare_mode - BSS compare mode |
339 | const struct cfg80211_bss_ies *ies; | 411 | * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) |
340 | const u8 *ie; | 412 | * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode |
341 | 413 | * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode | |
342 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) | 414 | */ |
343 | return false; | 415 | enum bss_compare_mode { |
344 | 416 | BSS_CMP_REGULAR, | |
345 | ies = rcu_access_pointer(a->ies); | 417 | BSS_CMP_HIDE_ZLEN, |
346 | if (!ies) | 418 | BSS_CMP_HIDE_NUL, |
347 | return false; | 419 | }; |
348 | |||
349 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); | ||
350 | if (!ie) | ||
351 | return false; | ||
352 | |||
353 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); | ||
354 | if (!ie) | ||
355 | return false; | ||
356 | |||
357 | return true; | ||
358 | } | ||
359 | |||
360 | static bool is_mesh(struct cfg80211_bss *a, | ||
361 | const u8 *meshid, size_t meshidlen, | ||
362 | const u8 *meshcfg) | ||
363 | { | ||
364 | const struct cfg80211_bss_ies *ies; | ||
365 | const u8 *ie; | ||
366 | |||
367 | if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) | ||
368 | return false; | ||
369 | |||
370 | ies = rcu_access_pointer(a->ies); | ||
371 | if (!ies) | ||
372 | return false; | ||
373 | |||
374 | ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len); | ||
375 | if (!ie) | ||
376 | return false; | ||
377 | if (ie[1] != meshidlen) | ||
378 | return false; | ||
379 | if (memcmp(ie + 2, meshid, meshidlen)) | ||
380 | return false; | ||
381 | |||
382 | ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len); | ||
383 | if (!ie) | ||
384 | return false; | ||
385 | if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) | ||
386 | return false; | ||
387 | |||
388 | /* | ||
389 | * Ignore mesh capability (last two bytes of the IE) when | ||
390 | * comparing since that may differ between stations taking | ||
391 | * part in the same mesh. | ||
392 | */ | ||
393 | return memcmp(ie + 2, meshcfg, | ||
394 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; | ||
395 | } | ||
396 | 420 | ||
397 | static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) | 421 | static int cmp_bss(struct cfg80211_bss *a, |
422 | struct cfg80211_bss *b, | ||
423 | enum bss_compare_mode mode) | ||
398 | { | 424 | { |
399 | const struct cfg80211_bss_ies *a_ies, *b_ies; | 425 | const struct cfg80211_bss_ies *a_ies, *b_ies; |
400 | int r; | 426 | const u8 *ie1 = NULL; |
427 | const u8 *ie2 = NULL; | ||
428 | int i, r; | ||
401 | 429 | ||
402 | if (a->channel != b->channel) | 430 | if (a->channel != b->channel) |
403 | return b->channel->center_freq - a->channel->center_freq; | 431 | return b->channel->center_freq - a->channel->center_freq; |
404 | 432 | ||
405 | if (is_mesh_bss(a) && is_mesh_bss(b)) { | ||
406 | a_ies = rcu_access_pointer(a->ies); | ||
407 | if (!a_ies) | ||
408 | return -1; | ||
409 | b_ies = rcu_access_pointer(b->ies); | ||
410 | if (!b_ies) | ||
411 | return 1; | ||
412 | |||
413 | r = cmp_ies(WLAN_EID_MESH_ID, | ||
414 | a_ies->data, a_ies->len, | ||
415 | b_ies->data, b_ies->len); | ||
416 | if (r) | ||
417 | return r; | ||
418 | return cmp_ies(WLAN_EID_MESH_CONFIG, | ||
419 | a_ies->data, a_ies->len, | ||
420 | b_ies->data, b_ies->len); | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * we can't use compare_ether_addr here since we need a < > operator. | ||
425 | * The binary return value of compare_ether_addr isn't enough | ||
426 | */ | ||
427 | return memcmp(a->bssid, b->bssid, sizeof(a->bssid)); | ||
428 | } | ||
429 | |||
430 | static int cmp_bss(struct cfg80211_bss *a, | ||
431 | struct cfg80211_bss *b) | ||
432 | { | ||
433 | const struct cfg80211_bss_ies *a_ies, *b_ies; | ||
434 | int r; | ||
435 | |||
436 | r = cmp_bss_core(a, b); | ||
437 | if (r) | ||
438 | return r; | ||
439 | |||
440 | a_ies = rcu_access_pointer(a->ies); | 433 | a_ies = rcu_access_pointer(a->ies); |
441 | if (!a_ies) | 434 | if (!a_ies) |
442 | return -1; | 435 | return -1; |
@@ -444,42 +437,51 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
444 | if (!b_ies) | 437 | if (!b_ies) |
445 | return 1; | 438 | return 1; |
446 | 439 | ||
447 | return cmp_ies(WLAN_EID_SSID, | 440 | if (WLAN_CAPABILITY_IS_STA_BSS(a->capability)) |
448 | a_ies->data, a_ies->len, | 441 | ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID, |
449 | b_ies->data, b_ies->len); | 442 | a_ies->data, a_ies->len); |
450 | } | 443 | if (WLAN_CAPABILITY_IS_STA_BSS(b->capability)) |
451 | 444 | ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID, | |
452 | static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) | 445 | b_ies->data, b_ies->len); |
453 | { | 446 | if (ie1 && ie2) { |
454 | const struct cfg80211_bss_ies *a_ies, *b_ies; | 447 | int mesh_id_cmp; |
455 | const u8 *ie1; | 448 | |
456 | const u8 *ie2; | 449 | if (ie1[1] == ie2[1]) |
457 | int i; | 450 | mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]); |
458 | int r; | 451 | else |
452 | mesh_id_cmp = ie2[1] - ie1[1]; | ||
453 | |||
454 | ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, | ||
455 | a_ies->data, a_ies->len); | ||
456 | ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, | ||
457 | b_ies->data, b_ies->len); | ||
458 | if (ie1 && ie2) { | ||
459 | if (mesh_id_cmp) | ||
460 | return mesh_id_cmp; | ||
461 | if (ie1[1] != ie2[1]) | ||
462 | return ie2[1] - ie1[1]; | ||
463 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
464 | } | ||
465 | } | ||
459 | 466 | ||
460 | r = cmp_bss_core(a, b); | 467 | /* |
468 | * we can't use compare_ether_addr here since we need a < > operator. | ||
469 | * The binary return value of compare_ether_addr isn't enough | ||
470 | */ | ||
471 | r = memcmp(a->bssid, b->bssid, sizeof(a->bssid)); | ||
461 | if (r) | 472 | if (r) |
462 | return r; | 473 | return r; |
463 | 474 | ||
464 | a_ies = rcu_access_pointer(a->ies); | ||
465 | if (!a_ies) | ||
466 | return -1; | ||
467 | b_ies = rcu_access_pointer(b->ies); | ||
468 | if (!b_ies) | ||
469 | return 1; | ||
470 | |||
471 | ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); | 475 | ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len); |
472 | ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); | 476 | ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len); |
473 | 477 | ||
478 | if (!ie1 && !ie2) | ||
479 | return 0; | ||
480 | |||
474 | /* | 481 | /* |
475 | * Key comparator must use same algorithm in any rb-tree | 482 | * Note that with "hide_ssid", the function returns a match if |
476 | * search function (order is important), otherwise ordering | 483 | * the already-present BSS ("b") is a hidden SSID beacon for |
477 | * of items in the tree is broken and search gives incorrect | 484 | * the new BSS ("a"). |
478 | * results. This code uses same order as cmp_ies() does. | ||
479 | * | ||
480 | * Note that due to the differring behaviour with hidden SSIDs | ||
481 | * this function only works when "b" is the tree element and | ||
482 | * "a" is the key we're looking for. | ||
483 | */ | 485 | */ |
484 | 486 | ||
485 | /* sort missing IE before (left of) present IE */ | 487 | /* sort missing IE before (left of) present IE */ |
@@ -488,24 +490,36 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) | |||
488 | if (!ie2) | 490 | if (!ie2) |
489 | return 1; | 491 | return 1; |
490 | 492 | ||
491 | /* zero-size SSID is used as an indication of the hidden bss */ | 493 | switch (mode) { |
492 | if (!ie2[1]) | 494 | case BSS_CMP_HIDE_ZLEN: |
495 | /* | ||
496 | * In ZLEN mode we assume the BSS entry we're | ||
497 | * looking for has a zero-length SSID. So if | ||
498 | * the one we're looking at right now has that, | ||
499 | * return 0. Otherwise, return the difference | ||
500 | * in length, but since we're looking for the | ||
501 | * 0-length it's really equivalent to returning | ||
502 | * the length of the one we're looking at. | ||
503 | * | ||
504 | * No content comparison is needed as we assume | ||
505 | * the content length is zero. | ||
506 | */ | ||
507 | return ie2[1]; | ||
508 | case BSS_CMP_REGULAR: | ||
509 | default: | ||
510 | /* sort by length first, then by contents */ | ||
511 | if (ie1[1] != ie2[1]) | ||
512 | return ie2[1] - ie1[1]; | ||
513 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
514 | case BSS_CMP_HIDE_NUL: | ||
515 | if (ie1[1] != ie2[1]) | ||
516 | return ie2[1] - ie1[1]; | ||
517 | /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */ | ||
518 | for (i = 0; i < ie2[1]; i++) | ||
519 | if (ie2[i + 2]) | ||
520 | return -1; | ||
493 | return 0; | 521 | return 0; |
494 | 522 | } | |
495 | /* sort by length first, then by contents */ | ||
496 | if (ie1[1] != ie2[1]) | ||
497 | return ie2[1] - ie1[1]; | ||
498 | |||
499 | /* | ||
500 | * zeroed SSID ie is another indication of a hidden bss; | ||
501 | * if it isn't zeroed just return the regular sort value | ||
502 | * to find the next candidate | ||
503 | */ | ||
504 | for (i = 0; i < ie2[1]; i++) | ||
505 | if (ie2[i + 2]) | ||
506 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); | ||
507 | |||
508 | return 0; | ||
509 | } | 523 | } |
510 | 524 | ||
511 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | 525 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, |
@@ -534,7 +548,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | |||
534 | continue; | 548 | continue; |
535 | if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { | 549 | if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { |
536 | res = bss; | 550 | res = bss; |
537 | kref_get(&res->ref); | 551 | bss_ref_get(dev, res); |
538 | break; | 552 | break; |
539 | } | 553 | } |
540 | } | 554 | } |
@@ -547,34 +561,6 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | |||
547 | } | 561 | } |
548 | EXPORT_SYMBOL(cfg80211_get_bss); | 562 | EXPORT_SYMBOL(cfg80211_get_bss); |
549 | 563 | ||
550 | struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, | ||
551 | struct ieee80211_channel *channel, | ||
552 | const u8 *meshid, size_t meshidlen, | ||
553 | const u8 *meshcfg) | ||
554 | { | ||
555 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
556 | struct cfg80211_internal_bss *bss, *res = NULL; | ||
557 | |||
558 | spin_lock_bh(&dev->bss_lock); | ||
559 | |||
560 | list_for_each_entry(bss, &dev->bss_list, list) { | ||
561 | if (channel && bss->pub.channel != channel) | ||
562 | continue; | ||
563 | if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { | ||
564 | res = bss; | ||
565 | kref_get(&res->ref); | ||
566 | break; | ||
567 | } | ||
568 | } | ||
569 | |||
570 | spin_unlock_bh(&dev->bss_lock); | ||
571 | if (!res) | ||
572 | return NULL; | ||
573 | return &res->pub; | ||
574 | } | ||
575 | EXPORT_SYMBOL(cfg80211_get_mesh); | ||
576 | |||
577 | |||
578 | static void rb_insert_bss(struct cfg80211_registered_device *dev, | 564 | static void rb_insert_bss(struct cfg80211_registered_device *dev, |
579 | struct cfg80211_internal_bss *bss) | 565 | struct cfg80211_internal_bss *bss) |
580 | { | 566 | { |
@@ -587,7 +573,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, | |||
587 | parent = *p; | 573 | parent = *p; |
588 | tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); | 574 | tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); |
589 | 575 | ||
590 | cmp = cmp_bss(&bss->pub, &tbss->pub); | 576 | cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR); |
591 | 577 | ||
592 | if (WARN_ON(!cmp)) { | 578 | if (WARN_ON(!cmp)) { |
593 | /* will sort of leak this BSS */ | 579 | /* will sort of leak this BSS */ |
@@ -606,7 +592,8 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev, | |||
606 | 592 | ||
607 | static struct cfg80211_internal_bss * | 593 | static struct cfg80211_internal_bss * |
608 | rb_find_bss(struct cfg80211_registered_device *dev, | 594 | rb_find_bss(struct cfg80211_registered_device *dev, |
609 | struct cfg80211_internal_bss *res) | 595 | struct cfg80211_internal_bss *res, |
596 | enum bss_compare_mode mode) | ||
610 | { | 597 | { |
611 | struct rb_node *n = dev->bss_tree.rb_node; | 598 | struct rb_node *n = dev->bss_tree.rb_node; |
612 | struct cfg80211_internal_bss *bss; | 599 | struct cfg80211_internal_bss *bss; |
@@ -614,7 +601,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
614 | 601 | ||
615 | while (n) { | 602 | while (n) { |
616 | bss = rb_entry(n, struct cfg80211_internal_bss, rbn); | 603 | bss = rb_entry(n, struct cfg80211_internal_bss, rbn); |
617 | r = cmp_bss(&res->pub, &bss->pub); | 604 | r = cmp_bss(&res->pub, &bss->pub, mode); |
618 | 605 | ||
619 | if (r == 0) | 606 | if (r == 0) |
620 | return bss; | 607 | return bss; |
@@ -627,46 +614,67 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
627 | return NULL; | 614 | return NULL; |
628 | } | 615 | } |
629 | 616 | ||
630 | static struct cfg80211_internal_bss * | 617 | static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, |
631 | rb_find_hidden_bss(struct cfg80211_registered_device *dev, | 618 | struct cfg80211_internal_bss *new) |
632 | struct cfg80211_internal_bss *res) | ||
633 | { | 619 | { |
634 | struct rb_node *n = dev->bss_tree.rb_node; | 620 | const struct cfg80211_bss_ies *ies; |
635 | struct cfg80211_internal_bss *bss; | 621 | struct cfg80211_internal_bss *bss; |
636 | int r; | 622 | const u8 *ie; |
623 | int i, ssidlen; | ||
624 | u8 fold = 0; | ||
637 | 625 | ||
638 | while (n) { | 626 | ies = rcu_access_pointer(new->pub.beacon_ies); |
639 | bss = rb_entry(n, struct cfg80211_internal_bss, rbn); | 627 | if (WARN_ON(!ies)) |
640 | r = cmp_hidden_bss(&res->pub, &bss->pub); | 628 | return false; |
641 | 629 | ||
642 | if (r == 0) | 630 | ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); |
643 | return bss; | 631 | if (!ie) { |
644 | else if (r < 0) | 632 | /* nothing to do */ |
645 | n = n->rb_left; | 633 | return true; |
646 | else | ||
647 | n = n->rb_right; | ||
648 | } | 634 | } |
649 | 635 | ||
650 | return NULL; | 636 | ssidlen = ie[1]; |
651 | } | 637 | for (i = 0; i < ssidlen; i++) |
638 | fold |= ie[2 + i]; | ||
652 | 639 | ||
653 | static void | 640 | if (fold) { |
654 | copy_hidden_ies(struct cfg80211_internal_bss *res, | 641 | /* not a hidden SSID */ |
655 | struct cfg80211_internal_bss *hidden) | 642 | return true; |
656 | { | 643 | } |
657 | const struct cfg80211_bss_ies *ies; | ||
658 | 644 | ||
659 | if (rcu_access_pointer(res->pub.beacon_ies)) | 645 | /* This is the bad part ... */ |
660 | return; | ||
661 | 646 | ||
662 | ies = rcu_access_pointer(hidden->pub.beacon_ies); | 647 | list_for_each_entry(bss, &dev->bss_list, list) { |
663 | if (WARN_ON(!ies)) | 648 | if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid)) |
664 | return; | 649 | continue; |
650 | if (bss->pub.channel != new->pub.channel) | ||
651 | continue; | ||
652 | if (rcu_access_pointer(bss->pub.beacon_ies)) | ||
653 | continue; | ||
654 | ies = rcu_access_pointer(bss->pub.ies); | ||
655 | if (!ies) | ||
656 | continue; | ||
657 | ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len); | ||
658 | if (!ie) | ||
659 | continue; | ||
660 | if (ssidlen && ie[1] != ssidlen) | ||
661 | continue; | ||
662 | /* that would be odd ... */ | ||
663 | if (bss->pub.beacon_ies) | ||
664 | continue; | ||
665 | if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss)) | ||
666 | continue; | ||
667 | if (WARN_ON_ONCE(!list_empty(&bss->hidden_list))) | ||
668 | list_del(&bss->hidden_list); | ||
669 | /* combine them */ | ||
670 | list_add(&bss->hidden_list, &new->hidden_list); | ||
671 | bss->pub.hidden_beacon_bss = &new->pub; | ||
672 | new->refcount += bss->refcount; | ||
673 | rcu_assign_pointer(bss->pub.beacon_ies, | ||
674 | new->pub.beacon_ies); | ||
675 | } | ||
665 | 676 | ||
666 | ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC); | 677 | return true; |
667 | if (unlikely(!ies)) | ||
668 | return; | ||
669 | rcu_assign_pointer(res->pub.beacon_ies, ies); | ||
670 | } | 678 | } |
671 | 679 | ||
672 | static struct cfg80211_internal_bss * | 680 | static struct cfg80211_internal_bss * |
@@ -687,11 +695,10 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
687 | return NULL; | 695 | return NULL; |
688 | } | 696 | } |
689 | 697 | ||
690 | found = rb_find_bss(dev, tmp); | 698 | found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR); |
691 | 699 | ||
692 | if (found) { | 700 | if (found) { |
693 | found->pub.beacon_interval = tmp->pub.beacon_interval; | 701 | found->pub.beacon_interval = tmp->pub.beacon_interval; |
694 | found->pub.tsf = tmp->pub.tsf; | ||
695 | found->pub.signal = tmp->pub.signal; | 702 | found->pub.signal = tmp->pub.signal; |
696 | found->pub.capability = tmp->pub.capability; | 703 | found->pub.capability = tmp->pub.capability; |
697 | found->ts = tmp->ts; | 704 | found->ts = tmp->ts; |
@@ -711,19 +718,45 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
711 | kfree_rcu((struct cfg80211_bss_ies *)old, | 718 | kfree_rcu((struct cfg80211_bss_ies *)old, |
712 | rcu_head); | 719 | rcu_head); |
713 | } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { | 720 | } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { |
714 | const struct cfg80211_bss_ies *old, *ies; | 721 | const struct cfg80211_bss_ies *old; |
722 | struct cfg80211_internal_bss *bss; | ||
723 | |||
724 | if (found->pub.hidden_beacon_bss && | ||
725 | !list_empty(&found->hidden_list)) { | ||
726 | /* | ||
727 | * The found BSS struct is one of the probe | ||
728 | * response members of a group, but we're | ||
729 | * receiving a beacon (beacon_ies in the tmp | ||
730 | * bss is used). This can only mean that the | ||
731 | * AP changed its beacon from not having an | ||
732 | * SSID to showing it, which is confusing so | ||
733 | * drop this information. | ||
734 | */ | ||
735 | goto drop; | ||
736 | } | ||
715 | 737 | ||
716 | old = rcu_access_pointer(found->pub.beacon_ies); | 738 | old = rcu_access_pointer(found->pub.beacon_ies); |
717 | ies = rcu_access_pointer(found->pub.ies); | ||
718 | 739 | ||
719 | rcu_assign_pointer(found->pub.beacon_ies, | 740 | rcu_assign_pointer(found->pub.beacon_ies, |
720 | tmp->pub.beacon_ies); | 741 | tmp->pub.beacon_ies); |
721 | 742 | ||
722 | /* Override IEs if they were from a beacon before */ | 743 | /* Override IEs if they were from a beacon before */ |
723 | if (old == ies) | 744 | if (old == rcu_access_pointer(found->pub.ies)) |
724 | rcu_assign_pointer(found->pub.ies, | 745 | rcu_assign_pointer(found->pub.ies, |
725 | tmp->pub.beacon_ies); | 746 | tmp->pub.beacon_ies); |
726 | 747 | ||
748 | /* Assign beacon IEs to all sub entries */ | ||
749 | list_for_each_entry(bss, &found->hidden_list, | ||
750 | hidden_list) { | ||
751 | const struct cfg80211_bss_ies *ies; | ||
752 | |||
753 | ies = rcu_access_pointer(bss->pub.beacon_ies); | ||
754 | WARN_ON(ies != old); | ||
755 | |||
756 | rcu_assign_pointer(bss->pub.beacon_ies, | ||
757 | tmp->pub.beacon_ies); | ||
758 | } | ||
759 | |||
727 | if (old) | 760 | if (old) |
728 | kfree_rcu((struct cfg80211_bss_ies *)old, | 761 | kfree_rcu((struct cfg80211_bss_ies *)old, |
729 | rcu_head); | 762 | rcu_head); |
@@ -733,19 +766,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
733 | struct cfg80211_internal_bss *hidden; | 766 | struct cfg80211_internal_bss *hidden; |
734 | struct cfg80211_bss_ies *ies; | 767 | struct cfg80211_bss_ies *ies; |
735 | 768 | ||
736 | /* First check if the beacon is a probe response from | ||
737 | * a hidden bss. If so, copy beacon ies (with nullified | ||
738 | * ssid) into the probe response bss entry (with real ssid). | ||
739 | * It is required basically for PSM implementation | ||
740 | * (probe responses do not contain tim ie) */ | ||
741 | |||
742 | /* TODO: The code is not trying to update existing probe | ||
743 | * response bss entries when beacon ies are | ||
744 | * getting changed. */ | ||
745 | hidden = rb_find_hidden_bss(dev, tmp); | ||
746 | if (hidden) | ||
747 | copy_hidden_ies(tmp, hidden); | ||
748 | |||
749 | /* | 769 | /* |
750 | * create a copy -- the "res" variable that is passed in | 770 | * create a copy -- the "res" variable that is passed in |
751 | * is allocated on the stack since it's not needed in the | 771 | * is allocated on the stack since it's not needed in the |
@@ -760,21 +780,51 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
760 | ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); | 780 | ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); |
761 | if (ies) | 781 | if (ies) |
762 | kfree_rcu(ies, rcu_head); | 782 | kfree_rcu(ies, rcu_head); |
763 | spin_unlock_bh(&dev->bss_lock); | 783 | goto drop; |
764 | return NULL; | ||
765 | } | 784 | } |
766 | memcpy(new, tmp, sizeof(*new)); | 785 | memcpy(new, tmp, sizeof(*new)); |
767 | kref_init(&new->ref); | 786 | new->refcount = 1; |
787 | INIT_LIST_HEAD(&new->hidden_list); | ||
788 | |||
789 | if (rcu_access_pointer(tmp->pub.proberesp_ies)) { | ||
790 | hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN); | ||
791 | if (!hidden) | ||
792 | hidden = rb_find_bss(dev, tmp, | ||
793 | BSS_CMP_HIDE_NUL); | ||
794 | if (hidden) { | ||
795 | new->pub.hidden_beacon_bss = &hidden->pub; | ||
796 | list_add(&new->hidden_list, | ||
797 | &hidden->hidden_list); | ||
798 | hidden->refcount++; | ||
799 | rcu_assign_pointer(new->pub.beacon_ies, | ||
800 | hidden->pub.beacon_ies); | ||
801 | } | ||
802 | } else { | ||
803 | /* | ||
804 | * Ok so we found a beacon, and don't have an entry. If | ||
805 | * it's a beacon with hidden SSID, we might be in for an | ||
806 | * expensive search for any probe responses that should | ||
807 | * be grouped with this beacon for updates ... | ||
808 | */ | ||
809 | if (!cfg80211_combine_bsses(dev, new)) { | ||
810 | kfree(new); | ||
811 | goto drop; | ||
812 | } | ||
813 | } | ||
814 | |||
768 | list_add_tail(&new->list, &dev->bss_list); | 815 | list_add_tail(&new->list, &dev->bss_list); |
769 | rb_insert_bss(dev, new); | 816 | rb_insert_bss(dev, new); |
770 | found = new; | 817 | found = new; |
771 | } | 818 | } |
772 | 819 | ||
773 | dev->bss_generation++; | 820 | dev->bss_generation++; |
821 | bss_ref_get(dev, found); | ||
774 | spin_unlock_bh(&dev->bss_lock); | 822 | spin_unlock_bh(&dev->bss_lock); |
775 | 823 | ||
776 | kref_get(&found->ref); | ||
777 | return found; | 824 | return found; |
825 | drop: | ||
826 | spin_unlock_bh(&dev->bss_lock); | ||
827 | return NULL; | ||
778 | } | 828 | } |
779 | 829 | ||
780 | static struct ieee80211_channel * | 830 | static struct ieee80211_channel * |
@@ -833,7 +883,6 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
833 | memcpy(tmp.pub.bssid, bssid, ETH_ALEN); | 883 | memcpy(tmp.pub.bssid, bssid, ETH_ALEN); |
834 | tmp.pub.channel = channel; | 884 | tmp.pub.channel = channel; |
835 | tmp.pub.signal = signal; | 885 | tmp.pub.signal = signal; |
836 | tmp.pub.tsf = tsf; | ||
837 | tmp.pub.beacon_interval = beacon_interval; | 886 | tmp.pub.beacon_interval = beacon_interval; |
838 | tmp.pub.capability = capability; | 887 | tmp.pub.capability = capability; |
839 | /* | 888 | /* |
@@ -841,16 +890,14 @@ cfg80211_inform_bss(struct wiphy *wiphy, | |||
841 | * Response frame, we need to pick one of the options and only use it | 890 | * Response frame, we need to pick one of the options and only use it |
842 | * with the driver that does not provide the full Beacon/Probe Response | 891 | * with the driver that does not provide the full Beacon/Probe Response |
843 | * frame. Use Beacon frame pointer to avoid indicating that this should | 892 | * frame. Use Beacon frame pointer to avoid indicating that this should |
844 | * override the iies pointer should we have received an earlier | 893 | * override the IEs pointer should we have received an earlier |
845 | * indication of Probe Response data. | 894 | * indication of Probe Response data. |
846 | * | ||
847 | * The initial buffer for the IEs is allocated with the BSS entry and | ||
848 | * is located after the private area. | ||
849 | */ | 895 | */ |
850 | ies = kmalloc(sizeof(*ies) + ielen, gfp); | 896 | ies = kmalloc(sizeof(*ies) + ielen, gfp); |
851 | if (!ies) | 897 | if (!ies) |
852 | return NULL; | 898 | return NULL; |
853 | ies->len = ielen; | 899 | ies->len = ielen; |
900 | ies->tsf = tsf; | ||
854 | memcpy(ies->data, ie, ielen); | 901 | memcpy(ies->data, ie, ielen); |
855 | 902 | ||
856 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); | 903 | rcu_assign_pointer(tmp.pub.beacon_ies, ies); |
@@ -907,6 +954,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
907 | if (!ies) | 954 | if (!ies) |
908 | return NULL; | 955 | return NULL; |
909 | ies->len = ielen; | 956 | ies->len = ielen; |
957 | ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | ||
910 | memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); | 958 | memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); |
911 | 959 | ||
912 | if (ieee80211_is_probe_resp(mgmt->frame_control)) | 960 | if (ieee80211_is_probe_resp(mgmt->frame_control)) |
@@ -918,7 +966,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
918 | memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); | 966 | memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); |
919 | tmp.pub.channel = channel; | 967 | tmp.pub.channel = channel; |
920 | tmp.pub.signal = signal; | 968 | tmp.pub.signal = signal; |
921 | tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); | ||
922 | tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); | 969 | tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); |
923 | tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); | 970 | tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); |
924 | 971 | ||
@@ -935,27 +982,35 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, | |||
935 | } | 982 | } |
936 | EXPORT_SYMBOL(cfg80211_inform_bss_frame); | 983 | EXPORT_SYMBOL(cfg80211_inform_bss_frame); |
937 | 984 | ||
938 | void cfg80211_ref_bss(struct cfg80211_bss *pub) | 985 | void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) |
939 | { | 986 | { |
987 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
940 | struct cfg80211_internal_bss *bss; | 988 | struct cfg80211_internal_bss *bss; |
941 | 989 | ||
942 | if (!pub) | 990 | if (!pub) |
943 | return; | 991 | return; |
944 | 992 | ||
945 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | 993 | bss = container_of(pub, struct cfg80211_internal_bss, pub); |
946 | kref_get(&bss->ref); | 994 | |
995 | spin_lock_bh(&dev->bss_lock); | ||
996 | bss_ref_get(dev, bss); | ||
997 | spin_unlock_bh(&dev->bss_lock); | ||
947 | } | 998 | } |
948 | EXPORT_SYMBOL(cfg80211_ref_bss); | 999 | EXPORT_SYMBOL(cfg80211_ref_bss); |
949 | 1000 | ||
950 | void cfg80211_put_bss(struct cfg80211_bss *pub) | 1001 | void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) |
951 | { | 1002 | { |
1003 | struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); | ||
952 | struct cfg80211_internal_bss *bss; | 1004 | struct cfg80211_internal_bss *bss; |
953 | 1005 | ||
954 | if (!pub) | 1006 | if (!pub) |
955 | return; | 1007 | return; |
956 | 1008 | ||
957 | bss = container_of(pub, struct cfg80211_internal_bss, pub); | 1009 | bss = container_of(pub, struct cfg80211_internal_bss, pub); |
958 | kref_put(&bss->ref, bss_release); | 1010 | |
1011 | spin_lock_bh(&dev->bss_lock); | ||
1012 | bss_ref_put(dev, bss); | ||
1013 | spin_unlock_bh(&dev->bss_lock); | ||
959 | } | 1014 | } |
960 | EXPORT_SYMBOL(cfg80211_put_bss); | 1015 | EXPORT_SYMBOL(cfg80211_put_bss); |
961 | 1016 | ||
@@ -971,8 +1026,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) | |||
971 | 1026 | ||
972 | spin_lock_bh(&dev->bss_lock); | 1027 | spin_lock_bh(&dev->bss_lock); |
973 | if (!list_empty(&bss->list)) { | 1028 | if (!list_empty(&bss->list)) { |
974 | __cfg80211_unlink_bss(dev, bss); | 1029 | if (__cfg80211_unlink_bss(dev, bss)) |
975 | dev->bss_generation++; | 1030 | dev->bss_generation++; |
976 | } | 1031 | } |
977 | spin_unlock_bh(&dev->bss_lock); | 1032 | spin_unlock_bh(&dev->bss_lock); |
978 | } | 1033 | } |
@@ -1155,16 +1210,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info, | |||
1155 | } | 1210 | } |
1156 | } | 1211 | } |
1157 | 1212 | ||
1158 | static inline unsigned int elapsed_jiffies_msecs(unsigned long start) | ||
1159 | { | ||
1160 | unsigned long end = jiffies; | ||
1161 | |||
1162 | if (end >= start) | ||
1163 | return jiffies_to_msecs(end - start); | ||
1164 | |||
1165 | return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); | ||
1166 | } | ||
1167 | |||
1168 | static char * | 1213 | static char * |
1169 | ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | 1214 | ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, |
1170 | struct cfg80211_internal_bss *bss, char *current_ev, | 1215 | struct cfg80211_internal_bss *bss, char *current_ev, |
@@ -1241,15 +1286,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1241 | 1286 | ||
1242 | rcu_read_lock(); | 1287 | rcu_read_lock(); |
1243 | ies = rcu_dereference(bss->pub.ies); | 1288 | ies = rcu_dereference(bss->pub.ies); |
1244 | if (ies) { | 1289 | rem = ies->len; |
1245 | rem = ies->len; | 1290 | ie = ies->data; |
1246 | ie = ies->data; | ||
1247 | } else { | ||
1248 | rem = 0; | ||
1249 | ie = NULL; | ||
1250 | } | ||
1251 | 1291 | ||
1252 | while (ies && rem >= 2) { | 1292 | while (rem >= 2) { |
1253 | /* invalid data */ | 1293 | /* invalid data */ |
1254 | if (ie[1] > rem - 2) | 1294 | if (ie[1] > rem - 2) |
1255 | break; | 1295 | break; |
@@ -1362,7 +1402,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, | |||
1362 | if (buf) { | 1402 | if (buf) { |
1363 | memset(&iwe, 0, sizeof(iwe)); | 1403 | memset(&iwe, 0, sizeof(iwe)); |
1364 | iwe.cmd = IWEVCUSTOM; | 1404 | iwe.cmd = IWEVCUSTOM; |
1365 | sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); | 1405 | sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf)); |
1366 | iwe.u.data.length = strlen(buf); | 1406 | iwe.u.data.length = strlen(buf); |
1367 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | 1407 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, |
1368 | &iwe, buf); | 1408 | &iwe, buf); |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index a825dfe12cf7..f432bd3755b1 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -301,7 +301,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) | |||
301 | 301 | ||
302 | bss = cfg80211_get_conn_bss(wdev); | 302 | bss = cfg80211_get_conn_bss(wdev); |
303 | if (bss) { | 303 | if (bss) { |
304 | cfg80211_put_bss(bss); | 304 | cfg80211_put_bss(&rdev->wiphy, bss); |
305 | } else { | 305 | } else { |
306 | /* not found */ | 306 | /* not found */ |
307 | if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) | 307 | if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) |
@@ -464,7 +464,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
464 | 464 | ||
465 | if (wdev->current_bss) { | 465 | if (wdev->current_bss) { |
466 | cfg80211_unhold_bss(wdev->current_bss); | 466 | cfg80211_unhold_bss(wdev->current_bss); |
467 | cfg80211_put_bss(&wdev->current_bss->pub); | 467 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
468 | wdev->current_bss = NULL; | 468 | wdev->current_bss = NULL; |
469 | } | 469 | } |
470 | 470 | ||
@@ -480,7 +480,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
480 | kfree(wdev->connect_keys); | 480 | kfree(wdev->connect_keys); |
481 | wdev->connect_keys = NULL; | 481 | wdev->connect_keys = NULL; |
482 | wdev->ssid_len = 0; | 482 | wdev->ssid_len = 0; |
483 | cfg80211_put_bss(bss); | 483 | cfg80211_put_bss(wdev->wiphy, bss); |
484 | return; | 484 | return; |
485 | } | 485 | } |
486 | 486 | ||
@@ -586,7 +586,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, | |||
586 | } | 586 | } |
587 | 587 | ||
588 | cfg80211_unhold_bss(wdev->current_bss); | 588 | cfg80211_unhold_bss(wdev->current_bss); |
589 | cfg80211_put_bss(&wdev->current_bss->pub); | 589 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
590 | wdev->current_bss = NULL; | 590 | wdev->current_bss = NULL; |
591 | 591 | ||
592 | cfg80211_hold_bss(bss_from_pub(bss)); | 592 | cfg80211_hold_bss(bss_from_pub(bss)); |
@@ -621,7 +621,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, | |||
621 | 621 | ||
622 | return; | 622 | return; |
623 | out: | 623 | out: |
624 | cfg80211_put_bss(bss); | 624 | cfg80211_put_bss(wdev->wiphy, bss); |
625 | } | 625 | } |
626 | 626 | ||
627 | void cfg80211_roamed(struct net_device *dev, | 627 | void cfg80211_roamed(struct net_device *dev, |
@@ -663,7 +663,7 @@ void cfg80211_roamed_bss(struct net_device *dev, | |||
663 | 663 | ||
664 | ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); | 664 | ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); |
665 | if (!ev) { | 665 | if (!ev) { |
666 | cfg80211_put_bss(bss); | 666 | cfg80211_put_bss(wdev->wiphy, bss); |
667 | return; | 667 | return; |
668 | } | 668 | } |
669 | 669 | ||
@@ -704,7 +704,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
704 | 704 | ||
705 | if (wdev->current_bss) { | 705 | if (wdev->current_bss) { |
706 | cfg80211_unhold_bss(wdev->current_bss); | 706 | cfg80211_unhold_bss(wdev->current_bss); |
707 | cfg80211_put_bss(&wdev->current_bss->pub); | 707 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
708 | } | 708 | } |
709 | 709 | ||
710 | wdev->current_bss = NULL; | 710 | wdev->current_bss = NULL; |
@@ -875,7 +875,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
875 | if (bss) { | 875 | if (bss) { |
876 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | 876 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; |
877 | err = cfg80211_conn_do_work(wdev); | 877 | err = cfg80211_conn_do_work(wdev); |
878 | cfg80211_put_bss(bss); | 878 | cfg80211_put_bss(wdev->wiphy, bss); |
879 | } else { | 879 | } else { |
880 | /* otherwise we'll need to scan for the AP first */ | 880 | /* otherwise we'll need to scan for the AP first */ |
881 | err = cfg80211_conn_scan(wdev); | 881 | err = cfg80211_conn_scan(wdev); |
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 1f6f01e2dc4c..238ee49b3868 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c | |||
@@ -106,9 +106,7 @@ static int wiphy_resume(struct device *dev) | |||
106 | int ret = 0; | 106 | int ret = 0; |
107 | 107 | ||
108 | /* Age scan results with time spent in suspend */ | 108 | /* Age scan results with time spent in suspend */ |
109 | spin_lock_bh(&rdev->bss_lock); | ||
110 | cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); | 109 | cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); |
111 | spin_unlock_bh(&rdev->bss_lock); | ||
112 | 110 | ||
113 | if (rdev->ops->resume) { | 111 | if (rdev->ops->resume) { |
114 | rtnl_lock(); | 112 | rtnl_lock(); |
diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 8bc553199686..b7a531380e19 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h | |||
@@ -2051,6 +2051,21 @@ TRACE_EVENT(cfg80211_reg_can_beacon, | |||
2051 | WIPHY_PR_ARG, CHAN_DEF_PR_ARG) | 2051 | WIPHY_PR_ARG, CHAN_DEF_PR_ARG) |
2052 | ); | 2052 | ); |
2053 | 2053 | ||
2054 | TRACE_EVENT(cfg80211_chandef_dfs_required, | ||
2055 | TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), | ||
2056 | TP_ARGS(wiphy, chandef), | ||
2057 | TP_STRUCT__entry( | ||
2058 | WIPHY_ENTRY | ||
2059 | CHAN_DEF_ENTRY | ||
2060 | ), | ||
2061 | TP_fast_assign( | ||
2062 | WIPHY_ASSIGN; | ||
2063 | CHAN_DEF_ASSIGN(chandef); | ||
2064 | ), | ||
2065 | TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, | ||
2066 | WIPHY_PR_ARG, CHAN_DEF_PR_ARG) | ||
2067 | ); | ||
2068 | |||
2054 | TRACE_EVENT(cfg80211_ch_switch_notify, | 2069 | TRACE_EVENT(cfg80211_ch_switch_notify, |
2055 | TP_PROTO(struct net_device *netdev, | 2070 | TP_PROTO(struct net_device *netdev, |
2056 | struct cfg80211_chan_def *chandef), | 2071 | struct cfg80211_chan_def *chandef), |
@@ -2067,6 +2082,36 @@ TRACE_EVENT(cfg80211_ch_switch_notify, | |||
2067 | NETDEV_PR_ARG, CHAN_DEF_PR_ARG) | 2082 | NETDEV_PR_ARG, CHAN_DEF_PR_ARG) |
2068 | ); | 2083 | ); |
2069 | 2084 | ||
2085 | TRACE_EVENT(cfg80211_radar_event, | ||
2086 | TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), | ||
2087 | TP_ARGS(wiphy, chandef), | ||
2088 | TP_STRUCT__entry( | ||
2089 | WIPHY_ENTRY | ||
2090 | CHAN_DEF_ENTRY | ||
2091 | ), | ||
2092 | TP_fast_assign( | ||
2093 | WIPHY_ASSIGN; | ||
2094 | CHAN_DEF_ASSIGN(chandef); | ||
2095 | ), | ||
2096 | TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, | ||
2097 | WIPHY_PR_ARG, CHAN_DEF_PR_ARG) | ||
2098 | ); | ||
2099 | |||
2100 | TRACE_EVENT(cfg80211_cac_event, | ||
2101 | TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt), | ||
2102 | TP_ARGS(netdev, evt), | ||
2103 | TP_STRUCT__entry( | ||
2104 | NETDEV_ENTRY | ||
2105 | __field(enum nl80211_radar_event, evt) | ||
2106 | ), | ||
2107 | TP_fast_assign( | ||
2108 | NETDEV_ASSIGN; | ||
2109 | __entry->evt = evt; | ||
2110 | ), | ||
2111 | TP_printk(NETDEV_PR_FMT ", event: %d", | ||
2112 | NETDEV_PR_ARG, __entry->evt) | ||
2113 | ); | ||
2114 | |||
2070 | DECLARE_EVENT_CLASS(cfg80211_rx_evt, | 2115 | DECLARE_EVENT_CLASS(cfg80211_rx_evt, |
2071 | TP_PROTO(struct net_device *netdev, const u8 *addr), | 2116 | TP_PROTO(struct net_device *netdev, const u8 *addr), |
2072 | TP_ARGS(netdev, addr), | 2117 | TP_ARGS(netdev, addr), |
@@ -2333,6 +2378,41 @@ TRACE_EVENT(cfg80211_return_u32, | |||
2333 | TP_printk("ret: %u", __entry->ret) | 2378 | TP_printk("ret: %u", __entry->ret) |
2334 | ); | 2379 | ); |
2335 | 2380 | ||
2381 | TRACE_EVENT(cfg80211_report_wowlan_wakeup, | ||
2382 | TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, | ||
2383 | struct cfg80211_wowlan_wakeup *wakeup), | ||
2384 | TP_ARGS(wiphy, wdev, wakeup), | ||
2385 | TP_STRUCT__entry( | ||
2386 | WIPHY_ENTRY | ||
2387 | WDEV_ENTRY | ||
2388 | __field(bool, disconnect) | ||
2389 | __field(bool, magic_pkt) | ||
2390 | __field(bool, gtk_rekey_failure) | ||
2391 | __field(bool, eap_identity_req) | ||
2392 | __field(bool, four_way_handshake) | ||
2393 | __field(bool, rfkill_release) | ||
2394 | __field(s32, pattern_idx) | ||
2395 | __field(u32, packet_len) | ||
2396 | __dynamic_array(u8, packet, wakeup->packet_present_len) | ||
2397 | ), | ||
2398 | TP_fast_assign( | ||
2399 | WIPHY_ASSIGN; | ||
2400 | WDEV_ASSIGN; | ||
2401 | __entry->disconnect = wakeup->disconnect; | ||
2402 | __entry->magic_pkt = wakeup->magic_pkt; | ||
2403 | __entry->gtk_rekey_failure = wakeup->gtk_rekey_failure; | ||
2404 | __entry->eap_identity_req = wakeup->eap_identity_req; | ||
2405 | __entry->four_way_handshake = wakeup->four_way_handshake; | ||
2406 | __entry->rfkill_release = wakeup->rfkill_release; | ||
2407 | __entry->pattern_idx = wakeup->pattern_idx; | ||
2408 | __entry->packet_len = wakeup->packet_len; | ||
2409 | if (wakeup->packet && wakeup->packet_present_len) | ||
2410 | memcpy(__get_dynamic_array(packet), wakeup->packet, | ||
2411 | wakeup->packet_present_len); | ||
2412 | ), | ||
2413 | TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) | ||
2414 | ); | ||
2415 | |||
2336 | #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ | 2416 | #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ |
2337 | 2417 | ||
2338 | #undef TRACE_INCLUDE_PATH | 2418 | #undef TRACE_INCLUDE_PATH |
diff --git a/net/wireless/util.c b/net/wireless/util.c index d7873c7ae0ec..37a56ee1e1ed 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -1217,10 +1217,10 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
1217 | break; | 1217 | break; |
1218 | case NL80211_IFTYPE_P2P_CLIENT: | 1218 | case NL80211_IFTYPE_P2P_CLIENT: |
1219 | case NL80211_IFTYPE_STATION: | 1219 | case NL80211_IFTYPE_STATION: |
1220 | case NL80211_IFTYPE_P2P_DEVICE: | ||
1220 | case NL80211_IFTYPE_MONITOR: | 1221 | case NL80211_IFTYPE_MONITOR: |
1221 | radar_required = false; | 1222 | radar_required = false; |
1222 | break; | 1223 | break; |
1223 | case NL80211_IFTYPE_P2P_DEVICE: | ||
1224 | case NUM_NL80211_IFTYPES: | 1224 | case NUM_NL80211_IFTYPES: |
1225 | case NL80211_IFTYPE_UNSPECIFIED: | 1225 | case NL80211_IFTYPE_UNSPECIFIED: |
1226 | default: | 1226 | default: |