aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/wow.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/wow.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/wow.c228
1 files changed, 107 insertions, 121 deletions
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
index 5f30e580d942..8d0b1730a9d5 100644
--- a/drivers/net/wireless/ath/ath9k/wow.c
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -16,36 +16,43 @@
16 16
17#include "ath9k.h" 17#include "ath9k.h"
18 18
19static const struct wiphy_wowlan_support ath9k_wowlan_support = { 19static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
20 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, 20 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
21 .n_patterns = MAX_NUM_USER_PATTERN, 21 .n_patterns = MAX_NUM_USER_PATTERN,
22 .pattern_min_len = 1, 22 .pattern_min_len = 1,
23 .pattern_max_len = MAX_PATTERN_SIZE, 23 .pattern_max_len = MAX_PATTERN_SIZE,
24}; 24};
25 25
26static void ath9k_wow_map_triggers(struct ath_softc *sc, 26static const struct wiphy_wowlan_support ath9k_wowlan_support = {
27 struct cfg80211_wowlan *wowlan, 27 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
28 u32 *wow_triggers) 28 .n_patterns = MAX_NUM_PATTERN - 2,
29 .pattern_min_len = 1,
30 .pattern_max_len = MAX_PATTERN_SIZE,
31};
32
33static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
34 struct cfg80211_wowlan *wowlan)
29{ 35{
36 u8 wow_triggers = 0;
37
30 if (wowlan->disconnect) 38 if (wowlan->disconnect)
31 *wow_triggers |= AH_WOW_LINK_CHANGE | 39 wow_triggers |= AH_WOW_LINK_CHANGE |
32 AH_WOW_BEACON_MISS; 40 AH_WOW_BEACON_MISS;
33 if (wowlan->magic_pkt) 41 if (wowlan->magic_pkt)
34 *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; 42 wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
35 43
36 if (wowlan->n_patterns) 44 if (wowlan->n_patterns)
37 *wow_triggers |= AH_WOW_USER_PATTERN_EN; 45 wow_triggers |= AH_WOW_USER_PATTERN_EN;
38
39 sc->wow_enabled = *wow_triggers;
40 46
47 return wow_triggers;
41} 48}
42 49
43static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) 50static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
44{ 51{
45 struct ath_hw *ah = sc->sc_ah; 52 struct ath_hw *ah = sc->sc_ah;
46 struct ath_common *common = ath9k_hw_common(ah); 53 struct ath_common *common = ath9k_hw_common(ah);
47 int pattern_count = 0; 54 int pattern_count = 0;
48 int i, byte_cnt; 55 int ret, i, byte_cnt = 0;
49 u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; 56 u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
50 u8 dis_deauth_mask[MAX_PATTERN_SIZE]; 57 u8 dis_deauth_mask[MAX_PATTERN_SIZE];
51 58
@@ -80,12 +87,7 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
80 * | x:x:x:x:x:x -- 22 bytes 87 * | x:x:x:x:x:x -- 22 bytes
81 */ 88 */
82 89
83 /* Create Disassociate Pattern first */
84
85 byte_cnt = 0;
86
87 /* Fill out the mask with all FF's */ 90 /* Fill out the mask with all FF's */
88
89 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++) 91 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
90 dis_deauth_mask[i] = 0xff; 92 dis_deauth_mask[i] = 0xff;
91 93
@@ -108,19 +110,17 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
108 byte_cnt += 6; 110 byte_cnt += 6;
109 111
110 /* copy the bssid, its same as the source mac address */ 112 /* copy the bssid, its same as the source mac address */
111
112 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); 113 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
113 114
114 /* Create Disassociate pattern mask */ 115 /* Create Disassociate pattern mask */
115
116 dis_deauth_mask[0] = 0xfe; 116 dis_deauth_mask[0] = 0xfe;
117 dis_deauth_mask[1] = 0x03; 117 dis_deauth_mask[1] = 0x03;
118 dis_deauth_mask[2] = 0xc0; 118 dis_deauth_mask[2] = 0xc0;
119 119
120 ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n"); 120 ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
121 121 pattern_count, byte_cnt);
122 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, 122 if (ret)
123 pattern_count, byte_cnt); 123 goto exit;
124 124
125 pattern_count++; 125 pattern_count++;
126 /* 126 /*
@@ -129,59 +129,39 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
129 */ 129 */
130 dis_deauth_pattern[0] = 0xC0; 130 dis_deauth_pattern[0] = 0xC0;
131 131
132 ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, 132 ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
133 pattern_count, byte_cnt); 133 pattern_count, byte_cnt);
134 134exit:
135 return ret;
135} 136}
136 137
137static void ath9k_wow_add_pattern(struct ath_softc *sc, 138static int ath9k_wow_add_pattern(struct ath_softc *sc,
138 struct cfg80211_wowlan *wowlan) 139 struct cfg80211_wowlan *wowlan)
139{ 140{
140 struct ath_hw *ah = sc->sc_ah; 141 struct ath_hw *ah = sc->sc_ah;
141 struct ath9k_wow_pattern *wow_pattern = NULL;
142 struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 142 struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
143 int mask_len; 143 u8 wow_pattern[MAX_PATTERN_SIZE];
144 u8 wow_mask[MAX_PATTERN_SIZE];
145 int mask_len, ret = 0;
144 s8 i = 0; 146 s8 i = 0;
145 147
146 if (!wowlan->n_patterns)
147 return;
148
149 /*
150 * Add the new user configured patterns
151 */
152 for (i = 0; i < wowlan->n_patterns; i++) { 148 for (i = 0; i < wowlan->n_patterns; i++) {
153 149 mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
154 wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL); 150 memset(wow_pattern, 0, MAX_PATTERN_SIZE);
155 151 memset(wow_mask, 0, MAX_PATTERN_SIZE);
156 if (!wow_pattern) 152 memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
157 return; 153 memcpy(wow_mask, patterns[i].mask, mask_len);
158 154
159 /* 155 ret = ath9k_hw_wow_apply_pattern(ah,
160 * TODO: convert the generic user space pattern to 156 wow_pattern,
161 * appropriate chip specific/802.11 pattern. 157 wow_mask,
162 */ 158 i + 2,
163 159 patterns[i].pattern_len);
164 mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); 160 if (ret)
165 memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE); 161 break;
166 memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE);
167 memcpy(wow_pattern->pattern_bytes, patterns[i].pattern,
168 patterns[i].pattern_len);
169 memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len);
170 wow_pattern->pattern_len = patterns[i].pattern_len;
171
172 /*
173 * just need to take care of deauth and disssoc pattern,
174 * make sure we don't overwrite them.
175 */
176
177 ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes,
178 wow_pattern->mask_bytes,
179 i + 2,
180 wow_pattern->pattern_len);
181 kfree(wow_pattern);
182
183 } 162 }
184 163
164 return ret;
185} 165}
186 166
187int ath9k_suspend(struct ieee80211_hw *hw, 167int ath9k_suspend(struct ieee80211_hw *hw,
@@ -190,41 +170,39 @@ int ath9k_suspend(struct ieee80211_hw *hw,
190 struct ath_softc *sc = hw->priv; 170 struct ath_softc *sc = hw->priv;
191 struct ath_hw *ah = sc->sc_ah; 171 struct ath_hw *ah = sc->sc_ah;
192 struct ath_common *common = ath9k_hw_common(ah); 172 struct ath_common *common = ath9k_hw_common(ah);
193 u32 wow_triggers_enabled = 0; 173 u8 triggers;
194 int ret = 0; 174 int ret = 0;
195 175
196 ath9k_deinit_channel_context(sc); 176 ath9k_deinit_channel_context(sc);
197 177
198 mutex_lock(&sc->mutex); 178 mutex_lock(&sc->mutex);
199 179
200 ath_cancel_work(sc);
201 ath_stop_ani(sc);
202
203 if (test_bit(ATH_OP_INVALID, &common->op_flags)) { 180 if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
204 ath_dbg(common, ANY, "Device not present\n"); 181 ath_err(common, "Device not present\n");
205 ret = -EINVAL; 182 ret = -ENODEV;
206 goto fail_wow; 183 goto fail_wow;
207 } 184 }
208 185
209 if (WARN_ON(!wowlan)) { 186 if (WARN_ON(!wowlan)) {
210 ath_dbg(common, WOW, "None of the WoW triggers enabled\n"); 187 ath_err(common, "None of the WoW triggers enabled\n");
211 ret = -EINVAL; 188 ret = -EINVAL;
212 goto fail_wow; 189 goto fail_wow;
213 } 190 }
214 191
215 if (!device_can_wakeup(sc->dev)) { 192 if (sc->cur_chan->nvifs > 1) {
216 ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n"); 193 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
217 ret = 1; 194 ret = 1;
218 goto fail_wow; 195 goto fail_wow;
219 } 196 }
220 197
221 /* 198 if (ath9k_is_chanctx_enabled()) {
222 * none of the sta vifs are associated 199 if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
223 * and we are not currently handling multivif 200 ath_dbg(common, WOW,
224 * cases, for instance we have to seperately 201 "Multi-channel WOW is not supported\n");
225 * configure 'keep alive frame' for each 202 ret = 1;
226 * STA. 203 goto fail_wow;
227 */ 204 }
205 }
228 206
229 if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { 207 if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
230 ath_dbg(common, WOW, "None of the STA vifs are associated\n"); 208 ath_dbg(common, WOW, "None of the STA vifs are associated\n");
@@ -232,16 +210,15 @@ int ath9k_suspend(struct ieee80211_hw *hw,
232 goto fail_wow; 210 goto fail_wow;
233 } 211 }
234 212
235 if (sc->cur_chan->nvifs > 1) { 213 triggers = ath9k_wow_map_triggers(sc, wowlan);
236 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); 214 if (!triggers) {
215 ath_dbg(common, WOW, "No valid WoW triggers\n");
237 ret = 1; 216 ret = 1;
238 goto fail_wow; 217 goto fail_wow;
239 } 218 }
240 219
241 ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled); 220 ath_cancel_work(sc);
242 221 ath_stop_ani(sc);
243 ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n",
244 wow_triggers_enabled);
245 222
246 ath9k_ps_wakeup(sc); 223 ath9k_ps_wakeup(sc);
247 224
@@ -251,10 +228,21 @@ int ath9k_suspend(struct ieee80211_hw *hw,
251 * Enable wake up on recieving disassoc/deauth 228 * Enable wake up on recieving disassoc/deauth
252 * frame by default. 229 * frame by default.
253 */ 230 */
254 ath9k_wow_add_disassoc_deauth_pattern(sc); 231 ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
232 if (ret) {
233 ath_err(common,
234 "Unable to add disassoc/deauth pattern: %d\n", ret);
235 goto fail_wow;
236 }
255 237
256 if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN) 238 if (triggers & AH_WOW_USER_PATTERN_EN) {
257 ath9k_wow_add_pattern(sc, wowlan); 239 ret = ath9k_wow_add_pattern(sc, wowlan);
240 if (ret) {
241 ath_err(common,
242 "Unable to add user pattern: %d\n", ret);
243 goto fail_wow;
244 }
245 }
258 246
259 spin_lock_bh(&sc->sc_pcu_lock); 247 spin_lock_bh(&sc->sc_pcu_lock);
260 /* 248 /*
@@ -278,12 +266,12 @@ int ath9k_suspend(struct ieee80211_hw *hw,
278 synchronize_irq(sc->irq); 266 synchronize_irq(sc->irq);
279 tasklet_kill(&sc->intr_tq); 267 tasklet_kill(&sc->intr_tq);
280 268
281 ath9k_hw_wow_enable(ah, wow_triggers_enabled); 269 ath9k_hw_wow_enable(ah, triggers);
282 270
283 ath9k_ps_restore(sc); 271 ath9k_ps_restore(sc);
284 ath_dbg(common, ANY, "WoW enabled in ath9k\n"); 272 ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
285 atomic_inc(&sc->wow_sleep_proc_intr);
286 273
274 set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
287fail_wow: 275fail_wow:
288 mutex_unlock(&sc->mutex); 276 mutex_unlock(&sc->mutex);
289 return ret; 277 return ret;
@@ -294,7 +282,7 @@ int ath9k_resume(struct ieee80211_hw *hw)
294 struct ath_softc *sc = hw->priv; 282 struct ath_softc *sc = hw->priv;
295 struct ath_hw *ah = sc->sc_ah; 283 struct ath_hw *ah = sc->sc_ah;
296 struct ath_common *common = ath9k_hw_common(ah); 284 struct ath_common *common = ath9k_hw_common(ah);
297 u32 wow_status; 285 u8 status;
298 286
299 mutex_lock(&sc->mutex); 287 mutex_lock(&sc->mutex);
300 288
@@ -309,29 +297,14 @@ int ath9k_resume(struct ieee80211_hw *hw)
309 297
310 spin_unlock_bh(&sc->sc_pcu_lock); 298 spin_unlock_bh(&sc->sc_pcu_lock);
311 299
312 wow_status = ath9k_hw_wow_wakeup(ah); 300 status = ath9k_hw_wow_wakeup(ah);
313 301 ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
314 if (atomic_read(&sc->wow_got_bmiss_intr) == 0) {
315 /*
316 * some devices may not pick beacon miss
317 * as the reason they woke up so we add
318 * that here for that shortcoming.
319 */
320 wow_status |= AH_WOW_BEACON_MISS;
321 atomic_dec(&sc->wow_got_bmiss_intr);
322 ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n");
323 }
324
325 atomic_dec(&sc->wow_sleep_proc_intr);
326
327 if (wow_status) {
328 ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n",
329 ath9k_hw_wow_event_to_string(wow_status), wow_status);
330 }
331 302
332 ath_restart_work(sc); 303 ath_restart_work(sc);
333 ath9k_start_btcoex(sc); 304 ath9k_start_btcoex(sc);
334 305
306 clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
307
335 ath9k_ps_restore(sc); 308 ath9k_ps_restore(sc);
336 mutex_unlock(&sc->mutex); 309 mutex_unlock(&sc->mutex);
337 310
@@ -341,22 +314,35 @@ int ath9k_resume(struct ieee80211_hw *hw)
341void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) 314void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
342{ 315{
343 struct ath_softc *sc = hw->priv; 316 struct ath_softc *sc = hw->priv;
317 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
344 318
345 mutex_lock(&sc->mutex); 319 mutex_lock(&sc->mutex);
346 device_init_wakeup(sc->dev, 1);
347 device_set_wakeup_enable(sc->dev, enabled); 320 device_set_wakeup_enable(sc->dev, enabled);
348 mutex_unlock(&sc->mutex); 321 mutex_unlock(&sc->mutex);
322
323 ath_dbg(common, WOW, "WoW wakeup source is %s\n",
324 (enabled) ? "enabled" : "disabled");
349} 325}
350 326
351void ath9k_init_wow(struct ieee80211_hw *hw) 327void ath9k_init_wow(struct ieee80211_hw *hw)
352{ 328{
353 struct ath_softc *sc = hw->priv; 329 struct ath_softc *sc = hw->priv;
330 struct ath_hw *ah = sc->sc_ah;
331
332 if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
333 if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
334 hw->wiphy->wowlan = &ath9k_wowlan_support;
335 else
336 hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
354 337
355 if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && 338 device_init_wakeup(sc->dev, 1);
356 (sc->driver_data & ATH9K_PCI_WOW) && 339 }
357 device_can_wakeup(sc->dev)) 340}
358 hw->wiphy->wowlan = &ath9k_wowlan_support; 341
342void ath9k_deinit_wow(struct ieee80211_hw *hw)
343{
344 struct ath_softc *sc = hw->priv;
359 345
360 atomic_set(&sc->wow_sleep_proc_intr, -1); 346 if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
361 atomic_set(&sc->wow_got_bmiss_intr, -1); 347 device_init_wakeup(sc->dev, 0);
362} 348}