Merge remote-tracking branch 'wireless/master' into mac80211
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / mac80211 / scan.c
index 7644181cb6b48ad4595586d9e230e34fbb3f73ea..df36280ed78fd06e5cb7799c8cc46be275d42df1 100644 (file)
@@ -83,13 +83,14 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 
        cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
                                         mgmt, len, signal, GFP_ATOMIC);
-
        if (!cbss)
                return NULL;
 
        cbss->free_priv = ieee80211_rx_bss_free;
        bss = (void *)cbss->priv;
 
+       bss->device_ts = rx_status->device_timestamp;
+
        if (elems->parse_error) {
                if (beacon)
                        bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON;
@@ -114,8 +115,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 
        if (elems->tim && (!elems->parse_error ||
                           !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) {
-               struct ieee80211_tim_ie *tim_ie =
-                       (struct ieee80211_tim_ie *)elems->tim;
+               struct ieee80211_tim_ie *tim_ie = elems->tim;
                bss->dtim_period = tim_ie->dtim_period;
                if (!elems->parse_error)
                                bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
@@ -165,52 +165,47 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        return bss;
 }
 
-ieee80211_rx_result
-ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
 {
        struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
-       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_sub_if_data *sdata1, *sdata2;
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
        struct ieee80211_bss *bss;
        u8 *elements;
        struct ieee80211_channel *channel;
        size_t baselen;
        int freq;
-       __le16 fc;
-       bool presp, beacon = false;
+       bool beacon;
        struct ieee802_11_elems elems;
 
-       if (skb->len < 2)
-               return RX_DROP_UNUSABLE;
-
-       mgmt = (struct ieee80211_mgmt *) skb->data;
-       fc = mgmt->frame_control;
+       if (skb->len < 24 ||
+           (!ieee80211_is_probe_resp(mgmt->frame_control) &&
+            !ieee80211_is_beacon(mgmt->frame_control)))
+               return;
 
-       if (ieee80211_is_ctl(fc))
-               return RX_CONTINUE;
+       sdata1 = rcu_dereference(local->scan_sdata);
+       sdata2 = rcu_dereference(local->sched_scan_sdata);
 
-       if (skb->len < 24)
-               return RX_CONTINUE;
+       if (likely(!sdata1 && !sdata2))
+               return;
 
-       presp = ieee80211_is_probe_resp(fc);
-       if (presp) {
+       if (ieee80211_is_probe_resp(mgmt->frame_control)) {
                /* ignore ProbeResp to foreign address */
-               if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
-                       return RX_DROP_MONITOR;
+               if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) &&
+                   (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr)))
+                       return;
 
-               presp = true;
                elements = mgmt->u.probe_resp.variable;
                baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+               beacon = false;
        } else {
-               beacon = ieee80211_is_beacon(fc);
                baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
                elements = mgmt->u.beacon.variable;
+               beacon = true;
        }
 
-       if (!presp && !beacon)
-               return RX_CONTINUE;
-
        if (baselen > skb->len)
-               return RX_DROP_MONITOR;
+               return;
 
        ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
 
@@ -220,22 +215,16 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
        else
                freq = rx_status->freq;
 
-       channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
+       channel = ieee80211_get_channel(local->hw.wiphy, freq);
 
        if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
-               return RX_DROP_MONITOR;
+               return;
 
-       bss = ieee80211_bss_info_update(sdata->local, rx_status,
+       bss = ieee80211_bss_info_update(local, rx_status,
                                        mgmt, skb->len, &elems,
                                        channel, beacon);
        if (bss)
-               ieee80211_rx_bss_put(sdata->local, bss);
-
-       if (channel == sdata->local->oper_channel)
-               return RX_CONTINUE;
-
-       dev_kfree_skb(skb);
-       return RX_QUEUED;
+               ieee80211_rx_bss_put(local, bss);
 }
 
 /* return false if no more work */
@@ -293,7 +282,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
                return;
 
        if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
-               int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req);
+               int rc;
+
+               rc = drv_hw_scan(local,
+                       rcu_dereference_protected(local->scan_sdata,
+                                                 lockdep_is_held(&local->mtx)),
+                       local->hw_scan_req);
+
                if (rc == 0)
                        return;
        }
@@ -323,7 +318,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
        ieee80211_mlme_notify_scan_completed(local);
        ieee80211_ibss_notify_scan_completed(local);
        ieee80211_mesh_notify_scan_completed(local);
-       ieee80211_queue_work(&local->hw, &local->work_work);
+       ieee80211_start_next_roc(local);
 }
 
 void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
@@ -376,7 +371,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
 static bool ieee80211_can_scan(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
-       if (!list_empty(&local->work_list))
+       if (!list_empty(&local->roc_list))
                return false;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
@@ -394,7 +389,10 @@ void ieee80211_run_deferred_scan(struct ieee80211_local *local)
        if (!local->scan_req || local->scanning)
                return;
 
-       if (!ieee80211_can_scan(local, local->scan_sdata))
+       if (!ieee80211_can_scan(local,
+                               rcu_dereference_protected(
+                                       local->scan_sdata,
+                                       lockdep_is_held(&local->mtx))))
                return;
 
        ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
@@ -405,9 +403,12 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
                                            unsigned long *next_delay)
 {
        int i;
-       struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+       struct ieee80211_sub_if_data *sdata;
        enum ieee80211_band band = local->hw.conf.channel->band;
 
+       sdata = rcu_dereference_protected(local->scan_sdata,
+                                         lockdep_is_held(&local->mtx));;
+
        for (i = 0; i < local->scan_req->n_ssids; i++)
                ieee80211_send_probe_req(
                        sdata, NULL,
@@ -439,7 +440,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        if (!ieee80211_can_scan(local, sdata)) {
                /* wait for the work to finish/time out */
                local->scan_req = req;
-               local->scan_sdata = sdata;
+               rcu_assign_pointer(local->scan_sdata, sdata);
                return 0;
        }
 
@@ -473,7 +474,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        }
 
        local->scan_req = req;
-       local->scan_sdata = sdata;
+       rcu_assign_pointer(local->scan_sdata, sdata);
 
        if (local->ops->hw_scan) {
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
@@ -533,7 +534,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                ieee80211_recalc_idle(local);
 
                local->scan_req = NULL;
-               local->scan_sdata = NULL;
+               rcu_assign_pointer(local->scan_sdata, NULL);
        }
 
        return rc;
@@ -720,7 +721,8 @@ void ieee80211_scan_work(struct work_struct *work)
 
        mutex_lock(&local->mtx);
 
-       sdata = local->scan_sdata;
+       sdata = rcu_dereference_protected(local->scan_sdata,
+                                         lockdep_is_held(&local->mtx));
 
        /* When scanning on-channel, the first-callback means completed. */
        if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
@@ -741,7 +743,7 @@ void ieee80211_scan_work(struct work_struct *work)
                int rc;
 
                local->scan_req = NULL;
-               local->scan_sdata = NULL;
+               rcu_assign_pointer(local->scan_sdata, NULL);
 
                rc = __ieee80211_start_scan(sdata, req);
                if (rc) {
@@ -893,7 +895,9 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
 
        if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
                if (local->ops->cancel_hw_scan)
-                       drv_cancel_hw_scan(local, local->scan_sdata);
+                       drv_cancel_hw_scan(local,
+                               rcu_dereference_protected(local->scan_sdata,
+                                               lockdep_is_held(&local->mtx)));
                goto out;
        }
 
@@ -915,9 +919,9 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        int ret, i;
 
-       mutex_lock(&sdata->local->mtx);
+       mutex_lock(&local->mtx);
 
-       if (local->sched_scanning) {
+       if (rcu_access_pointer(local->sched_scan_sdata)) {
                ret = -EBUSY;
                goto out;
        }
@@ -928,6 +932,9 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        }
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               if (!local->hw.wiphy->bands[i])
+                       continue;
+
                local->sched_scan_ies.ie[i] = kzalloc(2 +
                                                      IEEE80211_MAX_SSID_LEN +
                                                      local->scan_ies_len +
@@ -948,7 +955,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        ret = drv_sched_scan_start(local, sdata, req,
                                   &local->sched_scan_ies);
        if (ret == 0) {
-               local->sched_scanning = true;
+               rcu_assign_pointer(local->sched_scan_sdata, sdata);
                goto out;
        }
 
@@ -956,7 +963,7 @@ out_free:
        while (i > 0)
                kfree(local->sched_scan_ies.ie[--i]);
 out:
-       mutex_unlock(&sdata->local->mtx);
+       mutex_unlock(&local->mtx);
        return ret;
 }
 
@@ -965,22 +972,22 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        int ret = 0, i;
 
-       mutex_lock(&sdata->local->mtx);
+       mutex_lock(&local->mtx);
 
        if (!local->ops->sched_scan_stop) {
                ret = -ENOTSUPP;
                goto out;
        }
 
-       if (local->sched_scanning) {
+       if (rcu_access_pointer(local->sched_scan_sdata)) {
                for (i = 0; i < IEEE80211_NUM_BANDS; i++)
                        kfree(local->sched_scan_ies.ie[i]);
 
                drv_sched_scan_stop(local, sdata);
-               local->sched_scanning = false;
+               rcu_assign_pointer(local->sched_scan_sdata, NULL);
        }
 out:
-       mutex_unlock(&sdata->local->mtx);
+       mutex_unlock(&local->mtx);
 
        return ret;
 }
@@ -1004,7 +1011,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
 
        mutex_lock(&local->mtx);
 
-       if (!local->sched_scanning) {
+       if (!rcu_access_pointer(local->sched_scan_sdata)) {
                mutex_unlock(&local->mtx);
                return;
        }
@@ -1012,7 +1019,7 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
        for (i = 0; i < IEEE80211_NUM_BANDS; i++)
                kfree(local->sched_scan_ies.ie[i]);
 
-       local->sched_scanning = false;
+       rcu_assign_pointer(local->sched_scan_sdata, NULL);
 
        mutex_unlock(&local->mtx);