mac80211: update to latest wireless-testing + more stability work
authorFelix Fietkau <nbd@openwrt.org>
Sat, 20 Nov 2010 02:26:41 +0000 (02:26 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sat, 20 Nov 2010 02:26:41 +0000 (02:26 +0000)
SVN-Revision: 24044

package/mac80211/Makefile
package/mac80211/patches/300-ath9k_gpio_settings.patch
package/mac80211/patches/310-pending_cfg80211_fixes.patch [new file with mode: 0644]
package/mac80211/patches/310-pending_work.patch [deleted file]
package/mac80211/patches/311-pending_work.patch [new file with mode: 0644]

index 769fcec2e9d1267d11191bb592d652d65d3991f5..f7536950f1e5b94e343b405e393a14919cda3f3a 100644 (file)
@@ -10,12 +10,12 @@ include $(INCLUDE_DIR)/kernel.mk
 
 PKG_NAME:=mac80211
 
-PKG_VERSION:=2010-11-16
+PKG_VERSION:=2010-11-20
 PKG_RELEASE:=1
 PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources
 #      http://www.orbit-lab.org/kernel/compat-wireless-2.6/2010/11 \
 #      http://wireless.kernel.org/download/compat-wireless-2.6
-PKG_MD5SUM:=eb996cb637326203544753d3a05a3979
+PKG_MD5SUM:=2c9ca2ff2d8c20dca33e72d271219c3c
 
 PKG_SOURCE:=compat-wireless-$(PKG_VERSION).tar.bz2
 PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/compat-wireless-$(PKG_VERSION)
index a7ef6fa8948c34dd642609f6a3252572a44e2f2b..a7cf49a29f03b82c304efd05190fe712e3c0079d 100644 (file)
@@ -26,7 +26,7 @@
        struct ar5416IniArray iniCommon;
 --- a/drivers/net/wireless/ath/ath9k/hw.c
 +++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -1182,6 +1182,20 @@ static bool ath9k_hw_channel_change(stru
+@@ -1178,6 +1178,20 @@ static bool ath9k_hw_channel_change(stru
        return true;
  }
  
@@ -47,7 +47,7 @@
  bool ath9k_hw_check_alive(struct ath_hw *ah)
  {
        int count = 50;
-@@ -1468,6 +1482,8 @@ int ath9k_hw_reset(struct ath_hw *ah, st
+@@ -1464,6 +1478,8 @@ int ath9k_hw_reset(struct ath_hw *ah, st
        if (AR_SREV_9300_20_OR_LATER(ah))
                ar9003_hw_bb_watchdog_config(ah);
  
diff --git a/package/mac80211/patches/310-pending_cfg80211_fixes.patch b/package/mac80211/patches/310-pending_cfg80211_fixes.patch
new file mode 100644 (file)
index 0000000..f8334b4
--- /dev/null
@@ -0,0 +1,178 @@
+--- a/include/net/regulatory.h
++++ b/include/net/regulatory.h
+@@ -43,6 +43,12 @@ enum environment_cap {
+  * @intersect: indicates whether the wireless core should intersect
+  *    the requested regulatory domain with the presently set regulatory
+  *    domain.
++ * @processed: indicates whether or not this requests has already been
++ *    processed. When the last request is processed it means that the
++ *    currently regulatory domain set on cfg80211 is updated from
++ *    CRDA and can be used by other regulatory requests. When a
++ *    the last request is not yet processed we must yield until it
++ *    is processed before processing any new requests.
+  * @country_ie_checksum: checksum of the last processed and accepted
+  *    country IE
+  * @country_ie_env: lets us know if the AP is telling us we are outdoor,
+@@ -54,6 +60,7 @@ struct regulatory_request {
+       enum nl80211_reg_initiator initiator;
+       char alpha2[2];
+       bool intersect;
++      bool processed;
+       enum environment_cap country_ie_env;
+       struct list_head list;
+ };
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -96,6 +96,9 @@ struct reg_beacon {
+       struct ieee80211_channel chan;
+ };
++static void reg_todo(struct work_struct *work);
++static DECLARE_WORK(reg_work, reg_todo);
++
+ /* We keep a static world regulatory domain in case of the absence of CRDA */
+ static const struct ieee80211_regdomain world_regdom = {
+       .n_reg_rules = 5,
+@@ -1317,6 +1320,21 @@ static int ignore_request(struct wiphy *
+       return -EINVAL;
+ }
++static void reg_set_request_processed(void)
++{
++      bool need_more_processing = false;
++
++      last_request->processed = true;
++
++      spin_lock(&reg_requests_lock);
++      if (!list_empty(&reg_requests_list))
++              need_more_processing = true;
++      spin_unlock(&reg_requests_lock);
++
++      if (need_more_processing)
++              schedule_work(&reg_work);
++}
++
+ /**
+  * __regulatory_hint - hint to the wireless core a regulatory domain
+  * @wiphy: if the hint comes from country information from an AP, this
+@@ -1392,8 +1410,10 @@ new_request:
+                * have applied the requested regulatory domain before we just
+                * inform userspace we have processed the request
+                */
+-              if (r == -EALREADY)
++              if (r == -EALREADY) {
+                       nl80211_send_reg_change_event(last_request);
++                      reg_set_request_processed();
++              }
+               return r;
+       }
+@@ -1409,16 +1429,13 @@ static void reg_process_hint(struct regu
+       BUG_ON(!reg_request->alpha2);
+-      mutex_lock(&cfg80211_mutex);
+-      mutex_lock(&reg_mutex);
+-
+       if (wiphy_idx_valid(reg_request->wiphy_idx))
+               wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
+       if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+           !wiphy) {
+               kfree(reg_request);
+-              goto out;
++              return;
+       }
+       r = __regulatory_hint(wiphy, reg_request);
+@@ -1426,28 +1443,46 @@ static void reg_process_hint(struct regu
+       if (r == -EALREADY && wiphy &&
+           wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+               wiphy_update_regulatory(wiphy, initiator);
+-out:
+-      mutex_unlock(&reg_mutex);
+-      mutex_unlock(&cfg80211_mutex);
+ }
+-/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */
++/*
++ * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
++ * Regulatory hints come on a first come first serve basis and we
++ * must process each one atomically.
++ */
+ static void reg_process_pending_hints(void)
+-      {
++{
+       struct regulatory_request *reg_request;
++      mutex_lock(&cfg80211_mutex);
++      mutex_lock(&reg_mutex);
++
++      /* When last_request->processed becomes true this will be rescheduled */
++      if (last_request && !last_request->processed) {
++              REG_DBG_PRINT("Pending regulatory request, waiting "
++                            "for it to be processed...");
++              goto out;
++      }
++
+       spin_lock(&reg_requests_lock);
+-      while (!list_empty(&reg_requests_list)) {
+-              reg_request = list_first_entry(&reg_requests_list,
+-                                             struct regulatory_request,
+-                                             list);
+-              list_del_init(&reg_request->list);
++      if (list_empty(&reg_requests_list)) {
+               spin_unlock(&reg_requests_lock);
+-              reg_process_hint(reg_request);
+-              spin_lock(&reg_requests_lock);
++              goto out;
+       }
++
++      reg_request = list_first_entry(&reg_requests_list,
++                                     struct regulatory_request,
++                                     list);
++      list_del_init(&reg_request->list);
++
+       spin_unlock(&reg_requests_lock);
++
++      reg_process_hint(reg_request);
++
++out:
++      mutex_unlock(&reg_mutex);
++      mutex_unlock(&cfg80211_mutex);
+ }
+ /* Processes beacon hints -- this has nothing to do with country IEs */
+@@ -1494,8 +1529,6 @@ static void reg_todo(struct work_struct 
+       reg_process_pending_beacon_hints();
+ }
+-static DECLARE_WORK(reg_work, reg_todo);
+-
+ static void queue_regulatory_request(struct regulatory_request *request)
+ {
+       if (isalpha(request->alpha2[0]))
+@@ -1530,12 +1563,7 @@ static int regulatory_hint_core(const ch
+       request->alpha2[1] = alpha2[1];
+       request->initiator = NL80211_REGDOM_SET_BY_CORE;
+-      /*
+-       * This ensures last_request is populated once modules
+-       * come swinging in and calling regulatory hints and
+-       * wiphy_apply_custom_regulatory().
+-       */
+-      reg_process_hint(request);
++      queue_regulatory_request(request);
+       return 0;
+ }
+@@ -2061,6 +2089,8 @@ int set_regdom(const struct ieee80211_re
+       nl80211_send_reg_change_event(last_request);
++      reg_set_request_processed();
++
+       mutex_unlock(&reg_mutex);
+       return r;
diff --git a/package/mac80211/patches/310-pending_work.patch b/package/mac80211/patches/310-pending_work.patch
deleted file mode 100644 (file)
index 9691888..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
---- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
-+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
-@@ -3271,6 +3271,18 @@ static bool ar9300_check_eeprom_header(s
-       return ar9300_check_header(header);
- }
-+static int ar9300_eeprom_restore_flash(struct ath_hw *ah, u8 *mptr,
-+                                     int mdata_size)
-+{
-+      struct ath_common *common = ath9k_hw_common(ah);
-+      u16 *data = (u16 *) mptr;
-+      int i;
-+
-+      for (i = 0; i < mdata_size / 2; i++, data++)
-+              ath9k_hw_nvram_read(common, i, data);
-+
-+      return 0;
-+}
- /*
-  * Read the configuration data from the eeprom.
-  * The data can be put in any specified memory buffer.
-@@ -3293,6 +3305,9 @@ static int ar9300_eeprom_restore_interna
-       struct ath_common *common = ath9k_hw_common(ah);
-       eeprom_read_op read;
-+      if (ath9k_hw_use_flash(ah))
-+              return ar9300_eeprom_restore_flash(ah, mptr, mdata_size);
-+
-       word = kzalloc(2048, GFP_KERNEL);
-       if (!word)
-               return -1;
---- a/drivers/net/wireless/ath/ath9k/hw.c
-+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -419,10 +419,6 @@ static void ath9k_hw_init_defaults(struc
-       ah->hw_version.magic = AR5416_MAGIC;
-       ah->hw_version.subvendorid = 0;
--      ah->ah_flags = 0;
--      if (!AR_SREV_9100(ah))
--              ah->ah_flags = AH_USE_EEPROM;
--
-       ah->atim_window = 0;
-       ah->sta_id1_defaults =
-               AR_STA_ID1_CRPT_MIC_ENABLE |
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -533,6 +533,9 @@ static int ath9k_init_softc(u16 devid, s
-       ah->hw_version.subsysid = subsysid;
-       sc->sc_ah = ah;
-+      if (!sc->dev->platform_data)
-+              ah->ah_flags |= AH_USE_EEPROM;
-+
-       common = ath9k_hw_common(ah);
-       common->ops = &ath9k_common_ops;
-       common->bus_ops = bus_ops;
---- a/drivers/net/wireless/ath/ath9k/pci.c
-+++ b/drivers/net/wireless/ath/ath9k/pci.c
-@@ -16,6 +16,7 @@
- #include <linux/nl80211.h>
- #include <linux/pci.h>
-+#include <linux/ath9k_platform.h>
- #include "ath9k.h"
- static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
-@@ -53,21 +54,36 @@ static void ath_pci_read_cachesize(struc
- static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data)
- {
--      struct ath_hw *ah = (struct ath_hw *) common->ah;
-+      struct ath_softc *sc = (struct ath_softc *) common->priv;
-+      struct ath9k_platform_data *pdata = sc->dev->platform_data;
--      common->ops->read(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
-+      if (pdata) {
-+              if (off >= (ARRAY_SIZE(pdata->eeprom_data))) {
-+                      ath_print(common, ATH_DBG_FATAL,
-+                                "%s: eeprom read failed, offset %08x "
-+                                "is out of range\n",
-+                                __func__, off);
-+              }
-+
-+              *data = pdata->eeprom_data[off];
-+      } else {
-+              struct ath_hw *ah = (struct ath_hw *) common->ah;
-+
-+              common->ops->read(ah, AR5416_EEPROM_OFFSET +
-+                                    (off << AR5416_EEPROM_S));
-+
-+              if (!ath9k_hw_wait(ah,
-+                                 AR_EEPROM_STATUS_DATA,
-+                                 AR_EEPROM_STATUS_DATA_BUSY |
-+                                 AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0,
-+                                 AH_WAIT_TIMEOUT)) {
-+                      return false;
-+              }
--      if (!ath9k_hw_wait(ah,
--                         AR_EEPROM_STATUS_DATA,
--                         AR_EEPROM_STATUS_DATA_BUSY |
--                         AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0,
--                         AH_WAIT_TIMEOUT)) {
--              return false;
-+              *data = MS(common->ops->read(ah, AR_EEPROM_STATUS_DATA),
-+                         AR_EEPROM_STATUS_DATA_VAL);
-       }
--      *data = MS(common->ops->read(ah, AR_EEPROM_STATUS_DATA),
--                 AR_EEPROM_STATUS_DATA_VAL);
--
-       return true;
- }
---- a/include/net/regulatory.h
-+++ b/include/net/regulatory.h
-@@ -43,6 +43,12 @@ enum environment_cap {
-  * @intersect: indicates whether the wireless core should intersect
-  *    the requested regulatory domain with the presently set regulatory
-  *    domain.
-+ * @processed: indicates whether or not this requests has already been
-+ *    processed. When the last request is processed it means that the
-+ *    currently regulatory domain set on cfg80211 is updated from
-+ *    CRDA and can be used by other regulatory requests. When a
-+ *    the last request is not yet processed we must yield until it
-+ *    is processed before processing any new requests.
-  * @country_ie_checksum: checksum of the last processed and accepted
-  *    country IE
-  * @country_ie_env: lets us know if the AP is telling us we are outdoor,
-@@ -54,6 +60,7 @@ struct regulatory_request {
-       enum nl80211_reg_initiator initiator;
-       char alpha2[2];
-       bool intersect;
-+      bool processed;
-       enum environment_cap country_ie_env;
-       struct list_head list;
- };
---- a/net/wireless/reg.c
-+++ b/net/wireless/reg.c
-@@ -96,6 +96,9 @@ struct reg_beacon {
-       struct ieee80211_channel chan;
- };
-+static void reg_todo(struct work_struct *work);
-+static DECLARE_WORK(reg_work, reg_todo);
-+
- /* We keep a static world regulatory domain in case of the absence of CRDA */
- static const struct ieee80211_regdomain world_regdom = {
-       .n_reg_rules = 5,
-@@ -1317,6 +1320,21 @@ static int ignore_request(struct wiphy *
-       return -EINVAL;
- }
-+static void reg_set_request_processed(void)
-+{
-+      bool need_more_processing = false;
-+
-+      last_request->processed = true;
-+
-+      spin_lock(&reg_requests_lock);
-+      if (!list_empty(&reg_requests_list))
-+              need_more_processing = true;
-+      spin_unlock(&reg_requests_lock);
-+
-+      if (need_more_processing)
-+              schedule_work(&reg_work);
-+}
-+
- /**
-  * __regulatory_hint - hint to the wireless core a regulatory domain
-  * @wiphy: if the hint comes from country information from an AP, this
-@@ -1392,8 +1410,10 @@ new_request:
-                * have applied the requested regulatory domain before we just
-                * inform userspace we have processed the request
-                */
--              if (r == -EALREADY)
-+              if (r == -EALREADY) {
-                       nl80211_send_reg_change_event(last_request);
-+                      reg_set_request_processed();
-+              }
-               return r;
-       }
-@@ -1409,16 +1429,13 @@ static void reg_process_hint(struct regu
-       BUG_ON(!reg_request->alpha2);
--      mutex_lock(&cfg80211_mutex);
--      mutex_lock(&reg_mutex);
--
-       if (wiphy_idx_valid(reg_request->wiphy_idx))
-               wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
-       if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
-           !wiphy) {
-               kfree(reg_request);
--              goto out;
-+              return;
-       }
-       r = __regulatory_hint(wiphy, reg_request);
-@@ -1426,28 +1443,46 @@ static void reg_process_hint(struct regu
-       if (r == -EALREADY && wiphy &&
-           wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
-               wiphy_update_regulatory(wiphy, initiator);
--out:
--      mutex_unlock(&reg_mutex);
--      mutex_unlock(&cfg80211_mutex);
- }
--/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */
-+/*
-+ * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
-+ * Regulatory hints come on a first come first serve basis and we
-+ * must process each one atomically.
-+ */
- static void reg_process_pending_hints(void)
--      {
-+{
-       struct regulatory_request *reg_request;
-+      mutex_lock(&cfg80211_mutex);
-+      mutex_lock(&reg_mutex);
-+
-+      /* When last_request->processed becomes true this will be rescheduled */
-+      if (last_request && !last_request->processed) {
-+              REG_DBG_PRINT("Pending regulatory request, waiting "
-+                            "for it to be processed...");
-+              goto out;
-+      }
-+
-       spin_lock(&reg_requests_lock);
--      while (!list_empty(&reg_requests_list)) {
--              reg_request = list_first_entry(&reg_requests_list,
--                                             struct regulatory_request,
--                                             list);
--              list_del_init(&reg_request->list);
-+      if (list_empty(&reg_requests_list)) {
-               spin_unlock(&reg_requests_lock);
--              reg_process_hint(reg_request);
--              spin_lock(&reg_requests_lock);
-+              goto out;
-       }
-+
-+      reg_request = list_first_entry(&reg_requests_list,
-+                                     struct regulatory_request,
-+                                     list);
-+      list_del_init(&reg_request->list);
-+
-       spin_unlock(&reg_requests_lock);
-+
-+      reg_process_hint(reg_request);
-+
-+out:
-+      mutex_unlock(&reg_mutex);
-+      mutex_unlock(&cfg80211_mutex);
- }
- /* Processes beacon hints -- this has nothing to do with country IEs */
-@@ -1494,8 +1529,6 @@ static void reg_todo(struct work_struct 
-       reg_process_pending_beacon_hints();
- }
--static DECLARE_WORK(reg_work, reg_todo);
--
- static void queue_regulatory_request(struct regulatory_request *request)
- {
-       if (isalpha(request->alpha2[0]))
-@@ -1530,12 +1563,7 @@ static int regulatory_hint_core(const ch
-       request->alpha2[1] = alpha2[1];
-       request->initiator = NL80211_REGDOM_SET_BY_CORE;
--      /*
--       * This ensures last_request is populated once modules
--       * come swinging in and calling regulatory hints and
--       * wiphy_apply_custom_regulatory().
--       */
--      reg_process_hint(request);
-+      queue_regulatory_request(request);
-       return 0;
- }
-@@ -2061,6 +2089,8 @@ int set_regdom(const struct ieee80211_re
-       nl80211_send_reg_change_event(last_request);
-+      reg_set_request_processed();
-+
-       mutex_unlock(&reg_mutex);
-       return r;
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -54,8 +54,10 @@ static bool can_beacon_sec_chan(struct w
-       switch (channel_type) {
-       case NL80211_CHAN_HT40PLUS:
-               diff = 20;
-+              break;
-       case NL80211_CHAN_HT40MINUS:
-               diff = -20;
-+              break;
-       default:
-               return false;
-       }
diff --git a/package/mac80211/patches/311-pending_work.patch b/package/mac80211/patches/311-pending_work.patch
new file mode 100644 (file)
index 0000000..68b5723
--- /dev/null
@@ -0,0 +1,279 @@
+--- a/drivers/net/wireless/ath/ath9k/mac.c
++++ b/drivers/net/wireless/ath/ath9k/mac.c
+@@ -766,14 +766,6 @@ void ath9k_hw_startpcureceive(struct ath
+ }
+ EXPORT_SYMBOL(ath9k_hw_startpcureceive);
+-void ath9k_hw_stoppcurecv(struct ath_hw *ah)
+-{
+-      REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
+-
+-      ath9k_hw_disable_mib_counters(ah);
+-}
+-EXPORT_SYMBOL(ath9k_hw_stoppcurecv);
+-
+ void ath9k_hw_abortpcurecv(struct ath_hw *ah)
+ {
+       REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_ABORT | AR_DIAG_RX_DIS);
+--- a/drivers/net/wireless/ath/ath9k/mac.h
++++ b/drivers/net/wireless/ath/ath9k/mac.h
+@@ -691,7 +691,6 @@ void ath9k_hw_setuprxdesc(struct ath_hw 
+ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set);
+ void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp);
+ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning);
+-void ath9k_hw_stoppcurecv(struct ath_hw *ah);
+ void ath9k_hw_abortpcurecv(struct ath_hw *ah);
+ bool ath9k_hw_stopdmarecv(struct ath_hw *ah);
+ int ath9k_hw_beaconq_setup(struct ath_hw *ah);
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -518,7 +518,7 @@ bool ath_stoprecv(struct ath_softc *sc)
+       bool stopped;
+       spin_lock_bh(&sc->rx.rxbuflock);
+-      ath9k_hw_stoppcurecv(ah);
++      ath9k_hw_abortpcurecv(ah);
+       ath9k_hw_setrxfilter(ah, 0);
+       stopped = ath9k_hw_stopdmarecv(ah);
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -163,6 +163,7 @@ static void ath_tx_flush_tid(struct ath_
+               bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
+               list_move_tail(&bf->list, &bf_head);
++              spin_unlock_bh(&txq->axq_lock);
+               fi = get_frame_info(bf->bf_mpdu);
+               if (fi->retries) {
+                       ath_tx_update_baw(sc, tid, fi->seqno);
+@@ -170,6 +171,7 @@ static void ath_tx_flush_tid(struct ath_
+               } else {
+                       ath_tx_send_normal(sc, txq, tid, &bf_head);
+               }
++              spin_lock_bh(&txq->axq_lock);
+       }
+       spin_unlock_bh(&txq->axq_lock);
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -349,6 +349,7 @@ struct ieee80211_if_managed {
+       struct work_struct chswitch_work;
+       struct work_struct beacon_connection_loss_work;
++      unsigned long beacon_timeout;
+       unsigned long probe_timeout;
+       int probe_send_count;
+@@ -1269,6 +1270,8 @@ void ieee80211_send_nullfunc(struct ieee
+                            int powersave);
+ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+                            struct ieee80211_hdr *hdr);
++void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
++                           struct ieee80211_hdr *hdr);
+ void ieee80211_beacon_connection_loss_work(struct work_struct *work);
+ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -28,13 +28,19 @@
+ #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.
+@@ -45,7 +51,7 @@
+  * a probe request because of beacon loss or for
+  * checking the connection still works.
+  */
+-#define IEEE80211_PROBE_WAIT          (HZ / 2)
++#define IEEE80211_PROBE_WAIT          (HZ / 5)
+ /*
+  * Weight given to the latest Beacon frame when calculating average signal
+@@ -121,7 +127,7 @@ void ieee80211_sta_reset_beacon_monitor(
+               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)
+@@ -871,6 +877,9 @@ static void ieee80211_set_associated(str
+       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);
+@@ -1026,6 +1035,50 @@ void ieee80211_sta_rx_notify(struct ieee
+       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)
++{
++      if (!ieee80211_is_data(hdr->frame_control) &&
++          !ieee80211_is_nullfunc(hdr->frame_control))
++          return;
++
++      ieee80211_sta_reset_conn_monitor(sdata);
++
++      if (ieee80211_is_nullfunc(hdr->frame_control)) {
++              sdata->u.mgd.probe_send_count = 0;
++              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 +1094,19 @@ static void ieee80211_mgd_probe_ap_send(
+       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)
++              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;
+@@ -1509,29 +1573,8 @@ static void ieee80211_rx_mgmt_probe_resp
+       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);
+ }
+ /*
+@@ -1881,12 +1924,22 @@ void ieee80211_sta_work(struct ieee80211
+                           IEEE80211_STA_CONNECTION_POLL) &&
+           ifmgd->associated) {
+               u8 bssid[ETH_ALEN];
++              int max_tries;
++
++              /* ACK received for nullfunc probing frame */
++              if (!ifmgd->probe_send_count)
++                      ieee80211_reset_ap_probe(sdata);
++
++              if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
++                      max_tries = IEEE80211_MAX_NULLFUNC_TRIES;
++              else
++                      max_tries = IEEE80211_MAX_PROBE_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) {
++              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"
+@@ -2012,6 +2065,8 @@ void ieee80211_sta_restart(struct ieee80
+               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_send_nullfunc(sdata->local, sdata, 0);
+ }
+ #endif
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -155,6 +155,10 @@ static void ieee80211_frame_acked(struct
+               ieee80211_queue_work(&local->hw, &local->recalc_smps);
+       }
++
++      if ((sdata->vif.type == NL80211_IFTYPE_STATION) &&
++          (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
++              ieee80211_sta_tx_notify(sdata, (void *) skb->data);
+ }
+ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)