ath5k: channel change fix
[openwrt.git] / package / kernel / mac80211 / patches / 331-ath5k-channel-change-fix.patch
1 From a5d37f41d298a2a202296909892dd01e2bf071c7 Mon Sep 17 00:00:00 2001
2 From: Sergey Ryazanov <ryazanov.s.a@gmail.com>
3 Date: Wed, 4 Mar 2015 00:44:37 +0300
4 Subject: [PATCH] ath5k: channel change fix
5
6 ath5k updates the channel pointer and after that it stops the Rx logic
7 and apply channel to HW. In case of channel switch, such sequence
8 creates a small window when a frame, which is received on the old
9 channel is considered as a frame received on the new one.
10
11 The most notable consequence of this situation occurs during the switch
12 from 2 GHz band (CCK+OFDM) to the 5GHz band (OFDM-only). Frame received
13 with CCK rate, e.g. beacon received at the 1mbps, causes the following
14 warning:
15
16   WARNING: at ath5k/base.c:589 ath5k_tasklet_rx+0x318/0x6ec [ath5k]()
17   invalid hw_rix: 1a
18   [..]
19   Call Trace:
20   [<802656a8>] show_stack+0x48/0x70
21   [<802dd92c>] warn_slowpath_common+0x88/0xbc
22   [<802dd98c>] warn_slowpath_fmt+0x2c/0x38
23   [<81b51be8>] ath5k_tasklet_rx+0x318/0x6ec [ath5k]
24   [<8028ac64>] tasklet_action+0x8c/0xf0
25   [<80075804>] __do_softirq+0x180/0x32c
26   [<80196ce8>] irq_exit+0x54/0x70
27   [<80041848>] ret_from_irq+0x0/0x4
28   [<80182fdc>] ioread32+0x4/0xc
29   [<81b4c42c>] ath5k_hw_set_sleep_clock+0x2ec/0x474 [ath5k]
30   [<81b4cf28>] ath5k_hw_reset+0x50/0xeb8 [ath5k]
31   [<81b50900>] ath5k_reset+0xd4/0x310 [ath5k]
32   [<81b557e8>] ath5k_config+0x4c/0x104 [ath5k]
33   [<80d01770>] ieee80211_hw_config+0x2f4/0x35c [mac80211]
34   [<80d09aa8>] ieee80211_scan_work+0x2e4/0x414 [mac80211]
35   [<8022c3f4>] process_one_work+0x28c/0x400
36   [<802df8f8>] worker_thread+0x258/0x3c0
37   [<801b5710>] kthread+0xe0/0xec
38   [<800418a8>] ret_from_kernel_thread+0x14/0x1c
39
40 The easiest way to reproduce this warning is to run scan with dualband
41 NIC in noisy environments, when the channel 11 runs multiple APs. In my
42 tests if the APs num >= 12, the warning appears in the first few
43 seconds of scanning.
44
45 In order to fix this, the Rx disable code moved to a higher level and
46 placed before the channel pointer update. This is also makes the code a
47 bit more symmetrical, since we disable and enable the Rx in the same
48 function.
49
50 In fact, at the pointer update time new frames should not appear,
51 because interrupt generation at this point should already be disabled.
52 The next patch should address this issue.
53
54 CC: Jiri Slaby <jirislaby@gmail.com>
55 CC: Nick Kossifidis <mickflemm@gmail.com>
56 CC: Luis R. Rodriguez <mcgrof@do-not-panic.com>
57 Reported-by: Christophe Prevotaux <cprevotaux@nltinc.com>
58 Tested-by: Christophe Prevotaux <cprevotaux@nltinc.com>
59 Tested-by: Eric Bree <ebree@nltinc.com>
60 Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
61 ---
62  drivers/net/wireless/ath/ath5k/base.c  | 24 +++++++++++++++++++++---
63  drivers/net/wireless/ath/ath5k/reset.c | 24 ------------------------
64  2 files changed, 21 insertions(+), 27 deletions(-)
65
66 diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
67 index bc9cb35..34b2f15 100644
68 --- a/drivers/net/wireless/ath/ath5k/base.c
69 +++ b/drivers/net/wireless/ath/ath5k/base.c
70 @@ -2858,7 +2858,7 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
71  {
72         struct ath_common *common = ath5k_hw_common(ah);
73         int ret, ani_mode;
74 -       bool fast;
75 +       bool fast = chan && modparam_fastchanswitch ? 1 : 0;
76  
77         ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
78  
79 @@ -2876,11 +2876,29 @@ ath5k_reset(struct ath5k_hw *ah, struct ieee80211_channel *chan,
80          * so we should also free any remaining
81          * tx buffers */
82         ath5k_drain_tx_buffs(ah);
83 +
84 +       /* Stop PCU */
85 +       ath5k_hw_stop_rx_pcu(ah);
86 +
87 +       /* Stop DMA
88 +        *
89 +        * Note: If DMA didn't stop continue
90 +        * since only a reset will fix it.
91 +        */
92 +       ret = ath5k_hw_dma_stop(ah);
93 +
94 +       /* RF Bus grant won't work if we have pending
95 +        * frames
96 +        */
97 +       if (ret && fast) {
98 +               ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
99 +                         "DMA didn't stop, falling back to normal reset\n");
100 +               fast = false;
101 +       }
102 +
103         if (chan)
104                 ah->curchan = chan;
105  
106 -       fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
107 -
108         ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
109         if (ret) {
110                 ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
111 diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c
112 index b9b651e..99e62f9 100644
113 --- a/drivers/net/wireless/ath/ath5k/reset.c
114 +++ b/drivers/net/wireless/ath/ath5k/reset.c
115 @@ -1169,30 +1169,6 @@ ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
116         if (ah->ah_version == AR5K_AR5212)
117                 ath5k_hw_set_sleep_clock(ah, false);
118  
119 -       /*
120 -        * Stop PCU
121 -        */
122 -       ath5k_hw_stop_rx_pcu(ah);
123 -
124 -       /*
125 -        * Stop DMA
126 -        *
127 -        * Note: If DMA didn't stop continue
128 -        * since only a reset will fix it.
129 -        */
130 -       ret = ath5k_hw_dma_stop(ah);
131 -
132 -       /* RF Bus grant won't work if we have pending
133 -        * frames */
134 -       if (ret && fast) {
135 -               ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
136 -                       "DMA didn't stop, falling back to normal reset\n");
137 -               fast = false;
138 -               /* Non fatal, just continue with
139 -                * normal reset */
140 -               ret = 0;
141 -       }
142 -
143         mode = channel->hw_value;
144         switch (mode) {
145         case AR5K_MODE_11A: