wow.c (9195B)
1/* 2 * Copyright (c) 2013 Qualcomm Atheros, Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include "ath9k.h" 18 19static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = { 20 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, 21 .n_patterns = MAX_NUM_USER_PATTERN, 22 .pattern_min_len = 1, 23 .pattern_max_len = MAX_PATTERN_SIZE, 24}; 25 26static const struct wiphy_wowlan_support ath9k_wowlan_support = { 27 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, 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) 35{ 36 u8 wow_triggers = 0; 37 38 if (wowlan->disconnect) 39 wow_triggers |= AH_WOW_LINK_CHANGE | 40 AH_WOW_BEACON_MISS; 41 if (wowlan->magic_pkt) 42 wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; 43 44 if (wowlan->n_patterns) 45 wow_triggers |= AH_WOW_USER_PATTERN_EN; 46 47 return wow_triggers; 48} 49 50static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) 51{ 52 struct ath_hw *ah = sc->sc_ah; 53 struct ath_common *common = ath9k_hw_common(ah); 54 int pattern_count = 0; 55 int ret, i, byte_cnt = 0; 56 u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; 57 u8 dis_deauth_mask[MAX_PATTERN_SIZE]; 58 59 memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE); 60 memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE); 61 62 /* 63 * Create Dissassociate / Deauthenticate packet filter 64 * 65 * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes 66 * +--------------+----------+---------+--------+--------+---- 67 * + Frame Control+ Duration + DA + SA + BSSID + 68 * +--------------+----------+---------+--------+--------+---- 69 * 70 * The above is the management frame format for disassociate/ 71 * deauthenticate pattern, from this we need to match the first byte 72 * of 'Frame Control' and DA, SA, and BSSID fields 73 * (skipping 2nd byte of FC and Duration feild. 74 * 75 * Disassociate pattern 76 * -------------------- 77 * Frame control = 00 00 1010 78 * DA, SA, BSSID = x:x:x:x:x:x 79 * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x 80 * | x:x:x:x:x:x -- 22 bytes 81 * 82 * Deauthenticate pattern 83 * ---------------------- 84 * Frame control = 00 00 1100 85 * DA, SA, BSSID = x:x:x:x:x:x 86 * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x 87 * | x:x:x:x:x:x -- 22 bytes 88 */ 89 90 /* Fill out the mask with all FF's */ 91 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++) 92 dis_deauth_mask[i] = 0xff; 93 94 /* copy the first byte of frame control field */ 95 dis_deauth_pattern[byte_cnt] = 0xa0; 96 byte_cnt++; 97 98 /* skip 2nd byte of frame control and Duration field */ 99 byte_cnt += 3; 100 101 /* 102 * need not match the destination mac address, it can be a broadcast 103 * mac address or an unicast to this station 104 */ 105 byte_cnt += 6; 106 107 /* copy the source mac address */ 108 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); 109 110 byte_cnt += 6; 111 112 /* copy the bssid, its same as the source mac address */ 113 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); 114 115 /* Create Disassociate pattern mask */ 116 dis_deauth_mask[0] = 0xfe; 117 dis_deauth_mask[1] = 0x03; 118 dis_deauth_mask[2] = 0xc0; 119 120 ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, 121 pattern_count, byte_cnt); 122 if (ret) 123 goto exit; 124 125 pattern_count++; 126 /* 127 * for de-authenticate pattern, only the first byte of the frame 128 * control field gets changed from 0xA0 to 0xC0 129 */ 130 dis_deauth_pattern[0] = 0xC0; 131 132 ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, 133 pattern_count, byte_cnt); 134exit: 135 return ret; 136} 137 138static int ath9k_wow_add_pattern(struct ath_softc *sc, 139 struct cfg80211_wowlan *wowlan) 140{ 141 struct ath_hw *ah = sc->sc_ah; 142 struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 143 u8 wow_pattern[MAX_PATTERN_SIZE]; 144 u8 wow_mask[MAX_PATTERN_SIZE]; 145 int mask_len, ret = 0; 146 s8 i = 0; 147 148 for (i = 0; i < wowlan->n_patterns; i++) { 149 mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8); 150 memset(wow_pattern, 0, MAX_PATTERN_SIZE); 151 memset(wow_mask, 0, MAX_PATTERN_SIZE); 152 memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len); 153 memcpy(wow_mask, patterns[i].mask, mask_len); 154 155 ret = ath9k_hw_wow_apply_pattern(ah, 156 wow_pattern, 157 wow_mask, 158 i + 2, 159 patterns[i].pattern_len); 160 if (ret) 161 break; 162 } 163 164 return ret; 165} 166 167int ath9k_suspend(struct ieee80211_hw *hw, 168 struct cfg80211_wowlan *wowlan) 169{ 170 struct ath_softc *sc = hw->priv; 171 struct ath_hw *ah = sc->sc_ah; 172 struct ath_common *common = ath9k_hw_common(ah); 173 u8 triggers; 174 int ret = 0; 175 176 ath9k_deinit_channel_context(sc); 177 178 mutex_lock(&sc->mutex); 179 180 if (test_bit(ATH_OP_INVALID, &common->op_flags)) { 181 ath_err(common, "Device not present\n"); 182 ret = -ENODEV; 183 goto fail_wow; 184 } 185 186 if (WARN_ON(!wowlan)) { 187 ath_err(common, "None of the WoW triggers enabled\n"); 188 ret = -EINVAL; 189 goto fail_wow; 190 } 191 192 if (sc->cur_chan->nvifs > 1) { 193 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); 194 ret = 1; 195 goto fail_wow; 196 } 197 198 if (ath9k_is_chanctx_enabled()) { 199 if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) { 200 ath_dbg(common, WOW, 201 "Multi-channel WOW is not supported\n"); 202 ret = 1; 203 goto fail_wow; 204 } 205 } 206 207 if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { 208 ath_dbg(common, WOW, "None of the STA vifs are associated\n"); 209 ret = 1; 210 goto fail_wow; 211 } 212 213 triggers = ath9k_wow_map_triggers(sc, wowlan); 214 if (!triggers) { 215 ath_dbg(common, WOW, "No valid WoW triggers\n"); 216 ret = 1; 217 goto fail_wow; 218 } 219 220 ath_cancel_work(sc); 221 ath_stop_ani(sc); 222 223 ath9k_ps_wakeup(sc); 224 225 ath9k_stop_btcoex(sc); 226 227 /* 228 * Enable wake up on recieving disassoc/deauth 229 * frame by default. 230 */ 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 } 237 238 if (triggers & AH_WOW_USER_PATTERN_EN) { 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 } 246 247 spin_lock_bh(&sc->sc_pcu_lock); 248 /* 249 * To avoid false wake, we enable beacon miss interrupt only 250 * when we go to sleep. We save the current interrupt mask 251 * so we can restore it after the system wakes up 252 */ 253 sc->wow_intr_before_sleep = ah->imask; 254 ah->imask &= ~ATH9K_INT_GLOBAL; 255 ath9k_hw_disable_interrupts(ah); 256 ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL; 257 ath9k_hw_set_interrupts(ah); 258 ath9k_hw_enable_interrupts(ah); 259 260 spin_unlock_bh(&sc->sc_pcu_lock); 261 262 /* 263 * we can now sync irq and kill any running tasklets, since we already 264 * disabled interrupts and not holding a spin lock 265 */ 266 synchronize_irq(sc->irq); 267 tasklet_kill(&sc->intr_tq); 268 269 ath9k_hw_wow_enable(ah, triggers); 270 271 ath9k_ps_restore(sc); 272 ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers); 273 274 set_bit(ATH_OP_WOW_ENABLED, &common->op_flags); 275fail_wow: 276 mutex_unlock(&sc->mutex); 277 return ret; 278} 279 280int ath9k_resume(struct ieee80211_hw *hw) 281{ 282 struct ath_softc *sc = hw->priv; 283 struct ath_hw *ah = sc->sc_ah; 284 struct ath_common *common = ath9k_hw_common(ah); 285 u8 status; 286 287 mutex_lock(&sc->mutex); 288 289 ath9k_ps_wakeup(sc); 290 291 spin_lock_bh(&sc->sc_pcu_lock); 292 293 ath9k_hw_disable_interrupts(ah); 294 ah->imask = sc->wow_intr_before_sleep; 295 ath9k_hw_set_interrupts(ah); 296 ath9k_hw_enable_interrupts(ah); 297 298 spin_unlock_bh(&sc->sc_pcu_lock); 299 300 status = ath9k_hw_wow_wakeup(ah); 301 ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status); 302 303 ath_restart_work(sc); 304 ath9k_start_btcoex(sc); 305 306 clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags); 307 308 ath9k_ps_restore(sc); 309 mutex_unlock(&sc->mutex); 310 311 return 0; 312} 313 314void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) 315{ 316 struct ath_softc *sc = hw->priv; 317 struct ath_common *common = ath9k_hw_common(sc->sc_ah); 318 319 mutex_lock(&sc->mutex); 320 device_set_wakeup_enable(sc->dev, enabled); 321 mutex_unlock(&sc->mutex); 322 323 ath_dbg(common, WOW, "WoW wakeup source is %s\n", 324 (enabled) ? "enabled" : "disabled"); 325} 326 327void ath9k_init_wow(struct ieee80211_hw *hw) 328{ 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; 337 338 device_init_wakeup(sc->dev, 1); 339 } 340} 341 342void ath9k_deinit_wow(struct ieee80211_hw *hw) 343{ 344 struct ath_softc *sc = hw->priv; 345 346 if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) 347 device_init_wakeup(sc->dev, 0); 348}