18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2013 Qualcomm Atheros, Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "ath9k.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = { 208c2ecf20Sopenharmony_ci .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, 218c2ecf20Sopenharmony_ci .n_patterns = MAX_NUM_USER_PATTERN, 228c2ecf20Sopenharmony_ci .pattern_min_len = 1, 238c2ecf20Sopenharmony_ci .pattern_max_len = MAX_PATTERN_SIZE, 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic const struct wiphy_wowlan_support ath9k_wowlan_support = { 278c2ecf20Sopenharmony_ci .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, 288c2ecf20Sopenharmony_ci .n_patterns = MAX_NUM_PATTERN - 2, 298c2ecf20Sopenharmony_ci .pattern_min_len = 1, 308c2ecf20Sopenharmony_ci .pattern_max_len = MAX_PATTERN_SIZE, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic u8 ath9k_wow_map_triggers(struct ath_softc *sc, 348c2ecf20Sopenharmony_ci struct cfg80211_wowlan *wowlan) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci u8 wow_triggers = 0; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (wowlan->disconnect) 398c2ecf20Sopenharmony_ci wow_triggers |= AH_WOW_LINK_CHANGE | 408c2ecf20Sopenharmony_ci AH_WOW_BEACON_MISS; 418c2ecf20Sopenharmony_ci if (wowlan->magic_pkt) 428c2ecf20Sopenharmony_ci wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (wowlan->n_patterns) 458c2ecf20Sopenharmony_ci wow_triggers |= AH_WOW_USER_PATTERN_EN; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return wow_triggers; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 538c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 548c2ecf20Sopenharmony_ci int pattern_count = 0; 558c2ecf20Sopenharmony_ci int ret, i, byte_cnt = 0; 568c2ecf20Sopenharmony_ci u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; 578c2ecf20Sopenharmony_ci u8 dis_deauth_mask[MAX_PATTERN_SIZE]; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE); 608c2ecf20Sopenharmony_ci memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * Create Dissassociate / Deauthenticate packet filter 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes 668c2ecf20Sopenharmony_ci * +--------------+----------+---------+--------+--------+---- 678c2ecf20Sopenharmony_ci * + Frame Control+ Duration + DA + SA + BSSID + 688c2ecf20Sopenharmony_ci * +--------------+----------+---------+--------+--------+---- 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * The above is the management frame format for disassociate/ 718c2ecf20Sopenharmony_ci * deauthenticate pattern, from this we need to match the first byte 728c2ecf20Sopenharmony_ci * of 'Frame Control' and DA, SA, and BSSID fields 738c2ecf20Sopenharmony_ci * (skipping 2nd byte of FC and Duration feild. 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Disassociate pattern 768c2ecf20Sopenharmony_ci * -------------------- 778c2ecf20Sopenharmony_ci * Frame control = 00 00 1010 788c2ecf20Sopenharmony_ci * DA, SA, BSSID = x:x:x:x:x:x 798c2ecf20Sopenharmony_ci * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x 808c2ecf20Sopenharmony_ci * | x:x:x:x:x:x -- 22 bytes 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Deauthenticate pattern 838c2ecf20Sopenharmony_ci * ---------------------- 848c2ecf20Sopenharmony_ci * Frame control = 00 00 1100 858c2ecf20Sopenharmony_ci * DA, SA, BSSID = x:x:x:x:x:x 868c2ecf20Sopenharmony_ci * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x 878c2ecf20Sopenharmony_ci * | x:x:x:x:x:x -- 22 bytes 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Fill out the mask with all FF's */ 918c2ecf20Sopenharmony_ci for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++) 928c2ecf20Sopenharmony_ci dis_deauth_mask[i] = 0xff; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* copy the first byte of frame control field */ 958c2ecf20Sopenharmony_ci dis_deauth_pattern[byte_cnt] = 0xa0; 968c2ecf20Sopenharmony_ci byte_cnt++; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* skip 2nd byte of frame control and Duration field */ 998c2ecf20Sopenharmony_ci byte_cnt += 3; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * need not match the destination mac address, it can be a broadcast 1038c2ecf20Sopenharmony_ci * mac address or an unicast to this station 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci byte_cnt += 6; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* copy the source mac address */ 1088c2ecf20Sopenharmony_ci memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci byte_cnt += 6; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* copy the bssid, its same as the source mac address */ 1138c2ecf20Sopenharmony_ci memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* Create Disassociate pattern mask */ 1168c2ecf20Sopenharmony_ci dis_deauth_mask[0] = 0xfe; 1178c2ecf20Sopenharmony_ci dis_deauth_mask[1] = 0x03; 1188c2ecf20Sopenharmony_ci dis_deauth_mask[2] = 0xc0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, 1218c2ecf20Sopenharmony_ci pattern_count, byte_cnt); 1228c2ecf20Sopenharmony_ci if (ret) 1238c2ecf20Sopenharmony_ci goto exit; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci pattern_count++; 1268c2ecf20Sopenharmony_ci /* 1278c2ecf20Sopenharmony_ci * for de-authenticate pattern, only the first byte of the frame 1288c2ecf20Sopenharmony_ci * control field gets changed from 0xA0 to 0xC0 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci dis_deauth_pattern[0] = 0xC0; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, 1338c2ecf20Sopenharmony_ci pattern_count, byte_cnt); 1348c2ecf20Sopenharmony_ciexit: 1358c2ecf20Sopenharmony_ci return ret; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int ath9k_wow_add_pattern(struct ath_softc *sc, 1398c2ecf20Sopenharmony_ci struct cfg80211_wowlan *wowlan) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 1428c2ecf20Sopenharmony_ci struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 1438c2ecf20Sopenharmony_ci u8 wow_pattern[MAX_PATTERN_SIZE]; 1448c2ecf20Sopenharmony_ci u8 wow_mask[MAX_PATTERN_SIZE]; 1458c2ecf20Sopenharmony_ci int mask_len, ret = 0; 1468c2ecf20Sopenharmony_ci s8 i = 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < wowlan->n_patterns; i++) { 1498c2ecf20Sopenharmony_ci mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8); 1508c2ecf20Sopenharmony_ci memset(wow_pattern, 0, MAX_PATTERN_SIZE); 1518c2ecf20Sopenharmony_ci memset(wow_mask, 0, MAX_PATTERN_SIZE); 1528c2ecf20Sopenharmony_ci memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len); 1538c2ecf20Sopenharmony_ci memcpy(wow_mask, patterns[i].mask, mask_len); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ret = ath9k_hw_wow_apply_pattern(ah, 1568c2ecf20Sopenharmony_ci wow_pattern, 1578c2ecf20Sopenharmony_ci wow_mask, 1588c2ecf20Sopenharmony_ci i + 2, 1598c2ecf20Sopenharmony_ci patterns[i].pattern_len); 1608c2ecf20Sopenharmony_ci if (ret) 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return ret; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciint ath9k_suspend(struct ieee80211_hw *hw, 1688c2ecf20Sopenharmony_ci struct cfg80211_wowlan *wowlan) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 1718c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 1728c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 1738c2ecf20Sopenharmony_ci u8 triggers; 1748c2ecf20Sopenharmony_ci int ret = 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ath9k_deinit_channel_context(sc); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_INVALID, &common->op_flags)) { 1818c2ecf20Sopenharmony_ci ath_err(common, "Device not present\n"); 1828c2ecf20Sopenharmony_ci ret = -ENODEV; 1838c2ecf20Sopenharmony_ci goto fail_wow; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (WARN_ON(!wowlan)) { 1878c2ecf20Sopenharmony_ci ath_err(common, "None of the WoW triggers enabled\n"); 1888c2ecf20Sopenharmony_ci ret = -EINVAL; 1898c2ecf20Sopenharmony_ci goto fail_wow; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (sc->cur_chan->nvifs > 1) { 1938c2ecf20Sopenharmony_ci ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); 1948c2ecf20Sopenharmony_ci ret = 1; 1958c2ecf20Sopenharmony_ci goto fail_wow; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (ath9k_is_chanctx_enabled()) { 1998c2ecf20Sopenharmony_ci if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) { 2008c2ecf20Sopenharmony_ci ath_dbg(common, WOW, 2018c2ecf20Sopenharmony_ci "Multi-channel WOW is not supported\n"); 2028c2ecf20Sopenharmony_ci ret = 1; 2038c2ecf20Sopenharmony_ci goto fail_wow; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { 2088c2ecf20Sopenharmony_ci ath_dbg(common, WOW, "None of the STA vifs are associated\n"); 2098c2ecf20Sopenharmony_ci ret = 1; 2108c2ecf20Sopenharmony_ci goto fail_wow; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci triggers = ath9k_wow_map_triggers(sc, wowlan); 2148c2ecf20Sopenharmony_ci if (!triggers) { 2158c2ecf20Sopenharmony_ci ath_dbg(common, WOW, "No valid WoW triggers\n"); 2168c2ecf20Sopenharmony_ci ret = 1; 2178c2ecf20Sopenharmony_ci goto fail_wow; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ath_cancel_work(sc); 2218c2ecf20Sopenharmony_ci ath_stop_ani(sc); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ath9k_stop_btcoex(sc); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * Enable wake up on recieving disassoc/deauth 2298c2ecf20Sopenharmony_ci * frame by default. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci ret = ath9k_wow_add_disassoc_deauth_pattern(sc); 2328c2ecf20Sopenharmony_ci if (ret) { 2338c2ecf20Sopenharmony_ci ath_err(common, 2348c2ecf20Sopenharmony_ci "Unable to add disassoc/deauth pattern: %d\n", ret); 2358c2ecf20Sopenharmony_ci goto fail_wow; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (triggers & AH_WOW_USER_PATTERN_EN) { 2398c2ecf20Sopenharmony_ci ret = ath9k_wow_add_pattern(sc, wowlan); 2408c2ecf20Sopenharmony_ci if (ret) { 2418c2ecf20Sopenharmony_ci ath_err(common, 2428c2ecf20Sopenharmony_ci "Unable to add user pattern: %d\n", ret); 2438c2ecf20Sopenharmony_ci goto fail_wow; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 2488c2ecf20Sopenharmony_ci /* 2498c2ecf20Sopenharmony_ci * To avoid false wake, we enable beacon miss interrupt only 2508c2ecf20Sopenharmony_ci * when we go to sleep. We save the current interrupt mask 2518c2ecf20Sopenharmony_ci * so we can restore it after the system wakes up 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci sc->wow_intr_before_sleep = ah->imask; 2548c2ecf20Sopenharmony_ci ah->imask &= ~ATH9K_INT_GLOBAL; 2558c2ecf20Sopenharmony_ci ath9k_hw_disable_interrupts(ah); 2568c2ecf20Sopenharmony_ci ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL; 2578c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 2588c2ecf20Sopenharmony_ci ath9k_hw_enable_interrupts(ah); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * we can now sync irq and kill any running tasklets, since we already 2648c2ecf20Sopenharmony_ci * disabled interrupts and not holding a spin lock 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci synchronize_irq(sc->irq); 2678c2ecf20Sopenharmony_ci tasklet_kill(&sc->intr_tq); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ath9k_hw_wow_enable(ah, triggers); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 2728c2ecf20Sopenharmony_ci ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci set_bit(ATH_OP_WOW_ENABLED, &common->op_flags); 2758c2ecf20Sopenharmony_cifail_wow: 2768c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 2778c2ecf20Sopenharmony_ci return ret; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciint ath9k_resume(struct ieee80211_hw *hw) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 2838c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 2848c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 2858c2ecf20Sopenharmony_ci u8 status; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ath9k_ps_wakeup(sc); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci spin_lock_bh(&sc->sc_pcu_lock); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ath9k_hw_disable_interrupts(ah); 2948c2ecf20Sopenharmony_ci ah->imask = sc->wow_intr_before_sleep; 2958c2ecf20Sopenharmony_ci ath9k_hw_set_interrupts(ah); 2968c2ecf20Sopenharmony_ci ath9k_hw_enable_interrupts(ah); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci spin_unlock_bh(&sc->sc_pcu_lock); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci status = ath9k_hw_wow_wakeup(ah); 3018c2ecf20Sopenharmony_ci ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ath_restart_work(sc); 3048c2ecf20Sopenharmony_ci ath9k_start_btcoex(sc); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ath9k_ps_restore(sc); 3098c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_civoid ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 3178c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(sc->sc_ah); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci mutex_lock(&sc->mutex); 3208c2ecf20Sopenharmony_ci device_set_wakeup_enable(sc->dev, enabled); 3218c2ecf20Sopenharmony_ci mutex_unlock(&sc->mutex); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ath_dbg(common, WOW, "WoW wakeup source is %s\n", 3248c2ecf20Sopenharmony_ci (enabled) ? "enabled" : "disabled"); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_civoid ath9k_init_wow(struct ieee80211_hw *hw) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 3308c2ecf20Sopenharmony_ci struct ath_hw *ah = sc->sc_ah; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) { 3338c2ecf20Sopenharmony_ci if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah)) 3348c2ecf20Sopenharmony_ci hw->wiphy->wowlan = &ath9k_wowlan_support; 3358c2ecf20Sopenharmony_ci else 3368c2ecf20Sopenharmony_ci hw->wiphy->wowlan = &ath9k_wowlan_support_legacy; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci device_init_wakeup(sc->dev, 1); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_civoid ath9k_deinit_wow(struct ieee80211_hw *hw) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct ath_softc *sc = hw->priv; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) 3478c2ecf20Sopenharmony_ci device_init_wakeup(sc->dev, 0); 3488c2ecf20Sopenharmony_ci} 349