0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include "ath9k.h"
0018
0019 static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
0020 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
0021 .n_patterns = MAX_NUM_USER_PATTERN,
0022 .pattern_min_len = 1,
0023 .pattern_max_len = MAX_PATTERN_SIZE,
0024 };
0025
0026 static const struct wiphy_wowlan_support ath9k_wowlan_support = {
0027 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
0028 .n_patterns = MAX_NUM_PATTERN - 2,
0029 .pattern_min_len = 1,
0030 .pattern_max_len = MAX_PATTERN_SIZE,
0031 };
0032
0033 static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
0034 struct cfg80211_wowlan *wowlan)
0035 {
0036 u8 wow_triggers = 0;
0037
0038 if (wowlan->disconnect)
0039 wow_triggers |= AH_WOW_LINK_CHANGE |
0040 AH_WOW_BEACON_MISS;
0041 if (wowlan->magic_pkt)
0042 wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
0043
0044 if (wowlan->n_patterns)
0045 wow_triggers |= AH_WOW_USER_PATTERN_EN;
0046
0047 return wow_triggers;
0048 }
0049
0050 static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
0051 {
0052 struct ath_hw *ah = sc->sc_ah;
0053 struct ath_common *common = ath9k_hw_common(ah);
0054 int pattern_count = 0;
0055 int ret, i, byte_cnt = 0;
0056 u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
0057 u8 dis_deauth_mask[MAX_PATTERN_SIZE];
0058
0059 memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
0060 memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
0092 dis_deauth_mask[i] = 0xff;
0093
0094
0095 dis_deauth_pattern[byte_cnt] = 0xa0;
0096 byte_cnt++;
0097
0098
0099 byte_cnt += 3;
0100
0101
0102
0103
0104
0105 byte_cnt += 6;
0106
0107
0108 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
0109
0110 byte_cnt += 6;
0111
0112
0113 memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
0114
0115
0116 dis_deauth_mask[0] = 0xfe;
0117 dis_deauth_mask[1] = 0x03;
0118 dis_deauth_mask[2] = 0xc0;
0119
0120 ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
0121 pattern_count, byte_cnt);
0122 if (ret)
0123 goto exit;
0124
0125 pattern_count++;
0126
0127
0128
0129
0130 dis_deauth_pattern[0] = 0xC0;
0131
0132 ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
0133 pattern_count, byte_cnt);
0134 exit:
0135 return ret;
0136 }
0137
0138 static int ath9k_wow_add_pattern(struct ath_softc *sc,
0139 struct cfg80211_wowlan *wowlan)
0140 {
0141 struct ath_hw *ah = sc->sc_ah;
0142 struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
0143 u8 wow_pattern[MAX_PATTERN_SIZE];
0144 u8 wow_mask[MAX_PATTERN_SIZE];
0145 int mask_len, ret = 0;
0146 s8 i = 0;
0147
0148 for (i = 0; i < wowlan->n_patterns; i++) {
0149 mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
0150 memset(wow_pattern, 0, MAX_PATTERN_SIZE);
0151 memset(wow_mask, 0, MAX_PATTERN_SIZE);
0152 memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
0153 memcpy(wow_mask, patterns[i].mask, mask_len);
0154
0155 ret = ath9k_hw_wow_apply_pattern(ah,
0156 wow_pattern,
0157 wow_mask,
0158 i + 2,
0159 patterns[i].pattern_len);
0160 if (ret)
0161 break;
0162 }
0163
0164 return ret;
0165 }
0166
0167 int ath9k_suspend(struct ieee80211_hw *hw,
0168 struct cfg80211_wowlan *wowlan)
0169 {
0170 struct ath_softc *sc = hw->priv;
0171 struct ath_hw *ah = sc->sc_ah;
0172 struct ath_common *common = ath9k_hw_common(ah);
0173 u8 triggers;
0174 int ret = 0;
0175
0176 ath9k_deinit_channel_context(sc);
0177
0178 mutex_lock(&sc->mutex);
0179
0180 if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
0181 ath_err(common, "Device not present\n");
0182 ret = -ENODEV;
0183 goto fail_wow;
0184 }
0185
0186 if (WARN_ON(!wowlan)) {
0187 ath_err(common, "None of the WoW triggers enabled\n");
0188 ret = -EINVAL;
0189 goto fail_wow;
0190 }
0191
0192 if (sc->cur_chan->nvifs > 1) {
0193 ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
0194 ret = 1;
0195 goto fail_wow;
0196 }
0197
0198 if (ath9k_is_chanctx_enabled()) {
0199 if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
0200 ath_dbg(common, WOW,
0201 "Multi-channel WOW is not supported\n");
0202 ret = 1;
0203 goto fail_wow;
0204 }
0205 }
0206
0207 if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
0208 ath_dbg(common, WOW, "None of the STA vifs are associated\n");
0209 ret = 1;
0210 goto fail_wow;
0211 }
0212
0213 triggers = ath9k_wow_map_triggers(sc, wowlan);
0214 if (!triggers) {
0215 ath_dbg(common, WOW, "No valid WoW triggers\n");
0216 ret = 1;
0217 goto fail_wow;
0218 }
0219
0220 ath_cancel_work(sc);
0221 ath_stop_ani(sc);
0222
0223 ath9k_ps_wakeup(sc);
0224
0225 ath9k_stop_btcoex(sc);
0226
0227
0228
0229
0230
0231 ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
0232 if (ret) {
0233 ath_err(common,
0234 "Unable to add disassoc/deauth pattern: %d\n", ret);
0235 goto fail_wow;
0236 }
0237
0238 if (triggers & AH_WOW_USER_PATTERN_EN) {
0239 ret = ath9k_wow_add_pattern(sc, wowlan);
0240 if (ret) {
0241 ath_err(common,
0242 "Unable to add user pattern: %d\n", ret);
0243 goto fail_wow;
0244 }
0245 }
0246
0247 spin_lock_bh(&sc->sc_pcu_lock);
0248
0249
0250
0251
0252
0253 sc->wow_intr_before_sleep = ah->imask;
0254 ah->imask &= ~ATH9K_INT_GLOBAL;
0255 ath9k_hw_disable_interrupts(ah);
0256 ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
0257 ath9k_hw_set_interrupts(ah);
0258 ath9k_hw_enable_interrupts(ah);
0259
0260 spin_unlock_bh(&sc->sc_pcu_lock);
0261
0262
0263
0264
0265
0266 synchronize_irq(sc->irq);
0267 tasklet_kill(&sc->intr_tq);
0268
0269 ath9k_hw_wow_enable(ah, triggers);
0270
0271 ath9k_ps_restore(sc);
0272 ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
0273
0274 set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
0275 fail_wow:
0276 mutex_unlock(&sc->mutex);
0277 return ret;
0278 }
0279
0280 int ath9k_resume(struct ieee80211_hw *hw)
0281 {
0282 struct ath_softc *sc = hw->priv;
0283 struct ath_hw *ah = sc->sc_ah;
0284 struct ath_common *common = ath9k_hw_common(ah);
0285 u8 status;
0286
0287 mutex_lock(&sc->mutex);
0288
0289 ath9k_ps_wakeup(sc);
0290
0291 spin_lock_bh(&sc->sc_pcu_lock);
0292
0293 ath9k_hw_disable_interrupts(ah);
0294 ah->imask = sc->wow_intr_before_sleep;
0295 ath9k_hw_set_interrupts(ah);
0296 ath9k_hw_enable_interrupts(ah);
0297
0298 spin_unlock_bh(&sc->sc_pcu_lock);
0299
0300 status = ath9k_hw_wow_wakeup(ah);
0301 ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
0302
0303 ath_restart_work(sc);
0304 ath9k_start_btcoex(sc);
0305
0306 clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
0307
0308 ath9k_ps_restore(sc);
0309 mutex_unlock(&sc->mutex);
0310
0311 return 0;
0312 }
0313
0314 void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
0315 {
0316 struct ath_softc *sc = hw->priv;
0317 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
0318
0319 mutex_lock(&sc->mutex);
0320 device_set_wakeup_enable(sc->dev, enabled);
0321 mutex_unlock(&sc->mutex);
0322
0323 ath_dbg(common, WOW, "WoW wakeup source is %s\n",
0324 (enabled) ? "enabled" : "disabled");
0325 }
0326
0327 void ath9k_init_wow(struct ieee80211_hw *hw)
0328 {
0329 struct ath_softc *sc = hw->priv;
0330 struct ath_hw *ah = sc->sc_ah;
0331
0332 if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
0333 if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
0334 hw->wiphy->wowlan = &ath9k_wowlan_support;
0335 else
0336 hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
0337
0338 device_init_wakeup(sc->dev, 1);
0339 }
0340 }
0341
0342 void ath9k_deinit_wow(struct ieee80211_hw *hw)
0343 {
0344 struct ath_softc *sc = hw->priv;
0345
0346 if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
0347 device_init_wakeup(sc->dev, 0);
0348 }