Sophie

Sophie

distrib > CentOS > 5 > i386 > by-pkgid > ea32411352494358b8d75a78402a4713 > files > 3340

kernel-2.6.18-238.19.1.el5.centos.plus.src.rpm

From: John Linville <linville@redhat.com>
Date: Mon, 1 Feb 2010 18:40:59 -0500
Subject: [net] wireless fixes from 2.6.32.2
Message-id: <20100201184058.GD28565@redhat.com>
Patchwork-id: 23056
O-Subject: [RHEL5 Patch 1/4] wireless fixes from 2.6.32.2
Bugzilla: 559711
RH-Acked-by: David S. Miller <davem@redhat.com>
RH-Acked-by: Stanislaw Gruszka <sgruszka@redhat.com>

On Mon, Feb 01, 2010 at 01:36:11PM -0500, John W. Linville wrote:
> As you may know, the mac80211-based wireless bits in RHEL 5.5 are
> basically identical to the equivalent components in 2.6.32.  Since it's
> release, there have been a number of fixes propogated to the 2.6.32
> -stable tree.  This series brings those fixes to the RHEL 5.5 kernel.
>
> BZ559711

These are the bits included in 2.6.32.2.  FWIW, 2.6.32.1 didn't have
any wireless fixes applied.

Reasonably extensive "real world" testing by me, with positive
results...

commit 58cd74bafb36b884abe67998cdef4203ae01769f
Author: Vivek Natarajan <vnatarajan@atheros.com>
Date:   Wed Dec 16 11:51:45 2009 -0500

    mac80211: Fix dynamic power save for scanning.

    Upstream commit: 7c3f4bbedc241ddcd3abe1f419c356e625231da1

    Not only ps_sdata but also IEEE80211_CONF_PS is to be considered
    before restoring PS in scan_ps_disable(). For instance, when ps_sdata
    is set but CONF_PS is not set just because the dynamic timer is still
    running, a sw scan leads to setting of CONF_PS in scan_ps_disable
    instead of restarting the dynamic PS timer.
    Also for the above case, a null data frame is to be sent after
    returning to operating channel which was not happening with the
    current implementation. This patch fixes this too.

    Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
    Reviewed-by: Kalle Valo <kalle.valo@nokia.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit 0f77b958dccf9d3b59d986ea160f84b50c646712
Author: Felix Fietkau <nbd@openwrt.org>
Date:   Wed Dec 16 11:51:44 2009 -0500

    ath9k: fix tx status reporting

    This is a backport of upstream commit: 815833e7ecf0b9a017315cae6aef4d7cd9517681

    This patch fixes a bug in ath9k's tx status check, which
    caused mac80211 to consider regularly transmitted unicast frames
    as un-acked.

    When checking the ts_status field for errors, it needs to be masked
    with ATH9K_TXERR_FILT, because this field also contains other fields
    like ATH9K_TX_ACKED.

    Without this patch, AP mode is pretty much unusable, as hostapd
    checks the ACK status for the frames that it injects.

    Signed-off-by: Felix Fietkau <nbd@openwrt.org>
    Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit e82a3a2db53b6df3a211c7b90c793d61fcb6ddae
Author: Luis R. Rodriguez <lrodriguez@atheros.com>
Date:   Wed Dec 16 11:51:43 2009 -0500

    ath9k: Fix maximum tx fifo settings for single stream devices

    This is a backport of upstream commit: f4709fdf683e1ed37b321c258b614ebe39752bf3

    Atheros single stream AR9285 and AR9271 have half the PCU TX FIFO
    buffer size of that of dual stream devices. Dual stream devices
    have a max PCU TX FIFO size of 8 KB while single stream devices
    have 4 KB. Single stream devices have an issue though and require
    hardware only to use half of the amount of its capable PCU TX FIFO
    size, 2 KB and this requires a change in software.

    Technically a change would not have been required (except for frame
    burst considerations of 128 bytes) if these devices would have been
    able to use the full 4 KB of the PCU TX FIFO size but our systems
    engineers recommend 2 KB to be used only. We enforce this through
    software by reducing the max frame triggger level to 2 KB.

    Fixing the max frame trigger level should then have a few benefits:

      * The PER will now be adjusted as designed for underruns when the
        max trigger level is reached. This should help alleviate the
        bus as the rate control algorithm chooses a slower rate which
        should ensure frames are transmitted properly under high system
        bus load.

      * The poll we use on our TX queues should now trigger and work
        as designed for single stream devices. The hardware passes
        data from each TX queue on the PCU TX FIFO queue respecting each
        queue's priority. The new trigger level ensures this seeding of
        the PCU TX FIFO queue occurs as designed which could mean avoiding
        false resets and actually reseting hw correctly when a TX queue
        is indeed stuck.

      * Some undocumented / unsupported behaviour could have been triggered
        when the max trigger level level was being set to 4 KB on single
        stream devices. Its not clear what this issue was to me yet.

    Cc: Kyungwan Nam <kyungwan.nam@atheros.com>
    Cc: Bennyam Malavazi <bennyam.malavazi@atheros.com>
    Cc: Stephen Chen <stephen.chen@atheros.com>
    Cc: Shan Palanisamy <shan.palanisamy@atheros.com>
    Cc: Paul Shaw <paul.shaw@atheros.com>
    Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
    Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit 022fe0eb677b07aa37a3f88eab5ed47be2faffb5
Author: Luis R. Rodriguez <lrodriguez@atheros.com>
Date:   Wed Dec 16 11:51:42 2009 -0500

    ath9k: fix processing of TX PS null data frames

    This is a backport of upstream commit: e7824a50662f7f79b1a739f705b4d906c31cf221

    When mac80211 was telling us to go into Powersave we listened
    and immediately turned RX off. This meant hardware would not
    see the ACKs from the AP we're associated with and hardware
    we'd end up retransmiting the null data frame in a loop
    helplessly.

    Fix this by keeping track of the transmitted nullfunc frames
    and only when we are sure the AP has sent back an ACK do we
    go ahead and shut RX off.

    Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
    Signed-off-by: Vivek Natarajan <Vivek.Natarajan@atheros.com>
    Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit 978962f22c95c70f9018a9181c73525d26f0f3bd
Author: Sujith <Sujith.Manoharan@atheros.com>
Date:   Wed Dec 16 11:51:41 2009 -0500

    ath9k: Fix TX hang poll routine

    This is a backport of upstream commit: 332c556633b8c5fb4e890b1783122f2315526590

    When TX is hung, the chip is reset. Ensure that
    the chip is awake by using the PS wrappers.

    Signed-off-by: Sujith <Sujith.Manoharan@atheros.com>
    Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit 7fd943643cc5aef6546ca3d8914e01d4298ee769
Author: Larry Finger <Larry.Finger@lwfinger.net>
Date:   Sat Dec 5 19:25:22 2009 -0600

    rtl8187: Fix wrong rfkill switch mask for some models

    commit 70d57139f932b9ca21026253d02af71cf53d764a upstream.

    There are different bits used to convey the setting of the rfkill
    switch to the driver. The current driver only supports one of these
    possibilities. These changes were derived from the latest version
    of the vendor driver.

    This patch fixes the regression noted in kernel Bugzilla #14743.

    Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
    Reported-and-tested-by: Antti Kaijanmäki <antti@kaijanmaki.net>
    Tested-by: Hin-Tak Leung <hintak.leung@gmail.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit 22731c4b03371594e3bfdf032a64ee3cc6e61365
Author: Johannes Berg <johannes@sipsolutions.net>
Date:   Sat Oct 31 07:44:08 2009 +0100

    mac80211: fix scan abort sanity checks

    commit 6d3560d4fc9c5b9fe1a07a63926ea70512c69c32 upstream.

    Since sometimes mac80211 queues up a scan request
    to only act on it later, it must be allowed to
    (internally) cancel a not-yet-running scan, e.g.
    when the interface is taken down. This condition
    was missing since we always checked only the
    local->scanning variable which isn't yet set in
    that situation.

    Reported-by: Luis R. Rodriguez <mcgrof@gmail.com>
    Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit c04c99abc6754fa92d428915ac4215994fe95609
Author: Javier Cardona <javier@cozybit.com>
Date:   Wed Dec 9 18:43:01 2009 -0800

    mac80211: Revert 'Use correct sign for mesh active path refresh'

    commit 7b324d28a94dac5a451e8cba66e8d324601e5b9a upstream.

    The patch ("mac80211: Use correct sign for mesh active path
    refresh.") was actually a bug.  Reverted it and improved the
    explanation of how mesh path refresh works.

    Signed-off-by: Javier Cardona <javier@cozybit.com>
    Signed-off-by: Andrey Yurovsky <andrey@cozybit.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit dde24b6c58b10e27c296f2d343e9512ec0dce874
Author: Javier Cardona <javier@cozybit.com>
Date:   Wed Dec 9 18:43:00 2009 -0800

    mac80211: Fixed bug in mesh portal paths

    commit 5d618cb81aeea19879975cd1f9a1e707694dfd7c upstream.

    Paths to mesh portals were being timed out immediately after each use in
    intermediate forwarding nodes.  mppath->exp_time is set to the expiration time
    so assigning it to jiffies was marking the path as expired.

    Signed-off-by: Javier Cardona <javier@cozybit.com>
    Signed-off-by: Andrey Yurovsky <andrey@cozybit.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit fb0f7048cfa072125c98dc11884a96d1e03c0f9a
Author: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Date:   Fri Dec 4 17:41:34 2009 +0530

    mac80211: Fix bug in computing crc over dynamic IEs in beacon

    commit 1814077fd12a9cdf478c10076e9c42094e9d9250 upstream.

    On a 32-bit machine, BIT() macro does not give the required
    bit value if the bit is mroe than 31. In ieee802_11_parse_elems_crc(),
    BIT() is suppossed to get the bit value more than 31 (42 (id of ERP_INFO_IE),
    37 (CHANNEL_SWITCH_IE), (42), 32 (POWER_CONSTRAINT_IE), 45 (HT_CAP_IE),
    61 (HT_INFO_IE)). As we do not get the required bit value for the above
    IEs, crc over these IEs are never calculated, so any dynamic change in these
    IEs after the association is not really handled on 32-bit platforms.
    This patch fixes this issue.

    Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit 8bf3d79bc401ca417ccf9fc076d3295d1a71dbf5
Author: Luis R. Rodriguez <lrodriguez@atheros.com>
Date:   Wed Nov 25 17:23:26 2009 -0500

    ath5k: enable EEPROM checksum check

    commit 512414b0bed0d376ac4d5ec1dd6f0b1a3551febc upstream.

    Without this we have no gaurantee of the integrity of the
    EEPROM and are likely to encounter a lot of bogus bug reports
    due to actual issues on the EEPROM. With the EEPROM checksum
    check in place we can easily rule those issues out.

    If you run patch during a revert *you* have a card with a busted
    EEPROM and only older kernel will support that concoction. This
    patch is a trade off between not accepitng bogus EEPROMs and
    avoiding bogus bug reports allowing developers to focus instead
    on real concrete issues.

    If stable keeps bogus bug reports because of a possibly busted EEPROM
    feel free to apply this there too.

    Tested on an AR5414

    Cc: jirislaby@gmail.com
    Cc: akpm@linux-foundation.org
    Cc: rjw@sisk.pl
    Cc: me@bobcopeland.com
    Cc: david.quan@atheros.com
    Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

commit f0da7d9ab2ab222669e51f0404dc9b4e2fef9944
Author: Bob Copeland <me@bobcopeland.com>
Date:   Mon Nov 16 08:30:29 2009 -0500

    ath5k: allow setting txpower to 0

    commit 2eb2fa67e5462a36e98172fb92c78bc405b3035f upstream.

    As a holdover from earlier code when we used to set
    the power limit to '0' after a reset to configure the
    default transmit power, ath5k interprets txpower=0 as
    12.5 dBm.  Fix that by just passing 0 through.

    This fixes http://bugzilla.kernel.org/show_bug.cgi?id=14567

    Reported-by: Daniel Folkers <daniel.folkers@task24.nl>
    Tested-by: Daniel Folkers <daniel.folkers@task24.nl>
    Signed-off-by: Bob Copeland <me@bobcopeland.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Signed-off-by: Jarod Wilson <jarod@redhat.com>

diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
index c205df4..755e4c8 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -99,6 +99,7 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
 	struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
 	int ret;
 	u16 val;
+	u32 cksum, offset;
 
 	/*
 	 * Read values from EEPROM and store them in the capability structure
@@ -113,7 +114,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
 	if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0)
 		return 0;
 
-#ifdef notyet
 	/*
 	 * Validate the checksum of the EEPROM date. There are some
 	 * devices with invalid EEPROMs.
@@ -126,7 +126,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
 		ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum);
 		return -EIO;
 	}
-#endif
 
 	AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version),
 	    ee_ant_gain);
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index c6f3884..bd6574e 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -2958,8 +2958,6 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
 		ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
 		return -EINVAL;
 	}
-	if (txpower == 0)
-		txpower = AR5K_TUNE_DEFAULT_TXPOWER;
 
 	/* Reset TX power values */
 	memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index f4ade3d..5b6e40b 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -139,6 +139,7 @@ struct ath_buf {
 	dma_addr_t bf_daddr;		/* physical addr of desc */
 	dma_addr_t bf_buf_addr;		/* physical addr of data buffer */
 	bool bf_stale;
+	bool bf_isnullfunc;
 	u16 bf_flags;
 	struct ath_buf_state bf_state;
 	dma_addr_t bf_dmacontext;
@@ -528,6 +529,8 @@ struct ath_led {
 #define SC_OP_BEACON_SYNC       BIT(19)
 #define SC_OP_BTCOEX_ENABLED    BIT(20)
 #define SC_OP_BT_PRIORITY_DETECTED BIT(21)
+#define SC_OP_NULLFUNC_COMPLETED   BIT(22)
+#define SC_OP_PS_ENABLED	BIT(23)
 
 struct ath_bus_ops {
 	void		(*read_cachesize)(struct ath_softc *sc, int *csz);
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index ca7694c..c7aa05a 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -937,6 +937,11 @@ int ath9k_hw_init(struct ath_hw *ah)
 	DPRINTF(ah->ah_sc, ATH_DBG_RESET, "serialize_regmode is %d\n",
 		ah->config.serialize_regmode);
 
+	if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+		ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1;
+	else
+		ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD;
+
 	if (!ath9k_hw_macversion_supported(ah->hw_version.macVersion)) {
 		DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
 			"Mac Chip Rev 0x%02x.%x is not supported by "
@@ -3670,7 +3675,11 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah)
 		pCap->keycache_size = AR_KEYTABLE_SIZE;
 
 	pCap->hw_caps |= ATH9K_HW_CAP_FASTCC;
-	pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
+
+	if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+		pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD >> 1;
+	else
+		pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
 
 	if (AR_SREV_9285_10_OR_LATER(ah))
 		pCap->num_gpio_pins = AR9285_NUM_GPIO;
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index b892345..57f1463 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -218,6 +218,7 @@ struct ath9k_ops_config {
 #define AR_SPUR_FEEQ_BOUND_HT20 10
 	int spurmode;
 	u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
+	u8 max_txtrig_level;
 };
 
 enum ath9k_int {
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 800bfab..d4d9d82 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -70,7 +70,7 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
 	u32 txcfg, curLevel, newLevel;
 	enum ath9k_int omask;
 
-	if (ah->tx_trig_level >= MAX_TX_FIFO_THRESHOLD)
+	if (ah->tx_trig_level >= ah->config.max_txtrig_level)
 		return false;
 
 	omask = ath9k_hw_set_interrupts(ah, ah->mask_reg & ~ATH9K_INT_GLOBAL);
@@ -79,7 +79,7 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
 	curLevel = MS(txcfg, AR_FTRIG);
 	newLevel = curLevel;
 	if (bIncTrigLevel) {
-		if (curLevel < MAX_TX_FIFO_THRESHOLD)
+		if (curLevel < ah->config.max_txtrig_level)
 			newLevel++;
 	} else if (curLevel > MIN_TX_FIFO_THRESHOLD)
 		newLevel--;
@@ -222,6 +222,8 @@ int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds)
 	ds->ds_txstat.ts_status = 0;
 	ds->ds_txstat.ts_flags = 0;
 
+	if (ads->ds_txstatus1 & AR_FrmXmitOK)
+		ds->ds_txstat.ts_status |= ATH9K_TX_ACKED;
 	if (ads->ds_txstatus1 & AR_ExcessiveRetries)
 		ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY;
 	if (ads->ds_txstatus1 & AR_Filtered)
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index f56e77d..ff65f85 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -76,6 +76,7 @@
 #define ATH9K_TXERR_FIFO           0x04
 #define ATH9K_TXERR_XTXOP          0x08
 #define ATH9K_TXERR_TIMER_EXPIRED  0x10
+#define ATH9K_TX_ACKED		   0x20
 
 #define ATH9K_TX_BA                0x01
 #define ATH9K_TX_PWRMGMT           0x02
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 823f186..481b80b 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2344,6 +2344,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 
 	if (changed & IEEE80211_CONF_CHANGE_PS) {
 		if (conf->flags & IEEE80211_CONF_PS) {
+			sc->sc_flags |= SC_OP_PS_ENABLED;
 			if (!(ah->caps.hw_caps &
 			      ATH9K_HW_CAP_AUTOSLEEP)) {
 				if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
@@ -2351,11 +2352,17 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 					ath9k_hw_set_interrupts(sc->sc_ah,
 							sc->imask);
 				}
-				ath9k_hw_setrxabort(sc->sc_ah, 1);
 			}
 			sc->ps_enabled = true;
+			if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) {
+				sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
+				sc->ps_enabled = true;
+				ath9k_hw_setrxabort(sc->sc_ah, 1);
+			}
 		} else {
 			sc->ps_enabled = false;
+			sc->sc_flags &= ~(SC_OP_PS_ENABLED |
+					  SC_OP_NULLFUNC_COMPLETED);
 			ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
 			if (!(ah->caps.hw_caps &
 			      ATH9K_HW_CAP_AUTOSLEEP)) {
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 0d1452a..19d3075 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1608,6 +1608,13 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
 	}
 
 	bf->bf_buf_addr = bf->bf_dmacontext;
+
+	if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc)) {
+		bf->bf_isnullfunc = true;
+		sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
+	} else
+		bf->bf_isnullfunc = false;
+
 	return 0;
 }
 
@@ -2009,6 +2016,15 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 		if (ds == txq->axq_gatingds)
 			txq->axq_gatingds = NULL;
 
+		if (bf->bf_isnullfunc &&
+		    (ds->ds_txstat.ts_status & ATH9K_TX_ACKED)) {
+			if ((sc->sc_flags & SC_OP_PS_ENABLED)) {
+				sc->ps_enabled = true;
+				ath9k_hw_setrxabort(sc->sc_ah, 1);
+			} else
+				sc->sc_flags |= SC_OP_NULLFUNC_COMPLETED;
+		}
+
 		/*
 		 * Remove ath_buf's of the same transmit unit from txq,
 		 * however leave the last descriptor back as the holding
@@ -2024,7 +2040,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 		if (bf_isaggr(bf))
 			txq->axq_aggr_depth--;
 
-		txok = (ds->ds_txstat.ts_status == 0);
+		txok = !(ds->ds_txstat.ts_status & ATH9K_TXERR_FILT);
 		txq->axq_tx_inprogress = false;
 		spin_unlock_bh(&txq->axq_lock);
 
@@ -2084,7 +2100,9 @@ static void ath_tx_complete_poll_work(void *softc)
 
 	if (needreset) {
 		DPRINTF(sc, ATH_DBG_RESET, "tx hung, resetting the chip\n");
+		ath9k_ps_wakeup(sc);
 		ath_reset(sc, false);
+		ath9k_ps_restore(sc);
 	}
 
 	ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h
index 1fb5ec5..aa4d9a3 100644
--- a/drivers/net/wireless/rtl818x/rtl8187.h
+++ b/drivers/net/wireless/rtl818x/rtl8187.h
@@ -23,6 +23,7 @@
 #define RTL8187_EEPROM_TXPWR_CHAN_1	0x16	/* 3 channels */
 #define RTL8187_EEPROM_TXPWR_CHAN_6	0x1B	/* 2 channels */
 #define RTL8187_EEPROM_TXPWR_CHAN_4	0x3D	/* 2 channels */
+#define RTL8187_EEPROM_SELECT_GPIO	0x3B
 
 #define RTL8187_REQT_READ	0xC0
 #define RTL8187_REQT_WRITE	0x40
@@ -31,6 +32,9 @@
 
 #define RTL8187_MAX_RX		0x9C4
 
+#define RFKILL_MASK_8187_89_97	0x2
+#define RFKILL_MASK_8198	0x4
+
 struct rtl8187_rx_info {
 	struct urb *urb;
 	struct ieee80211_hw *dev;
@@ -125,6 +129,7 @@ struct rtl8187_priv {
 	u8 noise;
 	u8 slot_time;
 	u8 aifsn[4];
+	u8 rfkill_mask;
 	struct {
 		__le64 buf;
 		struct sk_buff_head queue;
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index c5ff248..2e98921 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -1372,6 +1372,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
 	struct ieee80211_channel *channel;
 	const char *chip_name;
 	u16 txpwr, reg;
+	u16 product_id = le16_to_cpu(udev->descriptor.idProduct);
 	int err, i;
 
 	dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops);
@@ -1531,6 +1532,13 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
 		(*channel++).hw_value = txpwr & 0xFF;
 		(*channel++).hw_value = txpwr >> 8;
 	}
+	/* Handle the differing rfkill GPIO bit in different models */
+	priv->rfkill_mask = RFKILL_MASK_8187_89_97;
+	if (product_id == 0x8197 || product_id == 0x8198) {
+		eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, &reg);
+		if (reg & 0xFF00)
+			priv->rfkill_mask = RFKILL_MASK_8198;
+	}
 
 	/*
 	 * XXX: Once this driver supports anything that requires
@@ -1559,9 +1567,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
 	mutex_init(&priv->conf_mutex);
 	skb_queue_head_init(&priv->b_tx_status.queue);
 
-	printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s\n",
+	printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s, rfkill mask %d\n",
 	       wiphy_name(dev->wiphy), dev->wiphy->perm_addr,
-	       chip_name, priv->asic_rev, priv->rf->name);
+	       chip_name, priv->asic_rev, priv->rf->name, priv->rfkill_mask);
 
 #ifdef CONFIG_RTL8187_LEDS
 	eeprom_93cx6_read(&eeprom, 0x3F, &reg);
diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c
index cad8037..03555e1 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c
@@ -25,10 +25,10 @@ static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv)
 	u8 gpio;
 
 	gpio = rtl818x_ioread8(priv, &priv->map->GPIO0);
-	rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~0x02);
+	rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~priv->rfkill_mask);
 	gpio = rtl818x_ioread8(priv, &priv->map->GPIO1);
 
-	return gpio & 0x02;
+	return gpio & priv->rfkill_mask;
 }
 
 void rtl8187_rfkill_init(struct ieee80211_hw *hw)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0e3a54c..b85f531 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -815,6 +815,7 @@ struct ieee80211_local {
 	unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
 
 	bool pspolling;
+	bool scan_ps_enabled;
 	/*
 	 * PS can only be enabled when we have exactly one managed
 	 * interface (and monitors) in PS, this then points there.
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index dd1c193..010ff2f 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -186,8 +186,9 @@ struct mesh_rmc {
  */
 #define MESH_PREQ_MIN_INT	10
 #define MESH_DIAM_TRAVERSAL_TIME 50
-/* Paths will be refreshed if they are closer than PATH_REFRESH_TIME to their
- * expiration
+/* A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds before
+ * timing out.  This way it will remain ACTIVE and no data frames will be
+ * unnecesarily held in the pending queue.
  */
 #define MESH_PATH_REFRESH_TIME			1000
 #define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 29b82e9..93c49fc 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -813,7 +813,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
 	}
 
 	if (mpath->flags & MESH_PATH_ACTIVE) {
-		if (time_after(jiffies, mpath->exp_time +
+		if (time_after(jiffies, mpath->exp_time -
 			msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time))
 				&& !memcmp(sdata->dev->dev_addr, hdr->addr4,
 					   ETH_ALEN)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 470eca1..3293d8d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1531,7 +1531,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 			mpp_path_add(mesh_hdr->eaddr2, hdr->addr4, sdata);
 		} else {
 			spin_lock_bh(&mppath->state_lock);
-			mppath->exp_time = jiffies;
 			if (compare_ether_addr(mppath->mpp, hdr->addr4) != 0)
 				memcpy(mppath->mpp, hdr->addr4, ETH_ALEN);
 			spin_unlock_bh(&mppath->state_lock);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 47bb40f..1ca2f98 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -196,7 +196,8 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
 static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_local *local = sdata->local;
-	bool ps = false;
+
+	local->scan_ps_enabled = false;
 
 	/* FIXME: what to do when local->pspolling is true? */
 
@@ -208,12 +209,13 @@ static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
 #endif
 
 	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-		ps = true;
+		local->scan_ps_enabled = true;
 		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
 	}
 
-	if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
+	if (!(local->scan_ps_enabled) ||
+	    !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
 		/*
 		 * If power save was enabled, no need to send a nullfunc
 		 * frame because AP knows that we are sleeping. But if the
@@ -234,7 +236,7 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
 
 	if (!local->ps_sdata)
 		ieee80211_send_nullfunc(local, sdata, 0);
-	else {
+	else if (local->scan_ps_enabled) {
 		/*
 		 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
 		 * will send a nullfunc frame with the powersave bit set
@@ -250,6 +252,16 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
 		 */
 		local->hw.conf.flags |= IEEE80211_CONF_PS;
 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+	} else if (local->hw.conf.dynamic_ps_timeout > 0) {
+		/*
+		 * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
+		 * had been running before leaving the operating channel,
+		 * restart the timer now and send a nullfunc frame to inform
+		 * the AP that we are awake.
+		 */
+		ieee80211_send_nullfunc(local, sdata, 0);
+		mod_timer(&local->dynamic_ps_timer, jiffies +
+			  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
 	}
 }
 
@@ -268,10 +280,14 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
 
 	mutex_lock(&local->scan_mtx);
 
-	if (WARN_ON(!local->scanning)) {
-		mutex_unlock(&local->scan_mtx);
-		return;
-	}
+	/*
+	 * It's ok to abort a not-yet-running scan (that
+	 * we have one at all will be verified by checking
+	 * local->scan_req next), but not to complete it
+	 * successfully.
+	 */
+	if (WARN_ON(!local->scanning && !aborted))
+		aborted = true;
 
 	if (WARN_ON(!local->scan_req)) {
 		mutex_unlock(&local->scan_mtx);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 5aec6ea..efe0576 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -604,7 +604,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
 		if (elen > left)
 			break;
 
-		if (calc_crc && id < 64 && (filter & BIT(id)))
+		if (calc_crc && id < 64 && (filter & (1ULL << id)))
 			crc = crc32_be(crc, pos - 2, elen + 2);
 
 		switch (id) {