Based on patch by Bryan Forbes <bryan@reigndropsfall.net> Also update mt76 to update for API changes Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 44655
		
			
				
	
	
		
			138 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
 | 
						|
Date: Wed, 4 Mar 2015 05:12:10 +0300
 | 
						|
Subject: [PATCH] ath5k: channel change fix
 | 
						|
 | 
						|
ath5k updates the channel pointer and after that it stops the Rx logic
 | 
						|
and apply channel to HW. In case of channel switch, such sequence
 | 
						|
creates a small window when a frame, which is received on the old
 | 
						|
channel is considered as a frame received on the new one.
 | 
						|
 | 
						|
The most notable consequence of this situation occurs during the switch
 | 
						|
from 2 GHz band (CCK+OFDM) to the 5GHz band (OFDM-only). Frame received
 | 
						|
with CCK rate, e.g. beacon received at the 1mbps, causes the following
 | 
						|
warning:
 | 
						|
 | 
						|
  WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]()
 | 
						|
  invalid hw_rix: 1a
 | 
						|
  [..]
 | 
						|
  Call Trace:
 | 
						|
  [<802656a8>] show_stack+0x48/0x70
 | 
						|
  [<802dd92c>] warn_slowpath_common+0x88/0xbc
 | 
						|
  [<802dd98c>] warn_slowpath_fmt+0x2c/0x38
 | 
						|
  [<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k]
 | 
						|
  [<8028ac64>] tasklet_action+0x8c/0xf0
 | 
						|
  [<80075804>] __do_softirq+0x180/0x32c
 | 
						|
  [<80196ce8>] irq_exit+0x54/0x70
 | 
						|
  [<80041848>] ret_from_irq+0x0/0x4
 | 
						|
  [<80182fdc>] ioread32+0x4/0xc
 | 
						|
  [<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k]
 | 
						|
  [<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k]
 | 
						|
  [<81b50900>] ath5k_reset+0xd4/0x310 [ath5k]
 | 
						|
  [<81b557e8>] ath5k_config+0x4c/0x104 [ath5k]
 | 
						|
  [<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211]
 | 
						|
  [<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211]
 | 
						|
  [<8022c3f4>] process_one_work+0x28c/0x400
 | 
						|
  [<802df8f8>] worker_thread+0x258/0x3c0
 | 
						|
  [<801b5710>] kthread+0xe0/0xec
 | 
						|
  [<800418a8>] ret_from_kernel_thread+0x14/0x1c
 | 
						|
 | 
						|
The easiest way to reproduce this warning is to run scan with dualband
 | 
						|
NIC in noisy environments, when the channel 11 runs multiple APs. In my
 | 
						|
tests if the APs num >= 12, the warning appears in the first few
 | 
						|
seconds of scanning.
 | 
						|
 | 
						|
In order to fix this, the Rx disable code moved to a higher level and
 | 
						|
placed before the channel pointer update. This is also makes the code a
 | 
						|
bit more symmetrical, since we disable and enable the Rx in the same
 | 
						|
function.
 | 
						|
 | 
						|
In fact, at the pointer update time new frames should not appear,
 | 
						|
because interrupt generation at this point should already be disabled.
 | 
						|
The next patch should address this issue.
 | 
						|
 | 
						|
CC: Jiri Slaby <jirislaby@gmail.com>
 | 
						|
CC: Nick Kossifidis <mickflemm@gmail.com>
 | 
						|
CC: Luis R. Rodriguez <mcgrof@do-not-panic.com>
 | 
						|
Reported-by: Christophe Prevotaux <cprevotaux@nltinc.com>
 | 
						|
Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com>
 | 
						|
Tested-by: Eric Bree <ebree@nltinc.com>
 | 
						|
Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
 | 
						|
---
 | 
						|
 | 
						|
--- a/drivers/net/wireless/ath/ath5k/base.c
 | 
						|
+++ b/drivers/net/wireless/ath/ath5k/base.c
 | 
						|
@@ -2858,7 +2858,7 @@ ath5k_reset(struct ath5k_hw *ah, struct 
 | 
						|
 {
 | 
						|
 	struct ath_common *common = ath5k_hw_common(ah);
 | 
						|
 	int ret, ani_mode;
 | 
						|
-	bool fast;
 | 
						|
+	bool fast = chan && modparam_fastchanswitch ? 1 : 0;
 | 
						|
 
 | 
						|
 	ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
 | 
						|
 
 | 
						|
@@ -2876,11 +2876,29 @@ ath5k_reset(struct ath5k_hw *ah, struct 
 | 
						|
 	 * so we should also free any remaining
 | 
						|
 	 * tx buffers */
 | 
						|
 	ath5k_drain_tx_buffs(ah);
 | 
						|
+
 | 
						|
+	/* Stop PCU */
 | 
						|
+	ath5k_hw_stop_rx_pcu(ah);
 | 
						|
+
 | 
						|
+	/* Stop DMA
 | 
						|
+	 *
 | 
						|
+	 * Note: If DMA didn't stop continue
 | 
						|
+	 * since only a reset will fix it.
 | 
						|
+	 */
 | 
						|
+	ret = ath5k_hw_dma_stop(ah);
 | 
						|
+
 | 
						|
+	/* RF Bus grant won't work if we have pending
 | 
						|
+	 * frames
 | 
						|
+	 */
 | 
						|
+	if (ret && fast) {
 | 
						|
+		ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
 | 
						|
+			  "DMA didn't stop, falling back to normal reset\n");
 | 
						|
+		fast = false;
 | 
						|
+	}
 | 
						|
+
 | 
						|
 	if (chan)
 | 
						|
 		ah->curchan = chan;
 | 
						|
 
 | 
						|
-	fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
 | 
						|
-
 | 
						|
 	ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
 | 
						|
 	if (ret) {
 | 
						|
 		ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
 | 
						|
--- a/drivers/net/wireless/ath/ath5k/reset.c
 | 
						|
+++ b/drivers/net/wireless/ath/ath5k/reset.c
 | 
						|
@@ -1169,30 +1169,6 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum
 | 
						|
 	if (ah->ah_version == AR5K_AR5212)
 | 
						|
 		ath5k_hw_set_sleep_clock(ah, false);
 | 
						|
 
 | 
						|
-	/*
 | 
						|
-	 * Stop PCU
 | 
						|
-	 */
 | 
						|
-	ath5k_hw_stop_rx_pcu(ah);
 | 
						|
-
 | 
						|
-	/*
 | 
						|
-	 * Stop DMA
 | 
						|
-	 *
 | 
						|
-	 * Note: If DMA didn't stop continue
 | 
						|
-	 * since only a reset will fix it.
 | 
						|
-	 */
 | 
						|
-	ret = ath5k_hw_dma_stop(ah);
 | 
						|
-
 | 
						|
-	/* RF Bus grant won't work if we have pending
 | 
						|
-	 * frames */
 | 
						|
-	if (ret && fast) {
 | 
						|
-		ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
 | 
						|
-			"DMA didn't stop, falling back to normal reset\n");
 | 
						|
-		fast = false;
 | 
						|
-		/* Non fatal, just continue with
 | 
						|
-		 * normal reset */
 | 
						|
-		ret = 0;
 | 
						|
-	}
 | 
						|
-
 | 
						|
 	mode = channel->hw_value;
 | 
						|
 	switch (mode) {
 | 
						|
 	case AR5K_MODE_11A:
 |