diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 118 |
1 files changed, 77 insertions, 41 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f40661eb75b5..a0a938145dcc 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -235,38 +235,51 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
235 | { | 235 | { |
236 | struct cfg80211_scan_request *req = local->scan_req; | 236 | struct cfg80211_scan_request *req = local->scan_req; |
237 | struct cfg80211_chan_def chandef; | 237 | struct cfg80211_chan_def chandef; |
238 | enum ieee80211_band band; | 238 | u8 bands_used = 0; |
239 | int i, ielen, n_chans; | 239 | int i, ielen, n_chans; |
240 | 240 | ||
241 | if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) | 241 | if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) |
242 | return false; | 242 | return false; |
243 | 243 | ||
244 | do { | 244 | if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) { |
245 | if (local->hw_scan_band == IEEE80211_NUM_BANDS) | ||
246 | return false; | ||
247 | |||
248 | band = local->hw_scan_band; | ||
249 | n_chans = 0; | ||
250 | for (i = 0; i < req->n_channels; i++) { | 245 | for (i = 0; i < req->n_channels; i++) { |
251 | if (req->channels[i]->band == band) { | 246 | local->hw_scan_req->req.channels[i] = req->channels[i]; |
252 | local->hw_scan_req->channels[n_chans] = | 247 | bands_used |= BIT(req->channels[i]->band); |
248 | } | ||
249 | |||
250 | n_chans = req->n_channels; | ||
251 | } else { | ||
252 | do { | ||
253 | if (local->hw_scan_band == IEEE80211_NUM_BANDS) | ||
254 | return false; | ||
255 | |||
256 | n_chans = 0; | ||
257 | |||
258 | for (i = 0; i < req->n_channels; i++) { | ||
259 | if (req->channels[i]->band != | ||
260 | local->hw_scan_band) | ||
261 | continue; | ||
262 | local->hw_scan_req->req.channels[n_chans] = | ||
253 | req->channels[i]; | 263 | req->channels[i]; |
254 | n_chans++; | 264 | n_chans++; |
265 | bands_used |= BIT(req->channels[i]->band); | ||
255 | } | 266 | } |
256 | } | ||
257 | 267 | ||
258 | local->hw_scan_band++; | 268 | local->hw_scan_band++; |
259 | } while (!n_chans); | 269 | } while (!n_chans); |
270 | } | ||
260 | 271 | ||
261 | local->hw_scan_req->n_channels = n_chans; | 272 | local->hw_scan_req->req.n_channels = n_chans; |
262 | ieee80211_prepare_scan_chandef(&chandef, req->scan_width); | 273 | ieee80211_prepare_scan_chandef(&chandef, req->scan_width); |
263 | 274 | ||
264 | ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, | 275 | ielen = ieee80211_build_preq_ies(local, |
276 | (u8 *)local->hw_scan_req->req.ie, | ||
265 | local->hw_scan_ies_bufsize, | 277 | local->hw_scan_ies_bufsize, |
266 | req->ie, req->ie_len, band, | 278 | &local->hw_scan_req->ies, |
267 | req->rates[band], &chandef); | 279 | req->ie, req->ie_len, |
268 | local->hw_scan_req->ie_len = ielen; | 280 | bands_used, req->rates, &chandef); |
269 | local->hw_scan_req->no_cck = req->no_cck; | 281 | local->hw_scan_req->req.ie_len = ielen; |
282 | local->hw_scan_req->req.no_cck = req->no_cck; | ||
270 | 283 | ||
271 | return true; | 284 | return true; |
272 | } | 285 | } |
@@ -291,7 +304,9 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
291 | if (WARN_ON(!local->scan_req)) | 304 | if (WARN_ON(!local->scan_req)) |
292 | return; | 305 | return; |
293 | 306 | ||
294 | if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | 307 | if (hw_scan && !aborted && |
308 | !(local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) && | ||
309 | ieee80211_prep_hw_scan(local)) { | ||
295 | int rc; | 310 | int rc; |
296 | 311 | ||
297 | rc = drv_hw_scan(local, | 312 | rc = drv_hw_scan(local, |
@@ -473,6 +488,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
473 | u8 *ies; | 488 | u8 *ies; |
474 | 489 | ||
475 | local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len; | 490 | local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len; |
491 | |||
492 | if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) { | ||
493 | int i, n_bands = 0; | ||
494 | u8 bands_counted = 0; | ||
495 | |||
496 | for (i = 0; i < req->n_channels; i++) { | ||
497 | if (bands_counted & BIT(req->channels[i]->band)) | ||
498 | continue; | ||
499 | bands_counted |= BIT(req->channels[i]->band); | ||
500 | n_bands++; | ||
501 | } | ||
502 | |||
503 | local->hw_scan_ies_bufsize *= n_bands; | ||
504 | } | ||
505 | |||
476 | local->hw_scan_req = kmalloc( | 506 | local->hw_scan_req = kmalloc( |
477 | sizeof(*local->hw_scan_req) + | 507 | sizeof(*local->hw_scan_req) + |
478 | req->n_channels * sizeof(req->channels[0]) + | 508 | req->n_channels * sizeof(req->channels[0]) + |
@@ -480,13 +510,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
480 | if (!local->hw_scan_req) | 510 | if (!local->hw_scan_req) |
481 | return -ENOMEM; | 511 | return -ENOMEM; |
482 | 512 | ||
483 | local->hw_scan_req->ssids = req->ssids; | 513 | local->hw_scan_req->req.ssids = req->ssids; |
484 | local->hw_scan_req->n_ssids = req->n_ssids; | 514 | local->hw_scan_req->req.n_ssids = req->n_ssids; |
485 | ies = (u8 *)local->hw_scan_req + | 515 | ies = (u8 *)local->hw_scan_req + |
486 | sizeof(*local->hw_scan_req) + | 516 | sizeof(*local->hw_scan_req) + |
487 | req->n_channels * sizeof(req->channels[0]); | 517 | req->n_channels * sizeof(req->channels[0]); |
488 | local->hw_scan_req->ie = ies; | 518 | local->hw_scan_req->req.ie = ies; |
489 | local->hw_scan_req->flags = req->flags; | 519 | local->hw_scan_req->req.flags = req->flags; |
490 | 520 | ||
491 | local->hw_scan_band = 0; | 521 | local->hw_scan_band = 0; |
492 | 522 | ||
@@ -973,9 +1003,13 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
973 | struct cfg80211_sched_scan_request *req) | 1003 | struct cfg80211_sched_scan_request *req) |
974 | { | 1004 | { |
975 | struct ieee80211_local *local = sdata->local; | 1005 | struct ieee80211_local *local = sdata->local; |
976 | struct ieee80211_sched_scan_ies sched_scan_ies = {}; | 1006 | struct ieee80211_scan_ies sched_scan_ies = {}; |
977 | struct cfg80211_chan_def chandef; | 1007 | struct cfg80211_chan_def chandef; |
978 | int ret, i, iebufsz; | 1008 | int ret, i, iebufsz, num_bands = 0; |
1009 | u32 rate_masks[IEEE80211_NUM_BANDS] = {}; | ||
1010 | u8 bands_used = 0; | ||
1011 | u8 *ie; | ||
1012 | size_t len; | ||
979 | 1013 | ||
980 | iebufsz = local->scan_ies_len + req->ie_len; | 1014 | iebufsz = local->scan_ies_len + req->ie_len; |
981 | 1015 | ||
@@ -985,33 +1019,35 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, | |||
985 | return -ENOTSUPP; | 1019 | return -ENOTSUPP; |
986 | 1020 | ||
987 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { | 1021 | for (i = 0; i < IEEE80211_NUM_BANDS; i++) { |
988 | if (!local->hw.wiphy->bands[i]) | 1022 | if (local->hw.wiphy->bands[i]) { |
989 | continue; | 1023 | bands_used |= BIT(i); |
990 | 1024 | rate_masks[i] = (u32) -1; | |
991 | sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL); | 1025 | num_bands++; |
992 | if (!sched_scan_ies.ie[i]) { | ||
993 | ret = -ENOMEM; | ||
994 | goto out_free; | ||
995 | } | 1026 | } |
1027 | } | ||
996 | 1028 | ||
997 | ieee80211_prepare_scan_chandef(&chandef, req->scan_width); | 1029 | ie = kzalloc(num_bands * iebufsz, GFP_KERNEL); |
998 | 1030 | if (!ie) { | |
999 | sched_scan_ies.len[i] = | 1031 | ret = -ENOMEM; |
1000 | ieee80211_build_preq_ies(local, sched_scan_ies.ie[i], | 1032 | goto out; |
1001 | iebufsz, req->ie, req->ie_len, | ||
1002 | i, (u32) -1, &chandef); | ||
1003 | } | 1033 | } |
1004 | 1034 | ||
1035 | ieee80211_prepare_scan_chandef(&chandef, req->scan_width); | ||
1036 | |||
1037 | len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz, | ||
1038 | &sched_scan_ies, req->ie, | ||
1039 | req->ie_len, bands_used, | ||
1040 | rate_masks, &chandef); | ||
1041 | |||
1005 | ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); | 1042 | ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); |
1006 | if (ret == 0) { | 1043 | if (ret == 0) { |
1007 | rcu_assign_pointer(local->sched_scan_sdata, sdata); | 1044 | rcu_assign_pointer(local->sched_scan_sdata, sdata); |
1008 | local->sched_scan_req = req; | 1045 | local->sched_scan_req = req; |
1009 | } | 1046 | } |
1010 | 1047 | ||
1011 | out_free: | 1048 | kfree(ie); |
1012 | while (i > 0) | ||
1013 | kfree(sched_scan_ies.ie[--i]); | ||
1014 | 1049 | ||
1050 | out: | ||
1015 | if (ret) { | 1051 | if (ret) { |
1016 | /* Clean in case of failure after HW restart or upon resume. */ | 1052 | /* Clean in case of failure after HW restart or upon resume. */ |
1017 | RCU_INIT_POINTER(local->sched_scan_sdata, NULL); | 1053 | RCU_INIT_POINTER(local->sched_scan_sdata, NULL); |