]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/mac80211/mlme.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-2.6.git] / net / mac80211 / mlme.c
index 34e0650e9ef8f4ab38f618e981f03e3f189b0be9..11783192a6254d543e73de0bd45860adbb714f0b 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/pm_qos_params.h>
 #include <linux/crc32.h>
+#include <linux/slab.h>
 #include <net/mac80211.h>
 #include <asm/unaligned.h>
 
  */
 #define IEEE80211_PROBE_WAIT           (HZ / 2)
 
+/*
+ * Weight given to the latest Beacon frame when calculating average signal
+ * strength for Beacon frames received in the current BSS. This must be
+ * between 1 and 15.
+ */
+#define IEEE80211_SIGNAL_AVE_WEIGHT    3
+
 #define TMR_RUNNING_TIMER      0
 #define TMR_RUNNING_CHANSW     1
 
@@ -129,11 +137,14 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
        struct sta_info *sta;
        u32 changed = 0;
        u16 ht_opmode;
-       bool enable_ht = true, ht_changed;
+       bool enable_ht = true;
+       enum nl80211_channel_type prev_chantype;
        enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
 
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
+       prev_chantype = sdata->vif.bss_conf.channel_type;
+
        /* HT is not supported */
        if (!sband->ht_cap.ht_supported)
                enable_ht = false;
@@ -164,36 +175,37 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
-                    channel_type != local->hw.conf.channel_type;
+       if (local->tmp_channel)
+               local->tmp_channel_type = channel_type;
 
-       local->oper_channel_type = channel_type;
+       if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+               /* can only fail due to HT40+/- mismatch */
+               channel_type = NL80211_CHAN_HT20;
+               WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type));
+       }
 
-       if (ht_changed) {
-                /* channel_type change automatically detected */
-               ieee80211_hw_config(local, 0);
+       /* channel_type change automatically detected */
+       ieee80211_hw_config(local, 0);
 
+       if (prev_chantype != channel_type) {
                rcu_read_lock();
                sta = sta_info_get(sdata, bssid);
                if (sta)
                        rate_control_rate_update(local, sband, sta,
                                                 IEEE80211_RC_HT_CHANGED,
-                                                local->oper_channel_type);
+                                                channel_type);
                rcu_read_unlock();
-        }
-
-       /* disable HT */
-       if (!enable_ht)
-               return 0;
+       }
 
        ht_opmode = le16_to_cpu(hti->operation_mode);
 
        /* if bss configuration changed store the new one */
-       if (!sdata->ht_opmode_valid ||
-           sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
+       if (sdata->ht_opmode_valid != enable_ht ||
+           sdata->vif.bss_conf.ht_operation_mode != ht_opmode ||
+           prev_chantype != channel_type) {
                changed |= BSS_CHANGED_HT;
                sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
-               sdata->ht_opmode_valid = true;
+               sdata->ht_opmode_valid = enable_ht;
        }
 
        return changed;
@@ -203,7 +215,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
 
 static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                                           const u8 *bssid, u16 stype, u16 reason,
-                                          void *cookie)
+                                          void *cookie, bool send_frame)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -240,7 +252,11 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                        cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
        if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
                IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-       ieee80211_tx_skb(sdata, skb);
+
+       if (send_frame)
+               ieee80211_tx_skb(sdata, skb);
+       else
+               kfree_skb(skb);
 }
 
 void ieee80211_send_pspoll(struct ieee80211_local *local,
@@ -464,6 +480,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 {
        struct ieee80211_sub_if_data *sdata, *found = NULL;
        int count = 0;
+       int timeout;
 
        if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
                local->ps_sdata = NULL;
@@ -497,6 +514,26 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                beaconint_us = ieee80211_tu_to_usec(
                                        found->vif.bss_conf.beacon_int);
 
+               timeout = local->hw.conf.dynamic_ps_forced_timeout;
+               if (timeout < 0) {
+                       /*
+                        * The 2 second value is there for compatibility until
+                        * the PM_QOS_NETWORK_LATENCY is configured with real
+                        * values.
+                        */
+                       if (latency == 2000000000)
+                               timeout = 100;
+                       else if (latency <= 50000)
+                               timeout = 300;
+                       else if (latency <= 100000)
+                               timeout = 100;
+                       else if (latency <= 500000)
+                               timeout = 50;
+                       else
+                               timeout = 0;
+               }
+               local->hw.conf.dynamic_ps_timeout = timeout;
+
                if (beaconint_us > latency) {
                        local->ps_sdata = NULL;
                } else {
@@ -589,6 +626,9 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
        int count;
        u8 *pos, uapsd_queues = 0;
 
+       if (!local->ops->conf_tx)
+               return;
+
        if (local->hw.queues < 4)
                return;
 
@@ -663,11 +703,15 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                       params.aifs, params.cw_min, params.cw_max, params.txop,
                       params.uapsd);
 #endif
-               if (drv_conf_tx(local, queue, &params) && local->ops->conf_tx)
+               if (drv_conf_tx(local, queue, &params))
                        printk(KERN_DEBUG "%s: failed to set TX queue "
                               "parameters for queue %d\n",
                               wiphy_name(local->hw.wiphy), queue);
        }
+
+       /* enable WMM or activate new settings */
+       local->hw.conf.flags |= IEEE80211_CONF_QOS;
+       drv_config(local, IEEE80211_CONF_CHANGE_QOS);
 }
 
 static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
@@ -728,6 +772,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        sdata->u.mgd.associated = cbss;
        memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
 
+       sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
+
        /* just to be sure */
        sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
                                IEEE80211_STA_BEACON_POLL);
@@ -769,7 +815,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        netif_carrier_on(sdata->dev);
 }
 
-static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
+                                  bool remove_sta)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
@@ -821,7 +868,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
        ieee80211_set_wmm_default(sdata);
 
        /* channel(_type) changes are handled by ieee80211_hw_config */
-       local->oper_channel_type = NL80211_CHAN_NO_HT;
+       WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
 
        /* on the next assoc, re-program HT parameters */
        sdata->ht_opmode_valid = false;
@@ -838,11 +885,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 
        ieee80211_hw_config(local, config_changed);
 
-       /* And the BSSID changed -- not very interesting here */
-       changed |= BSS_CHANGED_BSSID;
+       /* The BSSID (not really interesting) and HT changed */
+       changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
        ieee80211_bss_info_change_notify(sdata, changed);
 
-       sta_info_destroy_addr(sdata, bssid);
+       if (remove_sta)
+               sta_info_destroy_addr(sdata, bssid);
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -955,7 +1003,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
 
        printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
 
-       ieee80211_set_disassoc(sdata);
+       ieee80211_set_disassoc(sdata, true);
        ieee80211_recalc_idle(local);
        mutex_unlock(&ifmgd->mtx);
        /*
@@ -965,7 +1013,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
        ieee80211_send_deauth_disassoc(sdata, bssid,
                                       IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                                      NULL);
+                                      NULL, true);
 }
 
 void ieee80211_beacon_connection_loss_work(struct work_struct *work)
@@ -985,6 +1033,8 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
        struct ieee80211_hw *hw = &sdata->local->hw;
 
+       trace_api_beacon_loss(sdata);
+
        WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
        ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
 }
@@ -995,6 +1045,8 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif)
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
        struct ieee80211_hw *hw = &sdata->local->hw;
 
+       trace_api_connection_loss(sdata);
+
        WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
        ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
 }
@@ -1021,7 +1073,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
                        sdata->name, bssid, reason_code);
 
-       ieee80211_set_disassoc(sdata);
+       ieee80211_set_disassoc(sdata, true);
        ieee80211_recalc_idle(sdata->local);
 
        return RX_MGMT_CFG80211_DEAUTH;
@@ -1051,7 +1103,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
                        sdata->name, mgmt->sa, reason_code);
 
-       ieee80211_set_disassoc(sdata);
+       ieee80211_set_disassoc(sdata, true);
        ieee80211_recalc_idle(sdata->local);
        return RX_MGMT_CFG80211_DISASSOC;
 }
@@ -1304,12 +1356,17 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
                mutex_lock(&sdata->local->iflist_mtx);
                ieee80211_recalc_ps(sdata->local, -1);
                mutex_unlock(&sdata->local->iflist_mtx);
+
+               if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+                       return;
+
                /*
                 * We've received a probe response, but are not sure whether
                 * we have or will be receiving any beacons or data, so let's
                 * schedule the timers again, just in case.
                 */
                mod_beacon_timer(sdata);
+
                mod_timer(&ifmgd->conn_mon_timer,
                          round_jiffies_up(jiffies +
                                           IEEE80211_CONNECTION_IDLE_TIME));
@@ -1343,6 +1400,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_rx_status *rx_status)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
        size_t baselen;
        struct ieee802_11_elems elems;
        struct ieee80211_local *local = sdata->local;
@@ -1378,6 +1436,41 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
                return;
 
+       /* Track average RSSI from the Beacon frames of the current AP */
+       ifmgd->last_beacon_signal = rx_status->signal;
+       if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
+               ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
+               ifmgd->ave_beacon_signal = rx_status->signal;
+               ifmgd->last_cqm_event_signal = 0;
+       } else {
+               ifmgd->ave_beacon_signal =
+                       (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
+                        (16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
+                        ifmgd->ave_beacon_signal) / 16;
+       }
+       if (bss_conf->cqm_rssi_thold &&
+           !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
+               int sig = ifmgd->ave_beacon_signal / 16;
+               int last_event = ifmgd->last_cqm_event_signal;
+               int thold = bss_conf->cqm_rssi_thold;
+               int hyst = bss_conf->cqm_rssi_hyst;
+               if (sig < thold &&
+                   (last_event == 0 || sig < last_event - hyst)) {
+                       ifmgd->last_cqm_event_signal = sig;
+                       ieee80211_cqm_rssi_notify(
+                               &sdata->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                               GFP_KERNEL);
+               } else if (sig > thold &&
+                          (last_event == 0 || sig > last_event + hyst)) {
+                       ifmgd->last_cqm_event_signal = sig;
+                       ieee80211_cqm_rssi_notify(
+                               &sdata->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                               GFP_KERNEL);
+               }
+       }
+
        if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                if (net_ratelimit()) {
@@ -1663,7 +1756,7 @@ static void ieee80211_sta_work(struct work_struct *work)
                        printk(KERN_DEBUG "No probe response from AP %pM"
                                " after %dms, disconnecting.\n",
                                bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
-                       ieee80211_set_disassoc(sdata);
+                       ieee80211_set_disassoc(sdata, true);
                        ieee80211_recalc_idle(local);
                        mutex_unlock(&ifmgd->mtx);
                        /*
@@ -1673,7 +1766,7 @@ static void ieee80211_sta_work(struct work_struct *work)
                        ieee80211_send_deauth_disassoc(sdata, bssid,
                                        IEEE80211_STYPE_DEAUTH,
                                        WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                                       NULL);
+                                       NULL, true);
                        mutex_lock(&ifmgd->mtx);
                }
        }
@@ -1857,6 +1950,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_work *wk;
        u16 auth_alg;
 
+       if (req->local_state_change)
+               return 0; /* no need to update mac80211 state */
+
        switch (req->auth_type) {
        case NL80211_AUTHTYPE_OPEN_SYSTEM:
                auth_alg = WLAN_AUTH_OPEN;
@@ -1965,7 +2061,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                }
 
                /* Trying to reassociate - clear previous association state */
-               ieee80211_set_disassoc(sdata);
+               ieee80211_set_disassoc(sdata, true);
        }
        mutex_unlock(&ifmgd->mtx);
 
@@ -2069,7 +2165,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
        if (ifmgd->associated == req->bss) {
                bssid = req->bss->bssid;
-               ieee80211_set_disassoc(sdata);
+               ieee80211_set_disassoc(sdata, true);
                mutex_unlock(&ifmgd->mtx);
        } else {
                bool not_auth_yet = false;
@@ -2112,9 +2208,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
               sdata->name, bssid, req->reason_code);
 
-       ieee80211_send_deauth_disassoc(sdata, bssid,
-                       IEEE80211_STYPE_DEAUTH, req->reason_code,
-                       cookie);
+       ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH,
+                                      req->reason_code, cookie,
+                                      !req->local_state_change);
 
        ieee80211_recalc_idle(sdata->local);
 
@@ -2126,6 +2222,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                           void *cookie)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       u8 bssid[ETH_ALEN];
 
        mutex_lock(&ifmgd->mtx);
 
@@ -2143,13 +2240,15 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
               sdata->name, req->bss->bssid, req->reason_code);
 
-       ieee80211_set_disassoc(sdata);
+       memcpy(bssid, req->bss->bssid, ETH_ALEN);
+       ieee80211_set_disassoc(sdata, false);
 
        mutex_unlock(&ifmgd->mtx);
 
        ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
                        IEEE80211_STYPE_DISASSOC, req->reason_code,
-                       cookie);
+                       cookie, !req->local_state_change);
+       sta_info_destroy_addr(sdata, bssid);
 
        ieee80211_recalc_idle(sdata->local);
 
@@ -2169,7 +2268,7 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
        if ((chan != local->tmp_channel ||
             channel_type != local->tmp_channel_type) &&
            (chan != local->oper_channel ||
-            channel_type != local->oper_channel_type))
+            channel_type != local->_oper_channel_type))
                return -EBUSY;
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
@@ -2197,6 +2296,8 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 
+       trace_api_cqm_rssi_notify(sdata, rssi_event);
+
        cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
 }
 EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);