Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
[safe/jmp/linux-2.6] / net / mac80211 / scan.c
index 365f409..e1b0be7 100644 (file)
  * published by the Free Software Foundation.
  */
 
-#include <linux/wireless.h>
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
+#include <linux/pm_qos_params.h>
+#include <net/sch_generic.h>
+#include <linux/slab.h>
 #include <net/mac80211.h>
 
 #include "ieee80211_i.h"
@@ -55,6 +57,23 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,
        cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv));
 }
 
+static bool is_uapsd_supported(struct ieee802_11_elems *elems)
+{
+       u8 qos_info;
+
+       if (elems->wmm_info && elems->wmm_info_len == 7
+           && elems->wmm_info[5] == 1)
+               qos_info = elems->wmm_info[6];
+       else if (elems->wmm_param && elems->wmm_param_len == 24
+                && elems->wmm_param[5] == 1)
+               qos_info = elems->wmm_param[6];
+       else
+               /* no valid wmm information or parameter element found */
+               return false;
+
+       return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
+}
+
 struct ieee80211_bss *
 ieee80211_bss_info_update(struct ieee80211_local *local,
                          struct ieee80211_rx_status *rx_status,
@@ -66,7 +85,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
 {
        struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
-       int clen;
+       int clen, srlen;
        s32 signal = 0;
 
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@@ -95,29 +114,27 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                bss->dtim_period = tim_ie->dtim_period;
        }
 
-       /* set default value for buggy AP/no TIM element */
-       if (bss->dtim_period == 0)
-               bss->dtim_period = 1;
-
-       bss->supp_rates_len = 0;
+       /* replace old supported rates if we get new values */
+       srlen = 0;
        if (elems->supp_rates) {
-               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+               clen = IEEE80211_MAX_SUPP_RATES;
                if (clen > elems->supp_rates_len)
                        clen = elems->supp_rates_len;
-               memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
-                      clen);
-               bss->supp_rates_len += clen;
+               memcpy(bss->supp_rates, elems->supp_rates, clen);
+               srlen += clen;
        }
        if (elems->ext_supp_rates) {
-               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+               clen = IEEE80211_MAX_SUPP_RATES - srlen;
                if (clen > elems->ext_supp_rates_len)
                        clen = elems->ext_supp_rates_len;
-               memcpy(&bss->supp_rates[bss->supp_rates_len],
-                      elems->ext_supp_rates, clen);
-               bss->supp_rates_len += clen;
+               memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen);
+               srlen += clen;
        }
+       if (srlen)
+               bss->supp_rates_len = srlen;
 
        bss->wmm_used = elems->wmm_param || elems->wmm_info;
+       bss->uapsd_supported = is_uapsd_supported(elems);
 
        if (!beacon)
                bss->last_probe_resp = jiffies;
@@ -232,6 +249,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
        struct ieee80211_local *local = hw_to_local(hw);
        bool was_hw_scan;
 
+       trace_api_scan_completed(local, aborted);
+
        mutex_lock(&local->scan_mtx);
 
        /*
@@ -285,6 +304,7 @@ 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);
 }
 EXPORT_SYMBOL(ieee80211_scan_completed);
 
@@ -307,6 +327,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
 
        ieee80211_offchannel_stop_beaconing(local);
 
+       local->leave_oper_channel_time = 0;
        local->next_scan_state = SCAN_DECISION;
        local->scan_channel_idx = 0;
 
@@ -331,6 +352,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        if (local->scan_req)
                return -EBUSY;
 
+       if (!list_empty(&local->work_list)) {
+               /* wait for the work to finish/time out */
+               local->scan_req = req;
+               local->scan_sdata = sdata;
+               return 0;
+       }
+
        if (local->ops->hw_scan) {
                u8 *ies;
 
@@ -350,29 +378,33 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                local->hw_scan_req->ie = ies;
 
                local->hw_scan_band = 0;
+
+               /*
+                * After allocating local->hw_scan_req, we must
+                * go through until ieee80211_prep_hw_scan(), so
+                * anything that might be changed here and leave
+                * this function early must not go after this
+                * allocation.
+                */
        }
 
        local->scan_req = req;
        local->scan_sdata = sdata;
 
-       if (!list_empty(&local->work_list)) {
-               /* wait for the work to finish/time out */
-               return 0;
-       }
-
        if (local->ops->hw_scan)
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
        else
                __set_bit(SCAN_SW_SCANNING, &local->scanning);
+
        /*
         * Kicking off the scan need not be protected,
         * only the scan variable stuff, since now
         * local->scan_req is assigned and other callers
         * will abort their scan attempts.
         *
-        * This avoids getting a scan_mtx -> iflist_mtx
-        * dependency, so that the scan completed calls
-        * have more locking freedom.
+        * This avoids too many locking dependencies
+        * so that the scan completed calls have more
+        * locking freedom.
         */
 
        ieee80211_recalc_idle(local);
@@ -380,7 +412,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
 
        if (local->ops->hw_scan) {
                WARN_ON(!ieee80211_prep_hw_scan(local));
-               rc = drv_hw_scan(local, local->hw_scan_req);
+               rc = drv_hw_scan(local, sdata, local->hw_scan_req);
        } else
                rc = ieee80211_start_sw_scan(local);
 
@@ -400,11 +432,28 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        return rc;
 }
 
+static unsigned long
+ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
+{
+       /*
+        * TODO: channel switching also consumes quite some time,
+        * add that delay as well to get a better estimation
+        */
+       if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+               return IEEE80211_PASSIVE_CHANNEL_TIME;
+       return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
+}
+
 static int ieee80211_scan_state_decision(struct ieee80211_local *local,
                                         unsigned long *next_delay)
 {
        bool associated = false;
+       bool tx_empty = true;
+       bool bad_latency;
+       bool listen_int_exceeded;
+       unsigned long min_beacon_int = 0;
        struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_channel *next_chan;
 
        /* if no more bands/channels left, complete scan and advance to the idle state */
        if (local->scan_channel_idx >= local->scan_req->n_channels) {
@@ -412,7 +461,11 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
                return 1;
        }
 
-       /* check if at least one STA interface is associated */
+       /*
+        * check if at least one STA interface is associated,
+        * check if at least one STA interface has pending tx frames
+        * and grab the lowest used beacon interval
+        */
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (!ieee80211_sdata_running(sdata))
@@ -421,7 +474,16 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                        if (sdata->u.mgd.associated) {
                                associated = true;
-                               break;
+
+                               if (sdata->vif.bss_conf.beacon_int <
+                                   min_beacon_int || min_beacon_int == 0)
+                                       min_beacon_int =
+                                               sdata->vif.bss_conf.beacon_int;
+
+                               if (!qdisc_all_tx_empty(sdata->dev)) {
+                                       tx_empty = false;
+                                       break;
+                               }
                        }
                }
        }
@@ -430,11 +492,34 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
        if (local->scan_channel) {
                /*
                 * we're currently scanning a different channel, let's
-                * switch back to the operating channel now if at least
-                * one interface is associated. Otherwise just scan the
-                * next channel
+                * see if we can scan another channel without interfering
+                * with the current traffic situation.
+                *
+                * Since we don't know if the AP has pending frames for us
+                * we can only check for our tx queues and use the current
+                * pm_qos requirements for rx. Hence, if no tx traffic occurs
+                * at all we will scan as many channels in a row as the pm_qos
+                * latency allows us to. Additionally we also check for the
+                * currently negotiated listen interval to prevent losing
+                * frames unnecessarily.
+                *
+                * Otherwise switch back to the operating channel.
                 */
-               if (associated)
+               next_chan = local->scan_req->channels[local->scan_channel_idx];
+
+               bad_latency = time_after(jiffies +
+                               ieee80211_scan_get_channel_time(next_chan),
+                               local->leave_oper_channel_time +
+                               usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY)));
+
+               listen_int_exceeded = time_after(jiffies +
+                               ieee80211_scan_get_channel_time(next_chan),
+                               local->leave_oper_channel_time +
+                               usecs_to_jiffies(min_beacon_int * 1024) *
+                               local->hw.conf.listen_interval);
+
+               if (associated && ( !tx_empty || bad_latency ||
+                   listen_int_exceeded))
                        local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
                else
                        local->next_scan_state = SCAN_SET_CHANNEL;
@@ -466,6 +551,9 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca
        else
                *next_delay = HZ / 10;
 
+       /* remember when we left the operating channel */
+       local->leave_oper_channel_time = jiffies;
+
        /* advance to the next channel to be scanned */
        local->next_scan_state = SCAN_SET_CHANNEL;
 }
@@ -568,7 +656,7 @@ void ieee80211_scan_work(struct work_struct *work)
        }
 
        if (local->hw_scan_req) {
-               int rc = drv_hw_scan(local, local->hw_scan_req);
+               int rc = drv_hw_scan(local, sdata, local->hw_scan_req);
                mutex_unlock(&local->scan_mtx);
                if (rc)
                        ieee80211_scan_completed(&local->hw, true);
@@ -641,10 +729,12 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
 }
 
 int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
-                                   const u8 *ssid, u8 ssid_len)
+                                   const u8 *ssid, u8 ssid_len,
+                                   struct ieee80211_channel *chan)
 {
        struct ieee80211_local *local = sdata->local;
        int ret = -EBUSY;
+       enum nl80211_band band;
 
        mutex_lock(&local->scan_mtx);
 
@@ -652,6 +742,30 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
        if (local->scan_req)
                goto unlock;
 
+       /* fill internal scan request */
+       if (!chan) {
+               int i, nchan = 0;
+
+               for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+                       if (!local->hw.wiphy->bands[band])
+                               continue;
+                       for (i = 0;
+                            i < local->hw.wiphy->bands[band]->n_channels;
+                            i++) {
+                               local->int_scan_req->channels[nchan] =
+                                   &local->hw.wiphy->bands[band]->channels[i];
+                               nchan++;
+                       }
+               }
+
+               local->int_scan_req->n_channels = nchan;
+       } else {
+               local->int_scan_req->channels[0] = chan;
+               local->int_scan_req->n_channels = 1;
+       }
+
+       local->int_scan_req->ssids = &local->scan_ssid;
+       local->int_scan_req->n_ssids = 1;
        memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
        local->int_scan_req->ssids[0].ssid_len = ssid_len;