From: Felix Fietkau Date: Sat, 20 Nov 2010 02:26:41 +0000 (+0000) Subject: mac80211: update to latest wireless-testing + more stability work X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=0e2ad3a26609a4654d2ba14f7295a44671db5878;p=openwrt%2Fstaging%2Frmilecki.git mac80211: update to latest wireless-testing + more stability work SVN-Revision: 24044 --- diff --git a/package/mac80211/Makefile b/package/mac80211/Makefile index 769fcec2e9d..f7536950f1e 100644 --- a/package/mac80211/Makefile +++ b/package/mac80211/Makefile @@ -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) diff --git a/package/mac80211/patches/300-ath9k_gpio_settings.patch b/package/mac80211/patches/300-ath9k_gpio_settings.patch index a7ef6fa8948..a7cf49a29f0 100644 --- a/package/mac80211/patches/300-ath9k_gpio_settings.patch +++ b/package/mac80211/patches/300-ath9k_gpio_settings.patch @@ -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 index 00000000000..f8334b447e2 --- /dev/null +++ b/package/mac80211/patches/310-pending_cfg80211_fixes.patch @@ -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(®_requests_lock); ++ if (!list_empty(®_requests_list)) ++ need_more_processing = true; ++ spin_unlock(®_requests_lock); ++ ++ if (need_more_processing) ++ schedule_work(®_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(®_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(®_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(®_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(®_requests_lock); +- while (!list_empty(®_requests_list)) { +- reg_request = list_first_entry(®_requests_list, +- struct regulatory_request, +- list); +- list_del_init(®_request->list); + ++ if (list_empty(®_requests_list)) { + spin_unlock(®_requests_lock); +- reg_process_hint(reg_request); +- spin_lock(®_requests_lock); ++ goto out; + } ++ ++ reg_request = list_first_entry(®_requests_list, ++ struct regulatory_request, ++ list); ++ list_del_init(®_request->list); ++ + spin_unlock(®_requests_lock); ++ ++ reg_process_hint(reg_request); ++ ++out: ++ mutex_unlock(®_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(®_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 index 96918888d3e..00000000000 --- a/package/mac80211/patches/310-pending_work.patch +++ /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 - #include -+#include - #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(®_requests_lock); -+ if (!list_empty(®_requests_list)) -+ need_more_processing = true; -+ spin_unlock(®_requests_lock); -+ -+ if (need_more_processing) -+ schedule_work(®_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(®_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(®_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(®_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(®_requests_lock); -- while (!list_empty(®_requests_list)) { -- reg_request = list_first_entry(®_requests_list, -- struct regulatory_request, -- list); -- list_del_init(®_request->list); - -+ if (list_empty(®_requests_list)) { - spin_unlock(®_requests_lock); -- reg_process_hint(reg_request); -- spin_lock(®_requests_lock); -+ goto out; - } -+ -+ reg_request = list_first_entry(®_requests_list, -+ struct regulatory_request, -+ list); -+ list_del_init(®_request->list); -+ - spin_unlock(®_requests_lock); -+ -+ reg_process_hint(reg_request); -+ -+out: -+ mutex_unlock(®_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(®_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 index 00000000000..68b57234fc2 --- /dev/null +++ b/package/mac80211/patches/311-pending_work.patch @@ -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)