diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/wow.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/wow.c | 228 |
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 | ||
19 | static const struct wiphy_wowlan_support ath9k_wowlan_support = { | 19 | static 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 | ||
26 | static void ath9k_wow_map_triggers(struct ath_softc *sc, | 26 | static 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 | |||
33 | static 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 | ||
43 | static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) | 50 | static 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 | 134 | exit: | |
135 | return ret; | ||
135 | } | 136 | } |
136 | 137 | ||
137 | static void ath9k_wow_add_pattern(struct ath_softc *sc, | 138 | static 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 | ||
187 | int ath9k_suspend(struct ieee80211_hw *hw, | 167 | int 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); | ||
287 | fail_wow: | 275 | fail_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) | |||
341 | void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) | 314 | void 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 | ||
351 | void ath9k_init_wow(struct ieee80211_hw *hw) | 327 | void 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 | |
342 | void 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 | } |