|
@@ -187,6 +187,39 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
|
|
|
return RX_QUEUED;
|
|
|
}
|
|
|
|
|
|
+/* return false if no more work */
|
|
|
+static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
|
|
|
+{
|
|
|
+ struct cfg80211_scan_request *req = local->scan_req;
|
|
|
+ enum ieee80211_band band;
|
|
|
+ int i, ielen, n_chans;
|
|
|
+
|
|
|
+ do {
|
|
|
+ if (local->hw_scan_band == IEEE80211_NUM_BANDS)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ band = local->hw_scan_band;
|
|
|
+ n_chans = 0;
|
|
|
+ for (i = 0; i < req->n_channels; i++) {
|
|
|
+ if (req->channels[i]->band == band) {
|
|
|
+ local->hw_scan_req->channels[n_chans] =
|
|
|
+ req->channels[i];
|
|
|
+ n_chans++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ local->hw_scan_band++;
|
|
|
+ } while (!n_chans);
|
|
|
+
|
|
|
+ local->hw_scan_req->n_channels = n_chans;
|
|
|
+
|
|
|
+ ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
|
|
|
+ req->ie, req->ie_len, band);
|
|
|
+ local->hw_scan_req->ie_len = ielen;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* inform AP that we will go to sleep so that it will buffer the frames
|
|
|
* while we scan
|
|
@@ -247,13 +280,6 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void ieee80211_restore_scan_ies(struct ieee80211_local *local)
|
|
|
-{
|
|
|
- kfree(local->scan_req->ie);
|
|
|
- local->scan_req->ie = local->orig_ies;
|
|
|
- local->scan_req->ie_len = local->orig_ies_len;
|
|
|
-}
|
|
|
-
|
|
|
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
|
|
{
|
|
|
struct ieee80211_local *local = hw_to_local(hw);
|
|
@@ -272,15 +298,22 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (test_bit(SCAN_HW_SCANNING, &local->scanning))
|
|
|
- ieee80211_restore_scan_ies(local);
|
|
|
+ was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
|
|
|
+ if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
|
|
|
+ ieee80211_queue_delayed_work(&local->hw,
|
|
|
+ &local->scan_work, 0);
|
|
|
+ mutex_unlock(&local->scan_mtx);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ kfree(local->hw_scan_req);
|
|
|
+ local->hw_scan_req = NULL;
|
|
|
|
|
|
if (local->scan_req != local->int_scan_req)
|
|
|
cfg80211_scan_done(local->scan_req, aborted);
|
|
|
local->scan_req = NULL;
|
|
|
local->scan_sdata = NULL;
|
|
|
|
|
|
- was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
|
|
|
local->scanning = 0;
|
|
|
local->scan_channel = NULL;
|
|
|
|
|
@@ -392,19 +425,23 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
if (local->ops->hw_scan) {
|
|
|
u8 *ies;
|
|
|
- int ielen;
|
|
|
|
|
|
- ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN +
|
|
|
- local->scan_ies_len + req->ie_len, GFP_KERNEL);
|
|
|
- if (!ies)
|
|
|
+ local->hw_scan_req = kmalloc(
|
|
|
+ sizeof(*local->hw_scan_req) +
|
|
|
+ req->n_channels * sizeof(req->channels[0]) +
|
|
|
+ 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
|
|
|
+ req->ie_len, GFP_KERNEL);
|
|
|
+ if (!local->hw_scan_req)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- ielen = ieee80211_build_preq_ies(local, ies,
|
|
|
- req->ie, req->ie_len);
|
|
|
- local->orig_ies = req->ie;
|
|
|
- local->orig_ies_len = req->ie_len;
|
|
|
- req->ie = ies;
|
|
|
- req->ie_len = ielen;
|
|
|
+ local->hw_scan_req->ssids = req->ssids;
|
|
|
+ local->hw_scan_req->n_ssids = req->n_ssids;
|
|
|
+ ies = (u8 *)local->hw_scan_req +
|
|
|
+ sizeof(*local->hw_scan_req) +
|
|
|
+ req->n_channels * sizeof(req->channels[0]);
|
|
|
+ local->hw_scan_req->ie = ies;
|
|
|
+
|
|
|
+ local->hw_scan_band = 0;
|
|
|
}
|
|
|
|
|
|
local->scan_req = req;
|
|
@@ -436,16 +473,17 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|
|
ieee80211_recalc_idle(local);
|
|
|
mutex_unlock(&local->scan_mtx);
|
|
|
|
|
|
- if (local->ops->hw_scan)
|
|
|
- rc = drv_hw_scan(local, local->scan_req);
|
|
|
- else
|
|
|
+ if (local->ops->hw_scan) {
|
|
|
+ WARN_ON(!ieee80211_prep_hw_scan(local));
|
|
|
+ rc = drv_hw_scan(local, local->hw_scan_req);
|
|
|
+ } else
|
|
|
rc = ieee80211_start_sw_scan(local);
|
|
|
|
|
|
mutex_lock(&local->scan_mtx);
|
|
|
|
|
|
if (rc) {
|
|
|
- if (local->ops->hw_scan)
|
|
|
- ieee80211_restore_scan_ies(local);
|
|
|
+ kfree(local->hw_scan_req);
|
|
|
+ local->hw_scan_req = NULL;
|
|
|
local->scanning = 0;
|
|
|
|
|
|
ieee80211_recalc_idle(local);
|
|
@@ -654,6 +692,14 @@ void ieee80211_scan_work(struct work_struct *work)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (local->hw_scan_req) {
|
|
|
+ int rc = drv_hw_scan(local, local->hw_scan_req);
|
|
|
+ mutex_unlock(&local->scan_mtx);
|
|
|
+ if (rc)
|
|
|
+ ieee80211_scan_completed(&local->hw, true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (local->scan_req && !local->scanning) {
|
|
|
struct cfg80211_scan_request *req = local->scan_req;
|
|
|
int rc;
|