15fd3ac3cSJanusz Dziedzic /* 28b1083d6SKalle Valo * Copyright (c) 2015-2017 Qualcomm Atheros, Inc. 3*fa3440faSWen Gong * Copyright (c) 2018, The Linux Foundation. All rights reserved. 45fd3ac3cSJanusz Dziedzic * 55fd3ac3cSJanusz Dziedzic * Permission to use, copy, modify, and/or distribute this software for any 65fd3ac3cSJanusz Dziedzic * purpose with or without fee is hereby granted, provided that the above 75fd3ac3cSJanusz Dziedzic * copyright notice and this permission notice appear in all copies. 85fd3ac3cSJanusz Dziedzic * 95fd3ac3cSJanusz Dziedzic * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 105fd3ac3cSJanusz Dziedzic * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 115fd3ac3cSJanusz Dziedzic * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 125fd3ac3cSJanusz Dziedzic * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 135fd3ac3cSJanusz Dziedzic * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 145fd3ac3cSJanusz Dziedzic * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 155fd3ac3cSJanusz Dziedzic * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 165fd3ac3cSJanusz Dziedzic */ 175fd3ac3cSJanusz Dziedzic 185fd3ac3cSJanusz Dziedzic #include "mac.h" 195fd3ac3cSJanusz Dziedzic 205fd3ac3cSJanusz Dziedzic #include <net/mac80211.h> 215fd3ac3cSJanusz Dziedzic #include "hif.h" 225fd3ac3cSJanusz Dziedzic #include "core.h" 235fd3ac3cSJanusz Dziedzic #include "debug.h" 245fd3ac3cSJanusz Dziedzic #include "wmi.h" 255fd3ac3cSJanusz Dziedzic #include "wmi-ops.h" 265fd3ac3cSJanusz Dziedzic 275fd3ac3cSJanusz Dziedzic static const struct wiphy_wowlan_support ath10k_wowlan_support = { 285fd3ac3cSJanusz Dziedzic .flags = WIPHY_WOWLAN_DISCONNECT | 295fd3ac3cSJanusz Dziedzic WIPHY_WOWLAN_MAGIC_PKT, 3025c86619SJanusz Dziedzic .pattern_min_len = WOW_MIN_PATTERN_SIZE, 3125c86619SJanusz Dziedzic .pattern_max_len = WOW_MAX_PATTERN_SIZE, 3225c86619SJanusz Dziedzic .max_pkt_offset = WOW_MAX_PKT_OFFSET, 335fd3ac3cSJanusz Dziedzic }; 345fd3ac3cSJanusz Dziedzic 355fd3ac3cSJanusz Dziedzic static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif) 365fd3ac3cSJanusz Dziedzic { 375fd3ac3cSJanusz Dziedzic struct ath10k *ar = arvif->ar; 385fd3ac3cSJanusz Dziedzic int i, ret; 395fd3ac3cSJanusz Dziedzic 405fd3ac3cSJanusz Dziedzic for (i = 0; i < WOW_EVENT_MAX; i++) { 415fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); 425fd3ac3cSJanusz Dziedzic if (ret) { 435fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n", 445fd3ac3cSJanusz Dziedzic wow_wakeup_event(i), arvif->vdev_id, ret); 455fd3ac3cSJanusz Dziedzic return ret; 465fd3ac3cSJanusz Dziedzic } 475fd3ac3cSJanusz Dziedzic } 485fd3ac3cSJanusz Dziedzic 4925c86619SJanusz Dziedzic for (i = 0; i < ar->wow.max_num_patterns; i++) { 5025c86619SJanusz Dziedzic ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); 5125c86619SJanusz Dziedzic if (ret) { 5225c86619SJanusz Dziedzic ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n", 5325c86619SJanusz Dziedzic i, arvif->vdev_id, ret); 5425c86619SJanusz Dziedzic return ret; 5525c86619SJanusz Dziedzic } 5625c86619SJanusz Dziedzic } 5725c86619SJanusz Dziedzic 585fd3ac3cSJanusz Dziedzic return 0; 595fd3ac3cSJanusz Dziedzic } 605fd3ac3cSJanusz Dziedzic 615fd3ac3cSJanusz Dziedzic static int ath10k_wow_cleanup(struct ath10k *ar) 625fd3ac3cSJanusz Dziedzic { 635fd3ac3cSJanusz Dziedzic struct ath10k_vif *arvif; 645fd3ac3cSJanusz Dziedzic int ret; 655fd3ac3cSJanusz Dziedzic 665fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 675fd3ac3cSJanusz Dziedzic 685fd3ac3cSJanusz Dziedzic list_for_each_entry(arvif, &ar->arvifs, list) { 695fd3ac3cSJanusz Dziedzic ret = ath10k_wow_vif_cleanup(arvif); 705fd3ac3cSJanusz Dziedzic if (ret) { 715fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n", 725fd3ac3cSJanusz Dziedzic arvif->vdev_id, ret); 735fd3ac3cSJanusz Dziedzic return ret; 745fd3ac3cSJanusz Dziedzic } 755fd3ac3cSJanusz Dziedzic } 765fd3ac3cSJanusz Dziedzic 775fd3ac3cSJanusz Dziedzic return 0; 785fd3ac3cSJanusz Dziedzic } 795fd3ac3cSJanusz Dziedzic 80*fa3440faSWen Gong /** 81*fa3440faSWen Gong * Convert a 802.3 format to a 802.11 format. 82*fa3440faSWen Gong * +------------+-----------+--------+----------------+ 83*fa3440faSWen Gong * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | 84*fa3440faSWen Gong * +------------+-----------+--------+----------------+ 85*fa3440faSWen Gong * |__ |_______ |____________ |________ 86*fa3440faSWen Gong * | | | | 87*fa3440faSWen Gong * +--+------------+----+-----------+---------------+-----------+ 88*fa3440faSWen Gong * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | 89*fa3440faSWen Gong * +--+------------+----+-----------+---------------+-----------+ 90*fa3440faSWen Gong */ 91*fa3440faSWen Gong static void ath10k_wow_convert_8023_to_80211 92*fa3440faSWen Gong (struct cfg80211_pkt_pattern *new, 93*fa3440faSWen Gong const struct cfg80211_pkt_pattern *old) 94*fa3440faSWen Gong { 95*fa3440faSWen Gong u8 hdr_8023_pattern[ETH_HLEN] = {}; 96*fa3440faSWen Gong u8 hdr_8023_bit_mask[ETH_HLEN] = {}; 97*fa3440faSWen Gong u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; 98*fa3440faSWen Gong u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; 99*fa3440faSWen Gong 100*fa3440faSWen Gong int total_len = old->pkt_offset + old->pattern_len; 101*fa3440faSWen Gong int hdr_80211_end_offset; 102*fa3440faSWen Gong 103*fa3440faSWen Gong struct ieee80211_hdr_3addr *new_hdr_pattern = 104*fa3440faSWen Gong (struct ieee80211_hdr_3addr *)hdr_80211_pattern; 105*fa3440faSWen Gong struct ieee80211_hdr_3addr *new_hdr_mask = 106*fa3440faSWen Gong (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; 107*fa3440faSWen Gong struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; 108*fa3440faSWen Gong struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; 109*fa3440faSWen Gong int hdr_len = sizeof(*new_hdr_pattern); 110*fa3440faSWen Gong 111*fa3440faSWen Gong struct rfc1042_hdr *new_rfc_pattern = 112*fa3440faSWen Gong (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); 113*fa3440faSWen Gong struct rfc1042_hdr *new_rfc_mask = 114*fa3440faSWen Gong (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); 115*fa3440faSWen Gong int rfc_len = sizeof(*new_rfc_pattern); 116*fa3440faSWen Gong 117*fa3440faSWen Gong memcpy(hdr_8023_pattern + old->pkt_offset, 118*fa3440faSWen Gong old->pattern, ETH_HLEN - old->pkt_offset); 119*fa3440faSWen Gong memcpy(hdr_8023_bit_mask + old->pkt_offset, 120*fa3440faSWen Gong old->mask, ETH_HLEN - old->pkt_offset); 121*fa3440faSWen Gong 122*fa3440faSWen Gong /* Copy destination address */ 123*fa3440faSWen Gong memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); 124*fa3440faSWen Gong memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); 125*fa3440faSWen Gong 126*fa3440faSWen Gong /* Copy source address */ 127*fa3440faSWen Gong memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); 128*fa3440faSWen Gong memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); 129*fa3440faSWen Gong 130*fa3440faSWen Gong /* Copy logic link type */ 131*fa3440faSWen Gong memcpy(&new_rfc_pattern->snap_type, 132*fa3440faSWen Gong &old_hdr_pattern->h_proto, 133*fa3440faSWen Gong sizeof(old_hdr_pattern->h_proto)); 134*fa3440faSWen Gong memcpy(&new_rfc_mask->snap_type, 135*fa3440faSWen Gong &old_hdr_mask->h_proto, 136*fa3440faSWen Gong sizeof(old_hdr_mask->h_proto)); 137*fa3440faSWen Gong 138*fa3440faSWen Gong /* Caculate new pkt_offset */ 139*fa3440faSWen Gong if (old->pkt_offset < ETH_ALEN) 140*fa3440faSWen Gong new->pkt_offset = old->pkt_offset + 141*fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr1); 142*fa3440faSWen Gong else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) 143*fa3440faSWen Gong new->pkt_offset = old->pkt_offset + 144*fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr3) - 145*fa3440faSWen Gong offsetof(struct ethhdr, h_source); 146*fa3440faSWen Gong else 147*fa3440faSWen Gong new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; 148*fa3440faSWen Gong 149*fa3440faSWen Gong /* Caculate new hdr end offset */ 150*fa3440faSWen Gong if (total_len > ETH_HLEN) 151*fa3440faSWen Gong hdr_80211_end_offset = hdr_len + rfc_len; 152*fa3440faSWen Gong else if (total_len > offsetof(struct ethhdr, h_proto)) 153*fa3440faSWen Gong hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; 154*fa3440faSWen Gong else if (total_len > ETH_ALEN) 155*fa3440faSWen Gong hdr_80211_end_offset = total_len - ETH_ALEN + 156*fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr3); 157*fa3440faSWen Gong else 158*fa3440faSWen Gong hdr_80211_end_offset = total_len + 159*fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr1); 160*fa3440faSWen Gong 161*fa3440faSWen Gong new->pattern_len = hdr_80211_end_offset - new->pkt_offset; 162*fa3440faSWen Gong 163*fa3440faSWen Gong memcpy((u8 *)new->pattern, 164*fa3440faSWen Gong hdr_80211_pattern + new->pkt_offset, 165*fa3440faSWen Gong new->pattern_len); 166*fa3440faSWen Gong memcpy((u8 *)new->mask, 167*fa3440faSWen Gong hdr_80211_bit_mask + new->pkt_offset, 168*fa3440faSWen Gong new->pattern_len); 169*fa3440faSWen Gong 170*fa3440faSWen Gong if (total_len > ETH_HLEN) { 171*fa3440faSWen Gong /* Copy frame body */ 172*fa3440faSWen Gong memcpy((u8 *)new->pattern + new->pattern_len, 173*fa3440faSWen Gong (void *)old->pattern + ETH_HLEN - old->pkt_offset, 174*fa3440faSWen Gong total_len - ETH_HLEN); 175*fa3440faSWen Gong memcpy((u8 *)new->mask + new->pattern_len, 176*fa3440faSWen Gong (void *)old->mask + ETH_HLEN - old->pkt_offset, 177*fa3440faSWen Gong total_len - ETH_HLEN); 178*fa3440faSWen Gong 179*fa3440faSWen Gong new->pattern_len += total_len - ETH_HLEN; 180*fa3440faSWen Gong } 181*fa3440faSWen Gong } 182*fa3440faSWen Gong 1835fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, 1845fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 1855fd3ac3cSJanusz Dziedzic { 1865fd3ac3cSJanusz Dziedzic int ret, i; 1875fd3ac3cSJanusz Dziedzic unsigned long wow_mask = 0; 1885fd3ac3cSJanusz Dziedzic struct ath10k *ar = arvif->ar; 18925c86619SJanusz Dziedzic const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 19025c86619SJanusz Dziedzic int pattern_id = 0; 1915fd3ac3cSJanusz Dziedzic 1925fd3ac3cSJanusz Dziedzic /* Setup requested WOW features */ 1935fd3ac3cSJanusz Dziedzic switch (arvif->vdev_type) { 1945fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_IBSS: 1955fd3ac3cSJanusz Dziedzic __set_bit(WOW_BEACON_EVENT, &wow_mask); 1965fd3ac3cSJanusz Dziedzic /* fall through */ 1975fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_AP: 1985fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 1995fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 2005fd3ac3cSJanusz Dziedzic __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 2015fd3ac3cSJanusz Dziedzic __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 2025fd3ac3cSJanusz Dziedzic __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 2035fd3ac3cSJanusz Dziedzic __set_bit(WOW_HTT_EVENT, &wow_mask); 2045fd3ac3cSJanusz Dziedzic __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 2055fd3ac3cSJanusz Dziedzic break; 2065fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_STA: 2075fd3ac3cSJanusz Dziedzic if (wowlan->disconnect) { 2085fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 2095fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 2105fd3ac3cSJanusz Dziedzic __set_bit(WOW_BMISS_EVENT, &wow_mask); 2115fd3ac3cSJanusz Dziedzic __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 2125fd3ac3cSJanusz Dziedzic } 2135fd3ac3cSJanusz Dziedzic 2145fd3ac3cSJanusz Dziedzic if (wowlan->magic_pkt) 2155fd3ac3cSJanusz Dziedzic __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 2165fd3ac3cSJanusz Dziedzic break; 2175fd3ac3cSJanusz Dziedzic default: 2185fd3ac3cSJanusz Dziedzic break; 2195fd3ac3cSJanusz Dziedzic } 2205fd3ac3cSJanusz Dziedzic 22125c86619SJanusz Dziedzic for (i = 0; i < wowlan->n_patterns; i++) { 22225c86619SJanusz Dziedzic u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; 223*fa3440faSWen Gong u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; 224*fa3440faSWen Gong u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; 225*fa3440faSWen Gong struct cfg80211_pkt_pattern new_pattern = {}; 226*fa3440faSWen Gong struct cfg80211_pkt_pattern old_pattern = patterns[i]; 22725c86619SJanusz Dziedzic int j; 22825c86619SJanusz Dziedzic 229*fa3440faSWen Gong new_pattern.pattern = ath_pattern; 230*fa3440faSWen Gong new_pattern.mask = ath_bitmask; 23125c86619SJanusz Dziedzic if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) 23225c86619SJanusz Dziedzic continue; 23325c86619SJanusz Dziedzic /* convert bytemask to bitmask */ 23425c86619SJanusz Dziedzic for (j = 0; j < patterns[i].pattern_len; j++) 23525c86619SJanusz Dziedzic if (patterns[i].mask[j / 8] & BIT(j % 8)) 23625c86619SJanusz Dziedzic bitmask[j] = 0xff; 237*fa3440faSWen Gong old_pattern.mask = bitmask; 238*fa3440faSWen Gong new_pattern = old_pattern; 239*fa3440faSWen Gong 240*fa3440faSWen Gong if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 241*fa3440faSWen Gong if (patterns[i].pkt_offset < ETH_HLEN) 242*fa3440faSWen Gong ath10k_wow_convert_8023_to_80211(&new_pattern, 243*fa3440faSWen Gong &old_pattern); 244*fa3440faSWen Gong else 245*fa3440faSWen Gong new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; 246*fa3440faSWen Gong } 247*fa3440faSWen Gong 248*fa3440faSWen Gong if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) 249*fa3440faSWen Gong return -EINVAL; 25025c86619SJanusz Dziedzic 25125c86619SJanusz Dziedzic ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id, 25225c86619SJanusz Dziedzic pattern_id, 253*fa3440faSWen Gong new_pattern.pattern, 254*fa3440faSWen Gong new_pattern.mask, 255*fa3440faSWen Gong new_pattern.pattern_len, 256*fa3440faSWen Gong new_pattern.pkt_offset); 25725c86619SJanusz Dziedzic if (ret) { 25825c86619SJanusz Dziedzic ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n", 25925c86619SJanusz Dziedzic pattern_id, 26025c86619SJanusz Dziedzic arvif->vdev_id, ret); 26125c86619SJanusz Dziedzic return ret; 26225c86619SJanusz Dziedzic } 26325c86619SJanusz Dziedzic 26425c86619SJanusz Dziedzic pattern_id++; 26525c86619SJanusz Dziedzic __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); 26625c86619SJanusz Dziedzic } 26725c86619SJanusz Dziedzic 2685fd3ac3cSJanusz Dziedzic for (i = 0; i < WOW_EVENT_MAX; i++) { 2695fd3ac3cSJanusz Dziedzic if (!test_bit(i, &wow_mask)) 2705fd3ac3cSJanusz Dziedzic continue; 2715fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 2725fd3ac3cSJanusz Dziedzic if (ret) { 2735fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n", 2745fd3ac3cSJanusz Dziedzic wow_wakeup_event(i), arvif->vdev_id, ret); 2755fd3ac3cSJanusz Dziedzic return ret; 2765fd3ac3cSJanusz Dziedzic } 2775fd3ac3cSJanusz Dziedzic } 2785fd3ac3cSJanusz Dziedzic 2795fd3ac3cSJanusz Dziedzic return 0; 2805fd3ac3cSJanusz Dziedzic } 2815fd3ac3cSJanusz Dziedzic 2825fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar, 2835fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 2845fd3ac3cSJanusz Dziedzic { 2855fd3ac3cSJanusz Dziedzic struct ath10k_vif *arvif; 2865fd3ac3cSJanusz Dziedzic int ret; 2875fd3ac3cSJanusz Dziedzic 2885fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 2895fd3ac3cSJanusz Dziedzic 2905fd3ac3cSJanusz Dziedzic list_for_each_entry(arvif, &ar->arvifs, list) { 2915fd3ac3cSJanusz Dziedzic ret = ath10k_vif_wow_set_wakeups(arvif, wowlan); 2925fd3ac3cSJanusz Dziedzic if (ret) { 2935fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n", 2945fd3ac3cSJanusz Dziedzic arvif->vdev_id, ret); 2955fd3ac3cSJanusz Dziedzic return ret; 2965fd3ac3cSJanusz Dziedzic } 2975fd3ac3cSJanusz Dziedzic } 2985fd3ac3cSJanusz Dziedzic 2995fd3ac3cSJanusz Dziedzic return 0; 3005fd3ac3cSJanusz Dziedzic } 3015fd3ac3cSJanusz Dziedzic 3025fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar) 3035fd3ac3cSJanusz Dziedzic { 3045fd3ac3cSJanusz Dziedzic int ret; 3055fd3ac3cSJanusz Dziedzic 3065fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 3075fd3ac3cSJanusz Dziedzic 3085fd3ac3cSJanusz Dziedzic reinit_completion(&ar->target_suspend); 3095fd3ac3cSJanusz Dziedzic 3105fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_enable(ar); 3115fd3ac3cSJanusz Dziedzic if (ret) { 3125fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to issue wow enable: %d\n", ret); 3135fd3ac3cSJanusz Dziedzic return ret; 3145fd3ac3cSJanusz Dziedzic } 3155fd3ac3cSJanusz Dziedzic 3165fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ); 3175fd3ac3cSJanusz Dziedzic if (ret == 0) { 3185fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for suspend completion\n"); 3195fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 3205fd3ac3cSJanusz Dziedzic } 3215fd3ac3cSJanusz Dziedzic 3225fd3ac3cSJanusz Dziedzic return 0; 3235fd3ac3cSJanusz Dziedzic } 3245fd3ac3cSJanusz Dziedzic 3255fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar) 3265fd3ac3cSJanusz Dziedzic { 3275fd3ac3cSJanusz Dziedzic int ret; 3285fd3ac3cSJanusz Dziedzic 3295fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 3305fd3ac3cSJanusz Dziedzic 3315fd3ac3cSJanusz Dziedzic reinit_completion(&ar->wow.wakeup_completed); 3325fd3ac3cSJanusz Dziedzic 3335fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_host_wakeup_ind(ar); 3345fd3ac3cSJanusz Dziedzic if (ret) { 3355fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to send wow wakeup indication: %d\n", 3365fd3ac3cSJanusz Dziedzic ret); 3375fd3ac3cSJanusz Dziedzic return ret; 3385fd3ac3cSJanusz Dziedzic } 3395fd3ac3cSJanusz Dziedzic 3405fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ); 3415fd3ac3cSJanusz Dziedzic if (ret == 0) { 3425fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n"); 3435fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 3445fd3ac3cSJanusz Dziedzic } 3455fd3ac3cSJanusz Dziedzic 3465fd3ac3cSJanusz Dziedzic return 0; 3475fd3ac3cSJanusz Dziedzic } 3485fd3ac3cSJanusz Dziedzic 3495fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw, 3505fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 3515fd3ac3cSJanusz Dziedzic { 3525fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 3535fd3ac3cSJanusz Dziedzic int ret; 3545fd3ac3cSJanusz Dziedzic 3555fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 3565fd3ac3cSJanusz Dziedzic 3575fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 358c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features))) { 3595fd3ac3cSJanusz Dziedzic ret = 1; 3605fd3ac3cSJanusz Dziedzic goto exit; 3615fd3ac3cSJanusz Dziedzic } 3625fd3ac3cSJanusz Dziedzic 3635fd3ac3cSJanusz Dziedzic ret = ath10k_wow_cleanup(ar); 3645fd3ac3cSJanusz Dziedzic if (ret) { 3655fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", 3665fd3ac3cSJanusz Dziedzic ret); 3675fd3ac3cSJanusz Dziedzic goto exit; 3685fd3ac3cSJanusz Dziedzic } 3695fd3ac3cSJanusz Dziedzic 3705fd3ac3cSJanusz Dziedzic ret = ath10k_wow_set_wakeups(ar, wowlan); 3715fd3ac3cSJanusz Dziedzic if (ret) { 3725fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeup events: %d\n", 3735fd3ac3cSJanusz Dziedzic ret); 3745fd3ac3cSJanusz Dziedzic goto cleanup; 3755fd3ac3cSJanusz Dziedzic } 3765fd3ac3cSJanusz Dziedzic 3775fd3ac3cSJanusz Dziedzic ret = ath10k_wow_enable(ar); 3785fd3ac3cSJanusz Dziedzic if (ret) { 3795fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to start wow: %d\n", ret); 3805fd3ac3cSJanusz Dziedzic goto cleanup; 3815fd3ac3cSJanusz Dziedzic } 3825fd3ac3cSJanusz Dziedzic 3835fd3ac3cSJanusz Dziedzic ret = ath10k_hif_suspend(ar); 3845fd3ac3cSJanusz Dziedzic if (ret) { 3855fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to suspend hif: %d\n", ret); 3865fd3ac3cSJanusz Dziedzic goto wakeup; 3875fd3ac3cSJanusz Dziedzic } 3885fd3ac3cSJanusz Dziedzic 3895fd3ac3cSJanusz Dziedzic goto exit; 3905fd3ac3cSJanusz Dziedzic 3915fd3ac3cSJanusz Dziedzic wakeup: 3925fd3ac3cSJanusz Dziedzic ath10k_wow_wakeup(ar); 3935fd3ac3cSJanusz Dziedzic 3945fd3ac3cSJanusz Dziedzic cleanup: 3955fd3ac3cSJanusz Dziedzic ath10k_wow_cleanup(ar); 3965fd3ac3cSJanusz Dziedzic 3975fd3ac3cSJanusz Dziedzic exit: 3985fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 3995fd3ac3cSJanusz Dziedzic return ret ? 1 : 0; 4005fd3ac3cSJanusz Dziedzic } 4015fd3ac3cSJanusz Dziedzic 402393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) 403393b706cSRyan Hsu { 404393b706cSRyan Hsu struct ath10k *ar = hw->priv; 405393b706cSRyan Hsu 406393b706cSRyan Hsu mutex_lock(&ar->conf_mutex); 407393b706cSRyan Hsu if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 408393b706cSRyan Hsu ar->running_fw->fw_file.fw_features)) { 409393b706cSRyan Hsu device_set_wakeup_enable(ar->dev, enabled); 410393b706cSRyan Hsu } 411393b706cSRyan Hsu mutex_unlock(&ar->conf_mutex); 412393b706cSRyan Hsu } 413393b706cSRyan Hsu 4145fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw) 4155fd3ac3cSJanusz Dziedzic { 4165fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 4175fd3ac3cSJanusz Dziedzic int ret; 4185fd3ac3cSJanusz Dziedzic 4195fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 4205fd3ac3cSJanusz Dziedzic 4215fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 422c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features))) { 4235fd3ac3cSJanusz Dziedzic ret = 1; 4245fd3ac3cSJanusz Dziedzic goto exit; 4255fd3ac3cSJanusz Dziedzic } 4265fd3ac3cSJanusz Dziedzic 4275fd3ac3cSJanusz Dziedzic ret = ath10k_hif_resume(ar); 4285fd3ac3cSJanusz Dziedzic if (ret) { 4295fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to resume hif: %d\n", ret); 4305fd3ac3cSJanusz Dziedzic goto exit; 4315fd3ac3cSJanusz Dziedzic } 4325fd3ac3cSJanusz Dziedzic 4335fd3ac3cSJanusz Dziedzic ret = ath10k_wow_wakeup(ar); 4345fd3ac3cSJanusz Dziedzic if (ret) 4355fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); 4365fd3ac3cSJanusz Dziedzic 4375fd3ac3cSJanusz Dziedzic exit: 4386f7429c2SMichal Kazior if (ret) { 4396f7429c2SMichal Kazior switch (ar->state) { 4406f7429c2SMichal Kazior case ATH10K_STATE_ON: 4416f7429c2SMichal Kazior ar->state = ATH10K_STATE_RESTARTING; 4426f7429c2SMichal Kazior ret = 1; 4436f7429c2SMichal Kazior break; 4446f7429c2SMichal Kazior case ATH10K_STATE_OFF: 4456f7429c2SMichal Kazior case ATH10K_STATE_RESTARTING: 4466f7429c2SMichal Kazior case ATH10K_STATE_RESTARTED: 4476f7429c2SMichal Kazior case ATH10K_STATE_UTF: 4486f7429c2SMichal Kazior case ATH10K_STATE_WEDGED: 4496f7429c2SMichal Kazior ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n", 4506f7429c2SMichal Kazior ar->state); 4516f7429c2SMichal Kazior ret = -EIO; 4526f7429c2SMichal Kazior break; 4536f7429c2SMichal Kazior } 4546f7429c2SMichal Kazior } 4556f7429c2SMichal Kazior 4565fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 4576f7429c2SMichal Kazior return ret; 4585fd3ac3cSJanusz Dziedzic } 4595fd3ac3cSJanusz Dziedzic 4605fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar) 4615fd3ac3cSJanusz Dziedzic { 462c4cdf753SKalle Valo if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 463c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features)) 4645fd3ac3cSJanusz Dziedzic return 0; 4655fd3ac3cSJanusz Dziedzic 4665fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map))) 4675fd3ac3cSJanusz Dziedzic return -EINVAL; 4685fd3ac3cSJanusz Dziedzic 46925c86619SJanusz Dziedzic ar->wow.wowlan_support = ath10k_wowlan_support; 470*fa3440faSWen Gong 471*fa3440faSWen Gong if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 472*fa3440faSWen Gong ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; 473*fa3440faSWen Gong ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; 474*fa3440faSWen Gong } 475*fa3440faSWen Gong 47625c86619SJanusz Dziedzic ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; 47725c86619SJanusz Dziedzic ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; 4785fd3ac3cSJanusz Dziedzic 479393b706cSRyan Hsu device_set_wakeup_capable(ar->dev, true); 480393b706cSRyan Hsu 4815fd3ac3cSJanusz Dziedzic return 0; 4825fd3ac3cSJanusz Dziedzic } 483