]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/mac80211/mlme.c
Merge branch 'master' into tk71
[mv-sheeva.git] / net / mac80211 / mlme.c
index b6c163ac22da39fd3327a30f77478e27cb81756c..c9ceb4d57ab0e62ab3c250ad1e0d959ce1de06ef 100644 (file)
 #include "rate.h"
 #include "led.h"
 
+#define IEEE80211_MAX_NULLFUNC_TRIES 2
 #define IEEE80211_MAX_PROBE_TRIES 5
 
 /*
- * beacon loss detection timeout
- * XXX: should depend on beacon interval
+ * Beacon loss timeout is calculated as N frames times the
+ * advertised beacon interval.  This may need to be somewhat
+ * higher than what hardware might detect to account for
+ * delays in the host processing frames. But since we also
+ * probe on beacon miss before declaring the connection lost
+ * default to what we want.
  */
-#define IEEE80211_BEACON_LOSS_TIME     (2 * HZ)
+#define IEEE80211_BEACON_LOSS_COUNT    7
+
 /*
  * Time the connection can be idle before we probe
  * it to see if we can still talk to the AP.
  */
 #define IEEE80211_SIGNAL_AVE_WEIGHT    3
 
+/*
+ * How many Beacon frames need to have been used in average signal strength
+ * before starting to indicate signal change events.
+ */
+#define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
+
 #define TMR_RUNNING_TIMER      0
 #define TMR_RUNNING_CHANSW     1
 
@@ -86,7 +98,7 @@ enum rx_mgmt_action {
 /* utils */
 static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
 {
-       WARN_ON(!mutex_is_locked(&ifmgd->mtx));
+       lockdep_assert_held(&ifmgd->mtx);
 }
 
 /*
@@ -109,13 +121,26 @@ static void run_again(struct ieee80211_if_managed *ifmgd,
                mod_timer(&ifmgd->timer, timeout);
 }
 
-static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata)
+void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
 {
        if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER)
                return;
 
        mod_timer(&sdata->u.mgd.bcn_mon_timer,
-                 round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME));
+                 round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout));
+}
+
+void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+               return;
+
+       mod_timer(&sdata->u.mgd.conn_mon_timer,
+                 round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
+
+       ifmgd->probe_send_count = 0;
 }
 
 static int ecw2cw(int ecw)
@@ -600,11 +625,12 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                        /*
                         * Go to full PSM if the user configures a very low
                         * latency requirement.
-                        * The 2 second value is there for compatibility until
-                        * the PM_QOS_NETWORK_LATENCY is configured with real
-                        * values.
+                        * The 2000 second value is there for compatibility
+                        * until the PM_QOS_NETWORK_LATENCY is configured
+                        * with real values.
                         */
-                       if (latency > 1900000000 && latency != 2000000000)
+                       if (latency > (1900 * USEC_PER_MSEC) &&
+                           latency != (2000 * USEC_PER_SEC))
                                timeout = 0;
                        else
                                timeout = 100;
@@ -778,16 +804,17 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                params.uapsd = uapsd;
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-               printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d "
-                      "cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
-                      wiphy_name(local->hw.wiphy), queue, aci, acm,
-                      params.aifs, params.cw_min, params.cw_max, params.txop,
-                      params.uapsd);
+               wiphy_debug(local->hw.wiphy,
+                           "WMM queue=%d aci=%d acm=%d aifs=%d "
+                           "cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
+                           queue, aci, acm,
+                           params.aifs, params.cw_min, params.cw_max,
+                           params.txop, params.uapsd);
 #endif
                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);
+                       wiphy_debug(local->hw.wiphy,
+                                   "failed to set TX queue parameters for queue %d\n",
+                                   queue);
        }
 
        /* enable WMM or activate new settings */
@@ -851,6 +878,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        bss_info_changed |= ieee80211_handle_bss_capability(sdata,
                cbss->capability, bss->has_erp_value, bss->erp_value);
 
+       sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec(
+               IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int));
+
        sdata->u.mgd.associated = cbss;
        memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
 
@@ -860,14 +890,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
                                IEEE80211_STA_BEACON_POLL);
 
-       /*
-        * Always handle WMM once after association regardless
-        * of the first value the AP uses. Setting -1 here has
-        * that effect because the AP values is an unsigned
-        * 4-bit value.
-        */
-       sdata->u.mgd.wmm_last_param_set = -1;
-
        ieee80211_led_assoc(local, 1);
 
        if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
@@ -901,7 +923,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 
        mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_ps(local, -1);
-       ieee80211_recalc_smps(local, sdata);
+       ieee80211_recalc_smps(local);
        mutex_unlock(&local->iflist_mtx);
 
        netif_tx_start_all_queues(sdata->dev);
@@ -909,7 +931,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 }
 
 static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
-                                  bool remove_sta)
+                                  bool remove_sta, bool tx)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
@@ -948,7 +970,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        sta = sta_info_get(sdata, bssid);
        if (sta) {
                set_sta_flags(sta, WLAN_STA_BLOCK_BA);
-               ieee80211_sta_tear_down_BA_sessions(sta);
+               ieee80211_sta_tear_down_BA_sessions(sta, tx);
        }
        mutex_unlock(&local->sta_mtx);
 
@@ -990,6 +1012,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        if (remove_sta)
                sta_info_destroy_addr(sdata, bssid);
+
+       del_timer_sync(&sdata->u.mgd.conn_mon_timer);
+       del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
+       del_timer_sync(&sdata->u.mgd.timer);
+       del_timer_sync(&sdata->u.mgd.chswitch_timer);
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1006,21 +1033,92 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
        if (is_multicast_ether_addr(hdr->addr1))
                return;
 
+       /*
+        * In case we receive frames after disassociation.
+        */
+       if (!sdata->u.mgd.associated)
+               return;
+
+       ieee80211_sta_reset_conn_monitor(sdata);
+}
+
+static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+       if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
+                             IEEE80211_STA_CONNECTION_POLL)))
+           return;
+
+       ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
+                         IEEE80211_STA_BEACON_POLL);
+       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;
 
-       mod_timer(&sdata->u.mgd.conn_mon_timer,
-                 round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
+       /*
+        * 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.
+        */
+       ieee80211_sta_reset_beacon_monitor(sdata);
+
+       mod_timer(&ifmgd->conn_mon_timer,
+                 round_jiffies_up(jiffies +
+                                  IEEE80211_CONNECTION_IDLE_TIME));
+}
+
+void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
+                            struct ieee80211_hdr *hdr, bool ack)
+{
+       if (!ieee80211_is_data(hdr->frame_control))
+           return;
+
+       if (ack)
+               ieee80211_sta_reset_conn_monitor(sdata);
+
+       if (ieee80211_is_nullfunc(hdr->frame_control) &&
+           sdata->u.mgd.probe_send_count > 0) {
+               if (ack)
+                       sdata->u.mgd.probe_send_count = 0;
+               else
+                       sdata->u.mgd.nullfunc_failed = true;
+               ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+       }
 }
 
 static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const u8 *ssid;
+       u8 *dst = ifmgd->associated->bssid;
+       u8 unicast_limit = max(1, IEEE80211_MAX_PROBE_TRIES - 3);
 
-       ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
-       ieee80211_send_probe_req(sdata, ifmgd->associated->bssid,
-                                ssid + 2, ssid[1], NULL, 0);
+       /*
+        * Try sending broadcast probe requests for the last three
+        * probe requests after the first ones failed since some
+        * buggy APs only support broadcast probe requests.
+        */
+       if (ifmgd->probe_send_count >= unicast_limit)
+               dst = NULL;
+
+       /*
+        * When the hardware reports an accurate Tx ACK status, it's
+        * better to send a nullfunc frame instead of a probe request,
+        * as it will kick us off the AP quickly if we aren't associated
+        * anymore. The timeout will be reset if the frame is ACKed by
+        * the AP.
+        */
+       if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
+               ifmgd->nullfunc_failed = false;
+               ieee80211_send_nullfunc(sdata->local, sdata, 0);
+       } else {
+               ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+               ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0);
+       }
 
        ifmgd->probe_send_count++;
        ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT;
@@ -1086,6 +1184,30 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
        mutex_unlock(&ifmgd->mtx);
 }
 
+struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
+                                         struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct sk_buff *skb;
+       const u8 *ssid;
+
+       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+               return NULL;
+
+       ASSERT_MGD_MTX(ifmgd);
+
+       if (!ifmgd->associated)
+               return NULL;
+
+       ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+       skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
+                                       ssid + 2, ssid[1], NULL, 0);
+
+       return skb;
+}
+EXPORT_SYMBOL(ieee80211_ap_probereq_get);
+
 static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1102,9 +1224,12 @@ 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, true);
-       ieee80211_recalc_idle(local);
+       ieee80211_set_disassoc(sdata, true, true);
        mutex_unlock(&ifmgd->mtx);
+
+       mutex_lock(&local->mtx);
+       ieee80211_recalc_idle(local);
+       mutex_unlock(&local->mtx);
        /*
         * must be outside lock due to cfg80211,
         * but that's not a problem.
@@ -1172,8 +1297,10 @@ 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, true);
+       ieee80211_set_disassoc(sdata, true, false);
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
        return RX_MGMT_CFG80211_DEAUTH;
 }
@@ -1202,8 +1329,10 @@ 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, true);
+       ieee80211_set_disassoc(sdata, true, false);
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
        return RX_MGMT_CFG80211_DISASSOC;
 }
 
@@ -1262,7 +1391,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
 
        rates = 0;
        basic_rates = 0;
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+       sband = local->hw.wiphy->bands[wk->chan->band];
 
        for (i = 0; i < elems.supp_rates_len; i++) {
                int rate = (elems.supp_rates[i] & 0x7f) * 5;
@@ -1298,11 +1427,11 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
                }
        }
 
-       sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
+       sta->sta.supp_rates[wk->chan->band] = rates;
        sdata->vif.bss_conf.basic_rates = basic_rates;
 
        /* cf. IEEE 802.11 9.2.12 */
-       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
+       if (wk->chan->band == IEEE80211_BAND_2GHZ &&
            have_higher_than_11mbit)
                sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
        else
@@ -1330,6 +1459,14 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
                return false;
        }
 
+       /*
+        * Always handle WMM once after association regardless
+        * of the first value the AP uses. Setting -1 here has
+        * that effect because the AP values is an unsigned
+        * 4-bit value.
+        */
+       ifmgd->wmm_last_param_set = -1;
+
        if (elems.wmm_param)
                ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
                                         elems.wmm_param_len);
@@ -1362,7 +1499,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
         * Also start the timer that will detect beacon loss.
         */
        ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
-       mod_beacon_timer(sdata);
+       ieee80211_sta_reset_beacon_monitor(sdata);
 
        return true;
 }
@@ -1448,29 +1585,8 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
 
        if (ifmgd->associated &&
-           memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0 &&
-           ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
-                           IEEE80211_STA_CONNECTION_POLL)) {
-               ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-                                 IEEE80211_STA_BEACON_POLL);
-               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));
-       }
+           memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0)
+               ieee80211_reset_ap_probe(sdata);
 }
 
 /*
@@ -1540,15 +1656,18 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        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->ave_beacon_signal = rx_status->signal * 16;
                ifmgd->last_cqm_event_signal = 0;
+               ifmgd->count_beacon_signal = 1;
        } else {
                ifmgd->ave_beacon_signal =
                        (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
                         (16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
                         ifmgd->ave_beacon_signal) / 16;
+               ifmgd->count_beacon_signal++;
        }
        if (bss_conf->cqm_rssi_thold &&
+           ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
            !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
                int sig = ifmgd->ave_beacon_signal / 16;
                int last_event = ifmgd->last_cqm_event_signal;
@@ -1588,7 +1707,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
         * Push the beacon loss detection into the future since
         * we are processing a beacon from the AP just now.
         */
-       mod_beacon_timer(sdata);
+       ieee80211_sta_reset_beacon_monitor(sdata);
 
        ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
        ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
@@ -1599,7 +1718,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
                                                   ifmgd->aid);
 
-       if (ncrc != ifmgd->beacon_crc) {
+       if (ncrc != ifmgd->beacon_crc || !ifmgd->beacon_crc_valid) {
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
                                      true);
 
@@ -1630,9 +1749,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       if (ncrc == ifmgd->beacon_crc)
+       if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
                return;
        ifmgd->beacon_crc = ncrc;
+       ifmgd->beacon_crc_valid = true;
 
        if (elems.erp_info && elems.erp_info_len >= 1) {
                erp_valid = true;
@@ -1751,7 +1871,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                struct ieee80211_local *local = sdata->local;
                struct ieee80211_work *wk;
 
-               mutex_lock(&local->work_mtx);
+               mutex_lock(&local->mtx);
                list_for_each_entry(wk, &local->work_list, list) {
                        if (wk->sdata != sdata)
                                continue;
@@ -1783,7 +1903,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                        free_work(wk);
                        break;
                }
-               mutex_unlock(&local->work_mtx);
+               mutex_unlock(&local->mtx);
 
                cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
        }
@@ -1804,6 +1924,31 @@ static void ieee80211_sta_timer(unsigned long data)
        ieee80211_queue_work(&local->hw, &sdata->work);
 }
 
+static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
+                                         u8 *bssid)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+       ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
+                         IEEE80211_STA_BEACON_POLL);
+
+       ieee80211_set_disassoc(sdata, true, true);
+       mutex_unlock(&ifmgd->mtx);
+       mutex_lock(&local->mtx);
+       ieee80211_recalc_idle(local);
+       mutex_unlock(&local->mtx);
+       /*
+        * must be outside lock due to cfg80211,
+        * but that's not a problem.
+        */
+       ieee80211_send_deauth_disassoc(sdata, bssid,
+                       IEEE80211_STYPE_DEAUTH,
+                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+                       NULL, true);
+       mutex_lock(&ifmgd->mtx);
+}
+
 void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
@@ -1816,17 +1961,56 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                            IEEE80211_STA_CONNECTION_POLL) &&
            ifmgd->associated) {
                u8 bssid[ETH_ALEN];
+               int max_tries;
 
                memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
-               if (time_is_after_jiffies(ifmgd->probe_timeout))
-                       run_again(ifmgd, ifmgd->probe_timeout);
 
-               else if (ifmgd->probe_send_count < IEEE80211_MAX_PROBE_TRIES) {
+               if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+                       max_tries = IEEE80211_MAX_NULLFUNC_TRIES;
+               else
+                       max_tries = IEEE80211_MAX_PROBE_TRIES;
+
+               /* ACK received for nullfunc probing frame */
+               if (!ifmgd->probe_send_count)
+                       ieee80211_reset_ap_probe(sdata);
+               else if (ifmgd->nullfunc_failed) {
+                       if (ifmgd->probe_send_count < max_tries) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-                       printk(KERN_DEBUG "No probe response from AP %pM"
-                               " after %dms, try %d\n", bssid,
-                               (1000 * IEEE80211_PROBE_WAIT)/HZ,
-                               ifmgd->probe_send_count);
+                               wiphy_debug(local->hw.wiphy,
+                                           "%s: No ack for nullfunc frame to"
+                                           " AP %pM, try %d\n",
+                                           sdata->name, bssid,
+                                           ifmgd->probe_send_count);
+#endif
+                               ieee80211_mgd_probe_ap_send(sdata);
+                       } else {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+                               wiphy_debug(local->hw.wiphy,
+                                           "%s: No ack for nullfunc frame to"
+                                           " AP %pM, disconnecting.\n",
+                                           sdata->name, bssid);
+#endif
+                               ieee80211_sta_connection_lost(sdata, bssid);
+                       }
+               } else if (time_is_after_jiffies(ifmgd->probe_timeout))
+                       run_again(ifmgd, ifmgd->probe_timeout);
+               else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+                       wiphy_debug(local->hw.wiphy,
+                                   "%s: Failed to send nullfunc to AP %pM"
+                                   " after %dms, disconnecting.\n",
+                                   sdata->name,
+                                   bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
+#endif
+                       ieee80211_sta_connection_lost(sdata, bssid);
+               } else if (ifmgd->probe_send_count < max_tries) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+                       wiphy_debug(local->hw.wiphy,
+                                   "%s: No probe response from AP %pM"
+                                   " after %dms, try %d\n",
+                                   sdata->name,
+                                   bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ,
+                                   ifmgd->probe_send_count);
 #endif
                        ieee80211_mgd_probe_ap_send(sdata);
                } else {
@@ -1834,23 +2018,13 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                         * We actually lost the connection ... or did we?
                         * Let's make sure!
                         */
-                       ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-                                         IEEE80211_STA_BEACON_POLL);
-                       printk(KERN_DEBUG "No probe response from AP %pM"
-                               " after %dms, disconnecting.\n",
-                               bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
-                       ieee80211_set_disassoc(sdata, true);
-                       ieee80211_recalc_idle(local);
-                       mutex_unlock(&ifmgd->mtx);
-                       /*
-                        * must be outside lock due to cfg80211,
-                        * but that's not a problem.
-                        */
-                       ieee80211_send_deauth_disassoc(sdata, bssid,
-                                       IEEE80211_STYPE_DEAUTH,
-                                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                                       NULL, true);
-                       mutex_lock(&ifmgd->mtx);
+                       wiphy_debug(local->hw.wiphy,
+                                   "%s: No probe response from AP %pM"
+                                   " after %dms, disconnecting.\n",
+                                   sdata->name,
+                                   bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
+
+                       ieee80211_sta_connection_lost(sdata, bssid);
                }
        }
 
@@ -1917,6 +2091,8 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
         * time -- the code here is properly synchronised.
         */
 
+       cancel_work_sync(&ifmgd->request_smps_work);
+
        cancel_work_sync(&ifmgd->beacon_connection_loss_work);
        if (del_timer_sync(&ifmgd->timer))
                set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
@@ -1939,6 +2115,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
                add_timer(&ifmgd->timer);
        if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
                add_timer(&ifmgd->chswitch_timer);
+       ieee80211_sta_reset_beacon_monitor(sdata);
+       ieee80211_restart_sta_timer(sdata);
 }
 #endif
 
@@ -1952,6 +2130,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
        INIT_WORK(&ifmgd->beacon_connection_loss_work,
                  ieee80211_beacon_connection_loss_work);
+       INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -2158,7 +2337,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                }
 
                /* Trying to reassociate - clear previous association state */
-               ieee80211_set_disassoc(sdata, true);
+               ieee80211_set_disassoc(sdata, true, false);
        }
        mutex_unlock(&ifmgd->mtx);
 
@@ -2169,6 +2348,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
        ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
 
+       ifmgd->beacon_crc_valid = false;
+
        for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
                if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
@@ -2249,6 +2430,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        else
                ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
 
+       sdata->control_port_protocol = req->crypto.control_port_ethertype;
+       sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
+
        ieee80211_add_work(wk);
        return 0;
 }
@@ -2267,7 +2451,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
        memcpy(bssid, req->bss->bssid, ETH_ALEN);
        if (ifmgd->associated == req->bss) {
-               ieee80211_set_disassoc(sdata, false);
+               ieee80211_set_disassoc(sdata, false, true);
                mutex_unlock(&ifmgd->mtx);
                assoc_bss = true;
        } else {
@@ -2275,7 +2459,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
                mutex_unlock(&ifmgd->mtx);
 
-               mutex_lock(&local->work_mtx);
+               mutex_lock(&local->mtx);
                list_for_each_entry(wk, &local->work_list, list) {
                        if (wk->sdata != sdata)
                                continue;
@@ -2294,7 +2478,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                        free_work(wk);
                        break;
                }
-               mutex_unlock(&local->work_mtx);
+               mutex_unlock(&local->mtx);
 
                /*
                 * If somebody requests authentication and we haven't
@@ -2319,7 +2503,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
        if (assoc_bss)
                sta_info_destroy_addr(sdata, bssid);
 
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
        return 0;
 }
@@ -2348,7 +2534,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
               sdata->name, req->bss->bssid, req->reason_code);
 
        memcpy(bssid, req->bss->bssid, ETH_ALEN);
-       ieee80211_set_disassoc(sdata, false);
+       ieee80211_set_disassoc(sdata, false, true);
 
        mutex_unlock(&ifmgd->mtx);
 
@@ -2357,7 +2543,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                        cookie, !req->local_state_change);
        sta_info_destroy_addr(sdata, bssid);
 
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
        return 0;
 }