]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/mac80211/mlme.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / net / mac80211 / mlme.c
index a3a9421555af30201e13d8ea24c36f4bb759680f..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.
@@ -121,7 +127,7 @@ void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
                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)
@@ -619,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;
@@ -871,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);
 
@@ -1023,9 +1033,63 @@ 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;
+
+       /*
+        * 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;
@@ -1041,8 +1105,20 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
        if (ifmgd->probe_send_count >= unicast_limit)
                dst = NULL;
 
-       ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
-       ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0);
+       /*
+        * 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;
@@ -1108,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;
@@ -1485,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.
-                */
-               ieee80211_sta_reset_beacon_monitor(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);
 }
 
 /*
@@ -1845,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;
@@ -1857,12 +1961,49 @@ 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
+                               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"
@@ -1877,27 +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);
                        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_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);
+
+                       ieee80211_sta_connection_lost(sdata, bssid);
                }
        }
 
@@ -1988,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