179802b13SCarl Huang // SPDX-License-Identifier: BSD-3-Clause-Clear 279802b13SCarl Huang /* 379802b13SCarl Huang * Copyright (c) 2020 The Linux Foundation. All rights reserved. 479802b13SCarl Huang */ 579802b13SCarl Huang 679802b13SCarl Huang #include <linux/delay.h> 779802b13SCarl Huang 879802b13SCarl Huang #include "mac.h" 9ba9177fcSCarl Huang 10ba9177fcSCarl Huang #include <net/mac80211.h> 1179802b13SCarl Huang #include "core.h" 1279802b13SCarl Huang #include "hif.h" 1379802b13SCarl Huang #include "debug.h" 1479802b13SCarl Huang #include "wmi.h" 1579802b13SCarl Huang #include "wow.h" 1690bf5c8dSCarl Huang #include "dp_rx.h" 1779802b13SCarl Huang 18ba9177fcSCarl Huang static const struct wiphy_wowlan_support ath11k_wowlan_support = { 19ba9177fcSCarl Huang .flags = WIPHY_WOWLAN_DISCONNECT | 20a16d9b50SCarl Huang WIPHY_WOWLAN_MAGIC_PKT | 21a16d9b50SCarl Huang WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | 22a16d9b50SCarl Huang WIPHY_WOWLAN_GTK_REKEY_FAILURE, 23ba9177fcSCarl Huang .pattern_min_len = WOW_MIN_PATTERN_SIZE, 24ba9177fcSCarl Huang .pattern_max_len = WOW_MAX_PATTERN_SIZE, 25ba9177fcSCarl Huang .max_pkt_offset = WOW_MAX_PKT_OFFSET, 26ba9177fcSCarl Huang }; 27ba9177fcSCarl Huang 2879802b13SCarl Huang int ath11k_wow_enable(struct ath11k_base *ab) 2979802b13SCarl Huang { 3079802b13SCarl Huang struct ath11k *ar = ath11k_ab_to_ar(ab, 0); 3179802b13SCarl Huang int i, ret; 3279802b13SCarl Huang 3379802b13SCarl Huang clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags); 3479802b13SCarl Huang 3579802b13SCarl Huang for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) { 3679802b13SCarl Huang reinit_completion(&ab->htc_suspend); 3779802b13SCarl Huang 3879802b13SCarl Huang ret = ath11k_wmi_wow_enable(ar); 3979802b13SCarl Huang if (ret) { 4079802b13SCarl Huang ath11k_warn(ab, "failed to issue wow enable: %d\n", ret); 4179802b13SCarl Huang return ret; 4279802b13SCarl Huang } 4379802b13SCarl Huang 4479802b13SCarl Huang ret = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ); 4579802b13SCarl Huang if (ret == 0) { 4679802b13SCarl Huang ath11k_warn(ab, 4779802b13SCarl Huang "timed out while waiting for htc suspend completion\n"); 4879802b13SCarl Huang return -ETIMEDOUT; 4979802b13SCarl Huang } 5079802b13SCarl Huang 5179802b13SCarl Huang if (test_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags)) 5279802b13SCarl Huang /* success, suspend complete received */ 5379802b13SCarl Huang return 0; 5479802b13SCarl Huang 5579802b13SCarl Huang ath11k_warn(ab, "htc suspend not complete, retrying (try %d)\n", 5679802b13SCarl Huang i); 5779802b13SCarl Huang msleep(ATH11K_WOW_RETRY_WAIT_MS); 5879802b13SCarl Huang } 5979802b13SCarl Huang 6079802b13SCarl Huang ath11k_warn(ab, "htc suspend not complete, failing after %d tries\n", i); 6179802b13SCarl Huang 6279802b13SCarl Huang return -ETIMEDOUT; 6379802b13SCarl Huang } 6479802b13SCarl Huang 6579802b13SCarl Huang int ath11k_wow_wakeup(struct ath11k_base *ab) 6679802b13SCarl Huang { 6779802b13SCarl Huang struct ath11k *ar = ath11k_ab_to_ar(ab, 0); 6879802b13SCarl Huang int ret; 6979802b13SCarl Huang 7079802b13SCarl Huang reinit_completion(&ab->wow.wakeup_completed); 7179802b13SCarl Huang 7279802b13SCarl Huang ret = ath11k_wmi_wow_host_wakeup_ind(ar); 7379802b13SCarl Huang if (ret) { 7479802b13SCarl Huang ath11k_warn(ab, "failed to send wow wakeup indication: %d\n", 7579802b13SCarl Huang ret); 7679802b13SCarl Huang return ret; 7779802b13SCarl Huang } 7879802b13SCarl Huang 7979802b13SCarl Huang ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ); 8079802b13SCarl Huang if (ret == 0) { 8179802b13SCarl Huang ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n"); 8279802b13SCarl Huang return -ETIMEDOUT; 8379802b13SCarl Huang } 8479802b13SCarl Huang 8579802b13SCarl Huang return 0; 8679802b13SCarl Huang } 87ba9177fcSCarl Huang 88ba9177fcSCarl Huang static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif) 89ba9177fcSCarl Huang { 90ba9177fcSCarl Huang struct ath11k *ar = arvif->ar; 91ba9177fcSCarl Huang int i, ret; 92ba9177fcSCarl Huang 93ba9177fcSCarl Huang for (i = 0; i < WOW_EVENT_MAX; i++) { 94ba9177fcSCarl Huang ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); 95ba9177fcSCarl Huang if (ret) { 96ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n", 97ba9177fcSCarl Huang wow_wakeup_event(i), arvif->vdev_id, ret); 98ba9177fcSCarl Huang return ret; 99ba9177fcSCarl Huang } 100ba9177fcSCarl Huang } 101ba9177fcSCarl Huang 102ba9177fcSCarl Huang for (i = 0; i < ar->wow.max_num_patterns; i++) { 103ba9177fcSCarl Huang ret = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); 104ba9177fcSCarl Huang if (ret) { 105ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n", 106ba9177fcSCarl Huang i, arvif->vdev_id, ret); 107ba9177fcSCarl Huang return ret; 108ba9177fcSCarl Huang } 109ba9177fcSCarl Huang } 110ba9177fcSCarl Huang 111ba9177fcSCarl Huang return 0; 112ba9177fcSCarl Huang } 113ba9177fcSCarl Huang 114ba9177fcSCarl Huang static int ath11k_wow_cleanup(struct ath11k *ar) 115ba9177fcSCarl Huang { 116ba9177fcSCarl Huang struct ath11k_vif *arvif; 117ba9177fcSCarl Huang int ret; 118ba9177fcSCarl Huang 119ba9177fcSCarl Huang lockdep_assert_held(&ar->conf_mutex); 120ba9177fcSCarl Huang 121ba9177fcSCarl Huang list_for_each_entry(arvif, &ar->arvifs, list) { 122ba9177fcSCarl Huang ret = ath11k_wow_vif_cleanup(arvif); 123ba9177fcSCarl Huang if (ret) { 124ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n", 125ba9177fcSCarl Huang arvif->vdev_id, ret); 126ba9177fcSCarl Huang return ret; 127ba9177fcSCarl Huang } 128ba9177fcSCarl Huang } 129ba9177fcSCarl Huang 130ba9177fcSCarl Huang return 0; 131ba9177fcSCarl Huang } 132ba9177fcSCarl Huang 133ba9177fcSCarl Huang /* Convert a 802.3 format to a 802.11 format. 134ba9177fcSCarl Huang * +------------+-----------+--------+----------------+ 135ba9177fcSCarl Huang * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | 136ba9177fcSCarl Huang * +------------+-----------+--------+----------------+ 137ba9177fcSCarl Huang * |__ |_______ |____________ |________ 138ba9177fcSCarl Huang * | | | | 139ba9177fcSCarl Huang * +--+------------+----+-----------+---------------+-----------+ 140ba9177fcSCarl Huang * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | 141ba9177fcSCarl Huang * +--+------------+----+-----------+---------------+-----------+ 142ba9177fcSCarl Huang */ 143ba9177fcSCarl Huang static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, 144ba9177fcSCarl Huang const struct cfg80211_pkt_pattern *old) 145ba9177fcSCarl Huang { 146ba9177fcSCarl Huang u8 hdr_8023_pattern[ETH_HLEN] = {}; 147ba9177fcSCarl Huang u8 hdr_8023_bit_mask[ETH_HLEN] = {}; 148ba9177fcSCarl Huang u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; 149ba9177fcSCarl Huang u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; 150ba9177fcSCarl Huang 151ba9177fcSCarl Huang int total_len = old->pkt_offset + old->pattern_len; 152ba9177fcSCarl Huang int hdr_80211_end_offset; 153ba9177fcSCarl Huang 154ba9177fcSCarl Huang struct ieee80211_hdr_3addr *new_hdr_pattern = 155ba9177fcSCarl Huang (struct ieee80211_hdr_3addr *)hdr_80211_pattern; 156ba9177fcSCarl Huang struct ieee80211_hdr_3addr *new_hdr_mask = 157ba9177fcSCarl Huang (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; 158ba9177fcSCarl Huang struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; 159ba9177fcSCarl Huang struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; 160ba9177fcSCarl Huang int hdr_len = sizeof(*new_hdr_pattern); 161ba9177fcSCarl Huang 162ba9177fcSCarl Huang struct rfc1042_hdr *new_rfc_pattern = 163ba9177fcSCarl Huang (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); 164ba9177fcSCarl Huang struct rfc1042_hdr *new_rfc_mask = 165ba9177fcSCarl Huang (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); 166ba9177fcSCarl Huang int rfc_len = sizeof(*new_rfc_pattern); 167ba9177fcSCarl Huang 168ba9177fcSCarl Huang memcpy(hdr_8023_pattern + old->pkt_offset, 169ba9177fcSCarl Huang old->pattern, ETH_HLEN - old->pkt_offset); 170ba9177fcSCarl Huang memcpy(hdr_8023_bit_mask + old->pkt_offset, 171ba9177fcSCarl Huang old->mask, ETH_HLEN - old->pkt_offset); 172ba9177fcSCarl Huang 173ba9177fcSCarl Huang /* Copy destination address */ 174ba9177fcSCarl Huang memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); 175ba9177fcSCarl Huang memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); 176ba9177fcSCarl Huang 177ba9177fcSCarl Huang /* Copy source address */ 178ba9177fcSCarl Huang memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); 179ba9177fcSCarl Huang memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); 180ba9177fcSCarl Huang 181ba9177fcSCarl Huang /* Copy logic link type */ 182ba9177fcSCarl Huang memcpy(&new_rfc_pattern->snap_type, 183ba9177fcSCarl Huang &old_hdr_pattern->h_proto, 184ba9177fcSCarl Huang sizeof(old_hdr_pattern->h_proto)); 185ba9177fcSCarl Huang memcpy(&new_rfc_mask->snap_type, 186ba9177fcSCarl Huang &old_hdr_mask->h_proto, 187ba9177fcSCarl Huang sizeof(old_hdr_mask->h_proto)); 188ba9177fcSCarl Huang 189ba9177fcSCarl Huang /* Compute new pkt_offset */ 190ba9177fcSCarl Huang if (old->pkt_offset < ETH_ALEN) 191ba9177fcSCarl Huang new->pkt_offset = old->pkt_offset + 192ba9177fcSCarl Huang offsetof(struct ieee80211_hdr_3addr, addr1); 193ba9177fcSCarl Huang else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) 194ba9177fcSCarl Huang new->pkt_offset = old->pkt_offset + 195ba9177fcSCarl Huang offsetof(struct ieee80211_hdr_3addr, addr3) - 196ba9177fcSCarl Huang offsetof(struct ethhdr, h_source); 197ba9177fcSCarl Huang else 198ba9177fcSCarl Huang new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; 199ba9177fcSCarl Huang 200ba9177fcSCarl Huang /* Compute new hdr end offset */ 201ba9177fcSCarl Huang if (total_len > ETH_HLEN) 202ba9177fcSCarl Huang hdr_80211_end_offset = hdr_len + rfc_len; 203ba9177fcSCarl Huang else if (total_len > offsetof(struct ethhdr, h_proto)) 204ba9177fcSCarl Huang hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; 205ba9177fcSCarl Huang else if (total_len > ETH_ALEN) 206ba9177fcSCarl Huang hdr_80211_end_offset = total_len - ETH_ALEN + 207ba9177fcSCarl Huang offsetof(struct ieee80211_hdr_3addr, addr3); 208ba9177fcSCarl Huang else 209ba9177fcSCarl Huang hdr_80211_end_offset = total_len + 210ba9177fcSCarl Huang offsetof(struct ieee80211_hdr_3addr, addr1); 211ba9177fcSCarl Huang 212ba9177fcSCarl Huang new->pattern_len = hdr_80211_end_offset - new->pkt_offset; 213ba9177fcSCarl Huang 214ba9177fcSCarl Huang memcpy((u8 *)new->pattern, 215ba9177fcSCarl Huang hdr_80211_pattern + new->pkt_offset, 216ba9177fcSCarl Huang new->pattern_len); 217ba9177fcSCarl Huang memcpy((u8 *)new->mask, 218ba9177fcSCarl Huang hdr_80211_bit_mask + new->pkt_offset, 219ba9177fcSCarl Huang new->pattern_len); 220ba9177fcSCarl Huang 221ba9177fcSCarl Huang if (total_len > ETH_HLEN) { 222ba9177fcSCarl Huang /* Copy frame body */ 223ba9177fcSCarl Huang memcpy((u8 *)new->pattern + new->pattern_len, 224ba9177fcSCarl Huang (void *)old->pattern + ETH_HLEN - old->pkt_offset, 225ba9177fcSCarl Huang total_len - ETH_HLEN); 226ba9177fcSCarl Huang memcpy((u8 *)new->mask + new->pattern_len, 227ba9177fcSCarl Huang (void *)old->mask + ETH_HLEN - old->pkt_offset, 228ba9177fcSCarl Huang total_len - ETH_HLEN); 229ba9177fcSCarl Huang 230ba9177fcSCarl Huang new->pattern_len += total_len - ETH_HLEN; 231ba9177fcSCarl Huang } 232ba9177fcSCarl Huang } 233ba9177fcSCarl Huang 234fec4b898SCarl Huang static int ath11k_wmi_pno_check_and_convert(struct ath11k *ar, u32 vdev_id, 235fec4b898SCarl Huang struct cfg80211_sched_scan_request *nd_config, 236fec4b898SCarl Huang struct wmi_pno_scan_req *pno) 237fec4b898SCarl Huang { 238fec4b898SCarl Huang int i, j; 239fec4b898SCarl Huang u8 ssid_len; 240fec4b898SCarl Huang 241fec4b898SCarl Huang pno->enable = 1; 242fec4b898SCarl Huang pno->vdev_id = vdev_id; 243fec4b898SCarl Huang pno->uc_networks_count = nd_config->n_match_sets; 244fec4b898SCarl Huang 245fec4b898SCarl Huang if (!pno->uc_networks_count || 246fec4b898SCarl Huang pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) 247fec4b898SCarl Huang return -EINVAL; 248fec4b898SCarl Huang 249fec4b898SCarl Huang if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) 250fec4b898SCarl Huang return -EINVAL; 251fec4b898SCarl Huang 252fec4b898SCarl Huang /* Filling per profile params */ 253fec4b898SCarl Huang for (i = 0; i < pno->uc_networks_count; i++) { 254fec4b898SCarl Huang ssid_len = nd_config->match_sets[i].ssid.ssid_len; 255fec4b898SCarl Huang 256fec4b898SCarl Huang if (ssid_len == 0 || ssid_len > 32) 257fec4b898SCarl Huang return -EINVAL; 258fec4b898SCarl Huang 259fec4b898SCarl Huang pno->a_networks[i].ssid.ssid_len = ssid_len; 260fec4b898SCarl Huang 261fec4b898SCarl Huang memcpy(pno->a_networks[i].ssid.ssid, 262fec4b898SCarl Huang nd_config->match_sets[i].ssid.ssid, 263fec4b898SCarl Huang nd_config->match_sets[i].ssid.ssid_len); 264fec4b898SCarl Huang pno->a_networks[i].authentication = 0; 265fec4b898SCarl Huang pno->a_networks[i].encryption = 0; 266fec4b898SCarl Huang pno->a_networks[i].bcast_nw_type = 0; 267fec4b898SCarl Huang 268fec4b898SCarl Huang /* Copying list of valid channel into request */ 269fec4b898SCarl Huang pno->a_networks[i].channel_count = nd_config->n_channels; 270fec4b898SCarl Huang pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; 271fec4b898SCarl Huang 272fec4b898SCarl Huang for (j = 0; j < nd_config->n_channels; j++) { 273fec4b898SCarl Huang pno->a_networks[i].channels[j] = 274fec4b898SCarl Huang nd_config->channels[j]->center_freq; 275fec4b898SCarl Huang } 276fec4b898SCarl Huang } 277fec4b898SCarl Huang 278fec4b898SCarl Huang /* set scan to passive if no SSIDs are specified in the request */ 279fec4b898SCarl Huang if (nd_config->n_ssids == 0) 280fec4b898SCarl Huang pno->do_passive_scan = true; 281fec4b898SCarl Huang else 282fec4b898SCarl Huang pno->do_passive_scan = false; 283fec4b898SCarl Huang 284fec4b898SCarl Huang for (i = 0; i < nd_config->n_ssids; i++) { 285fec4b898SCarl Huang j = 0; 286fec4b898SCarl Huang while (j < pno->uc_networks_count) { 287fec4b898SCarl Huang if (pno->a_networks[j].ssid.ssid_len == 288fec4b898SCarl Huang nd_config->ssids[i].ssid_len && 289fec4b898SCarl Huang (memcmp(pno->a_networks[j].ssid.ssid, 290fec4b898SCarl Huang nd_config->ssids[i].ssid, 291fec4b898SCarl Huang pno->a_networks[j].ssid.ssid_len) == 0)) { 292fec4b898SCarl Huang pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; 293fec4b898SCarl Huang break; 294fec4b898SCarl Huang } 295fec4b898SCarl Huang j++; 296fec4b898SCarl Huang } 297fec4b898SCarl Huang } 298fec4b898SCarl Huang 299fec4b898SCarl Huang if (nd_config->n_scan_plans == 2) { 300fec4b898SCarl Huang pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 301fec4b898SCarl Huang pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; 302fec4b898SCarl Huang pno->slow_scan_period = 303fec4b898SCarl Huang nd_config->scan_plans[1].interval * MSEC_PER_SEC; 304fec4b898SCarl Huang } else if (nd_config->n_scan_plans == 1) { 305fec4b898SCarl Huang pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 306fec4b898SCarl Huang pno->fast_scan_max_cycles = 1; 307fec4b898SCarl Huang pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 308fec4b898SCarl Huang } else { 309fec4b898SCarl Huang ath11k_warn(ar->ab, "Invalid number of scan plans %d !!", 310fec4b898SCarl Huang nd_config->n_scan_plans); 311fec4b898SCarl Huang } 312fec4b898SCarl Huang 313fec4b898SCarl Huang if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 314fec4b898SCarl Huang /* enable mac randomization */ 315fec4b898SCarl Huang pno->enable_pno_scan_randomization = 1; 316fec4b898SCarl Huang memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); 317fec4b898SCarl Huang memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); 318fec4b898SCarl Huang } 319fec4b898SCarl Huang 320fec4b898SCarl Huang pno->delay_start_time = nd_config->delay; 321fec4b898SCarl Huang 322fec4b898SCarl Huang /* Current FW does not support min-max range for dwell time */ 323fec4b898SCarl Huang pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; 324fec4b898SCarl Huang pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; 325fec4b898SCarl Huang 326fec4b898SCarl Huang return 0; 327fec4b898SCarl Huang } 328fec4b898SCarl Huang 329ba9177fcSCarl Huang static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif, 330ba9177fcSCarl Huang struct cfg80211_wowlan *wowlan) 331ba9177fcSCarl Huang { 332ba9177fcSCarl Huang int ret, i; 333ba9177fcSCarl Huang unsigned long wow_mask = 0; 334ba9177fcSCarl Huang struct ath11k *ar = arvif->ar; 335ba9177fcSCarl Huang const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 336ba9177fcSCarl Huang int pattern_id = 0; 337ba9177fcSCarl Huang 338ba9177fcSCarl Huang /* Setup requested WOW features */ 339ba9177fcSCarl Huang switch (arvif->vdev_type) { 340ba9177fcSCarl Huang case WMI_VDEV_TYPE_IBSS: 341ba9177fcSCarl Huang __set_bit(WOW_BEACON_EVENT, &wow_mask); 342ba9177fcSCarl Huang fallthrough; 343ba9177fcSCarl Huang case WMI_VDEV_TYPE_AP: 344ba9177fcSCarl Huang __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 345ba9177fcSCarl Huang __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 346ba9177fcSCarl Huang __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 347ba9177fcSCarl Huang __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 348ba9177fcSCarl Huang __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 349ba9177fcSCarl Huang __set_bit(WOW_HTT_EVENT, &wow_mask); 350ba9177fcSCarl Huang __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 351ba9177fcSCarl Huang break; 352ba9177fcSCarl Huang case WMI_VDEV_TYPE_STA: 353ba9177fcSCarl Huang if (wowlan->disconnect) { 354ba9177fcSCarl Huang __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 355ba9177fcSCarl Huang __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 356ba9177fcSCarl Huang __set_bit(WOW_BMISS_EVENT, &wow_mask); 357ba9177fcSCarl Huang __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 358ba9177fcSCarl Huang } 359ba9177fcSCarl Huang 360ba9177fcSCarl Huang if (wowlan->magic_pkt) 361ba9177fcSCarl Huang __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 362fec4b898SCarl Huang 363fec4b898SCarl Huang if (wowlan->nd_config) { 364fec4b898SCarl Huang struct wmi_pno_scan_req *pno; 365fec4b898SCarl Huang int ret; 366fec4b898SCarl Huang 367fec4b898SCarl Huang pno = kzalloc(sizeof(*pno), GFP_KERNEL); 368fec4b898SCarl Huang if (!pno) 369fec4b898SCarl Huang return -ENOMEM; 370fec4b898SCarl Huang 371fec4b898SCarl Huang ar->nlo_enabled = true; 372fec4b898SCarl Huang 373fec4b898SCarl Huang ret = ath11k_wmi_pno_check_and_convert(ar, arvif->vdev_id, 374fec4b898SCarl Huang wowlan->nd_config, pno); 375fec4b898SCarl Huang if (!ret) { 376fec4b898SCarl Huang ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 377fec4b898SCarl Huang __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); 378fec4b898SCarl Huang } 379fec4b898SCarl Huang 380fec4b898SCarl Huang kfree(pno); 381fec4b898SCarl Huang } 382ba9177fcSCarl Huang break; 383ba9177fcSCarl Huang default: 384ba9177fcSCarl Huang break; 385ba9177fcSCarl Huang } 386ba9177fcSCarl Huang 387ba9177fcSCarl Huang for (i = 0; i < wowlan->n_patterns; i++) { 388ba9177fcSCarl Huang u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; 389ba9177fcSCarl Huang u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; 390ba9177fcSCarl Huang u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; 391ba9177fcSCarl Huang struct cfg80211_pkt_pattern new_pattern = {}; 392ba9177fcSCarl Huang struct cfg80211_pkt_pattern old_pattern = patterns[i]; 393ba9177fcSCarl Huang int j; 394ba9177fcSCarl Huang 395ba9177fcSCarl Huang new_pattern.pattern = ath_pattern; 396ba9177fcSCarl Huang new_pattern.mask = ath_bitmask; 397ba9177fcSCarl Huang if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) 398ba9177fcSCarl Huang continue; 399ba9177fcSCarl Huang /* convert bytemask to bitmask */ 400ba9177fcSCarl Huang for (j = 0; j < patterns[i].pattern_len; j++) 401ba9177fcSCarl Huang if (patterns[i].mask[j / 8] & BIT(j % 8)) 402ba9177fcSCarl Huang bitmask[j] = 0xff; 403ba9177fcSCarl Huang old_pattern.mask = bitmask; 404ba9177fcSCarl Huang 405ba9177fcSCarl Huang if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode == 406ba9177fcSCarl Huang ATH11K_HW_TXRX_NATIVE_WIFI) { 407ba9177fcSCarl Huang if (patterns[i].pkt_offset < ETH_HLEN) { 408ba9177fcSCarl Huang u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {}; 409ba9177fcSCarl Huang 410ba9177fcSCarl Huang memcpy(pattern_ext, old_pattern.pattern, 411ba9177fcSCarl Huang old_pattern.pattern_len); 412ba9177fcSCarl Huang old_pattern.pattern = pattern_ext; 413ba9177fcSCarl Huang ath11k_wow_convert_8023_to_80211(&new_pattern, 414ba9177fcSCarl Huang &old_pattern); 415ba9177fcSCarl Huang } else { 416ba9177fcSCarl Huang new_pattern = old_pattern; 417ba9177fcSCarl Huang new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; 418ba9177fcSCarl Huang } 419ba9177fcSCarl Huang } 420ba9177fcSCarl Huang 421ba9177fcSCarl Huang if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) 422ba9177fcSCarl Huang return -EINVAL; 423ba9177fcSCarl Huang 424ba9177fcSCarl Huang ret = ath11k_wmi_wow_add_pattern(ar, arvif->vdev_id, 425ba9177fcSCarl Huang pattern_id, 426ba9177fcSCarl Huang new_pattern.pattern, 427ba9177fcSCarl Huang new_pattern.mask, 428ba9177fcSCarl Huang new_pattern.pattern_len, 429ba9177fcSCarl Huang new_pattern.pkt_offset); 430ba9177fcSCarl Huang if (ret) { 431ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n", 432ba9177fcSCarl Huang pattern_id, 433ba9177fcSCarl Huang arvif->vdev_id, ret); 434ba9177fcSCarl Huang return ret; 435ba9177fcSCarl Huang } 436ba9177fcSCarl Huang 437ba9177fcSCarl Huang pattern_id++; 438ba9177fcSCarl Huang __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); 439ba9177fcSCarl Huang } 440ba9177fcSCarl Huang 441ba9177fcSCarl Huang for (i = 0; i < WOW_EVENT_MAX; i++) { 442ba9177fcSCarl Huang if (!test_bit(i, &wow_mask)) 443ba9177fcSCarl Huang continue; 444ba9177fcSCarl Huang ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 445ba9177fcSCarl Huang if (ret) { 446ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n", 447ba9177fcSCarl Huang wow_wakeup_event(i), arvif->vdev_id, ret); 448ba9177fcSCarl Huang return ret; 449ba9177fcSCarl Huang } 450ba9177fcSCarl Huang } 451ba9177fcSCarl Huang 452ba9177fcSCarl Huang return 0; 453ba9177fcSCarl Huang } 454ba9177fcSCarl Huang 455ba9177fcSCarl Huang static int ath11k_wow_set_wakeups(struct ath11k *ar, 456ba9177fcSCarl Huang struct cfg80211_wowlan *wowlan) 457ba9177fcSCarl Huang { 458ba9177fcSCarl Huang struct ath11k_vif *arvif; 459ba9177fcSCarl Huang int ret; 460ba9177fcSCarl Huang 461ba9177fcSCarl Huang lockdep_assert_held(&ar->conf_mutex); 462ba9177fcSCarl Huang 463ba9177fcSCarl Huang list_for_each_entry(arvif, &ar->arvifs, list) { 464ba9177fcSCarl Huang ret = ath11k_vif_wow_set_wakeups(arvif, wowlan); 465ba9177fcSCarl Huang if (ret) { 466ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n", 467ba9177fcSCarl Huang arvif->vdev_id, ret); 468ba9177fcSCarl Huang return ret; 469ba9177fcSCarl Huang } 470ba9177fcSCarl Huang } 471ba9177fcSCarl Huang 472ba9177fcSCarl Huang return 0; 473ba9177fcSCarl Huang } 474ba9177fcSCarl Huang 475fec4b898SCarl Huang static int ath11k_vif_wow_clean_nlo(struct ath11k_vif *arvif) 476fec4b898SCarl Huang { 477fec4b898SCarl Huang int ret = 0; 478fec4b898SCarl Huang struct ath11k *ar = arvif->ar; 479fec4b898SCarl Huang 480fec4b898SCarl Huang switch (arvif->vdev_type) { 481fec4b898SCarl Huang case WMI_VDEV_TYPE_STA: 482fec4b898SCarl Huang if (ar->nlo_enabled) { 483fec4b898SCarl Huang struct wmi_pno_scan_req *pno; 484fec4b898SCarl Huang 485fec4b898SCarl Huang pno = kzalloc(sizeof(*pno), GFP_KERNEL); 486fec4b898SCarl Huang if (!pno) 487fec4b898SCarl Huang return -ENOMEM; 488fec4b898SCarl Huang 489fec4b898SCarl Huang pno->enable = 0; 490fec4b898SCarl Huang ar->nlo_enabled = false; 491fec4b898SCarl Huang ret = ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 492fec4b898SCarl Huang kfree(pno); 493fec4b898SCarl Huang } 494fec4b898SCarl Huang break; 495fec4b898SCarl Huang default: 496fec4b898SCarl Huang break; 497fec4b898SCarl Huang } 498fec4b898SCarl Huang return ret; 499fec4b898SCarl Huang } 500fec4b898SCarl Huang 501fec4b898SCarl Huang static int ath11k_wow_nlo_cleanup(struct ath11k *ar) 502fec4b898SCarl Huang { 503fec4b898SCarl Huang struct ath11k_vif *arvif; 504fec4b898SCarl Huang int ret; 505fec4b898SCarl Huang 506fec4b898SCarl Huang lockdep_assert_held(&ar->conf_mutex); 507fec4b898SCarl Huang 508fec4b898SCarl Huang list_for_each_entry(arvif, &ar->arvifs, list) { 509fec4b898SCarl Huang ret = ath11k_vif_wow_clean_nlo(arvif); 510fec4b898SCarl Huang if (ret) { 511fec4b898SCarl Huang ath11k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n", 512fec4b898SCarl Huang arvif->vdev_id, ret); 513fec4b898SCarl Huang return ret; 514fec4b898SCarl Huang } 515fec4b898SCarl Huang } 516fec4b898SCarl Huang 517fec4b898SCarl Huang return 0; 518fec4b898SCarl Huang } 519fec4b898SCarl Huang 520c417b247SCarl Huang static int ath11k_wow_set_hw_filter(struct ath11k *ar) 521c417b247SCarl Huang { 522c417b247SCarl Huang struct ath11k_vif *arvif; 523c417b247SCarl Huang u32 bitmap; 524c417b247SCarl Huang int ret; 525c417b247SCarl Huang 526c417b247SCarl Huang lockdep_assert_held(&ar->conf_mutex); 527c417b247SCarl Huang 528c417b247SCarl Huang list_for_each_entry(arvif, &ar->arvifs, list) { 529c417b247SCarl Huang bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC | 530c417b247SCarl Huang WMI_HW_DATA_FILTER_DROP_NON_ARP_BC; 531c417b247SCarl Huang ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 532c417b247SCarl Huang bitmap, 533c417b247SCarl Huang true); 534c417b247SCarl Huang if (ret) { 535c417b247SCarl Huang ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n", 536c417b247SCarl Huang arvif->vdev_id, ret); 537c417b247SCarl Huang return ret; 538c417b247SCarl Huang } 539c417b247SCarl Huang } 540c417b247SCarl Huang 541c417b247SCarl Huang return 0; 542c417b247SCarl Huang } 543c417b247SCarl Huang 544c417b247SCarl Huang static int ath11k_wow_clear_hw_filter(struct ath11k *ar) 545c417b247SCarl Huang { 546c417b247SCarl Huang struct ath11k_vif *arvif; 547c417b247SCarl Huang int ret; 548c417b247SCarl Huang 549c417b247SCarl Huang lockdep_assert_held(&ar->conf_mutex); 550c417b247SCarl Huang 551c417b247SCarl Huang list_for_each_entry(arvif, &ar->arvifs, list) { 552c417b247SCarl Huang ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false); 553c417b247SCarl Huang 554c417b247SCarl Huang if (ret) { 555c417b247SCarl Huang ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n", 556c417b247SCarl Huang arvif->vdev_id, ret); 557c417b247SCarl Huang return ret; 558c417b247SCarl Huang } 559c417b247SCarl Huang } 560c417b247SCarl Huang 561c417b247SCarl Huang return 0; 562c417b247SCarl Huang } 563c417b247SCarl Huang 564c3c36bfeSCarl Huang static int ath11k_wow_arp_ns_offload(struct ath11k *ar, bool enable) 565c3c36bfeSCarl Huang { 566c3c36bfeSCarl Huang struct ath11k_vif *arvif; 567c3c36bfeSCarl Huang int ret; 568c3c36bfeSCarl Huang 569c3c36bfeSCarl Huang lockdep_assert_held(&ar->conf_mutex); 570c3c36bfeSCarl Huang 571c3c36bfeSCarl Huang list_for_each_entry(arvif, &ar->arvifs, list) { 572c3c36bfeSCarl Huang if (arvif->vdev_type != WMI_VDEV_TYPE_STA) 573c3c36bfeSCarl Huang continue; 574c3c36bfeSCarl Huang 575c3c36bfeSCarl Huang ret = ath11k_wmi_arp_ns_offload(ar, arvif, enable); 576c3c36bfeSCarl Huang 577c3c36bfeSCarl Huang if (ret) { 578c3c36bfeSCarl Huang ath11k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n", 579c3c36bfeSCarl Huang arvif->vdev_id, enable, ret); 580c3c36bfeSCarl Huang return ret; 581c3c36bfeSCarl Huang } 582c3c36bfeSCarl Huang } 583c3c36bfeSCarl Huang 584c3c36bfeSCarl Huang return 0; 585c3c36bfeSCarl Huang } 586c3c36bfeSCarl Huang 587a16d9b50SCarl Huang static int ath11k_gtk_rekey_offload(struct ath11k *ar, bool enable) 588a16d9b50SCarl Huang { 589a16d9b50SCarl Huang struct ath11k_vif *arvif; 590a16d9b50SCarl Huang int ret; 591a16d9b50SCarl Huang 592a16d9b50SCarl Huang lockdep_assert_held(&ar->conf_mutex); 593a16d9b50SCarl Huang 594a16d9b50SCarl Huang list_for_each_entry(arvif, &ar->arvifs, list) { 595a16d9b50SCarl Huang if (arvif->vdev_type != WMI_VDEV_TYPE_STA || 596a16d9b50SCarl Huang !arvif->is_up || 597a16d9b50SCarl Huang !arvif->rekey_data.enable_offload) 598a16d9b50SCarl Huang continue; 599a16d9b50SCarl Huang 600a16d9b50SCarl Huang /* get rekey info before disable rekey offload */ 601a16d9b50SCarl Huang if (!enable) { 602a16d9b50SCarl Huang ret = ath11k_wmi_gtk_rekey_getinfo(ar, arvif); 603a16d9b50SCarl Huang if (ret) { 604a16d9b50SCarl Huang ath11k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n", 605a16d9b50SCarl Huang arvif->vdev_id, ret); 606a16d9b50SCarl Huang return ret; 607a16d9b50SCarl Huang } 608a16d9b50SCarl Huang } 609a16d9b50SCarl Huang 610a16d9b50SCarl Huang ret = ath11k_wmi_gtk_rekey_offload(ar, arvif, enable); 611a16d9b50SCarl Huang 612a16d9b50SCarl Huang if (ret) { 613a16d9b50SCarl Huang ath11k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n", 614a16d9b50SCarl Huang arvif->vdev_id, enable, ret); 615a16d9b50SCarl Huang return ret; 616a16d9b50SCarl Huang } 617a16d9b50SCarl Huang } 618a16d9b50SCarl Huang 619a16d9b50SCarl Huang return 0; 620a16d9b50SCarl Huang } 621a16d9b50SCarl Huang 622c3c36bfeSCarl Huang static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable) 623c3c36bfeSCarl Huang { 624c3c36bfeSCarl Huang int ret; 625c3c36bfeSCarl Huang 626c3c36bfeSCarl Huang ret = ath11k_wow_arp_ns_offload(ar, enable); 627c3c36bfeSCarl Huang if (ret) { 628c3c36bfeSCarl Huang ath11k_warn(ar->ab, "failed to offload ARP and NS %d %d\n", 629c3c36bfeSCarl Huang enable, ret); 630c3c36bfeSCarl Huang return ret; 631c3c36bfeSCarl Huang } 632c3c36bfeSCarl Huang 633a16d9b50SCarl Huang ret = ath11k_gtk_rekey_offload(ar, enable); 634a16d9b50SCarl Huang if (ret) { 635a16d9b50SCarl Huang ath11k_warn(ar->ab, "failed to offload gtk rekey %d %d\n", 636a16d9b50SCarl Huang enable, ret); 637a16d9b50SCarl Huang return ret; 638a16d9b50SCarl Huang } 639a16d9b50SCarl Huang 640c3c36bfeSCarl Huang return 0; 641c3c36bfeSCarl Huang } 642c3c36bfeSCarl Huang 643ba9177fcSCarl Huang int ath11k_wow_op_suspend(struct ieee80211_hw *hw, 644ba9177fcSCarl Huang struct cfg80211_wowlan *wowlan) 645ba9177fcSCarl Huang { 646ba9177fcSCarl Huang struct ath11k *ar = hw->priv; 647ba9177fcSCarl Huang int ret; 648ba9177fcSCarl Huang 649ba9177fcSCarl Huang mutex_lock(&ar->conf_mutex); 650ba9177fcSCarl Huang 65190bf5c8dSCarl Huang ret = ath11k_dp_rx_pktlog_stop(ar->ab, true); 65290bf5c8dSCarl Huang if (ret) { 65390bf5c8dSCarl Huang ath11k_warn(ar->ab, 65490bf5c8dSCarl Huang "failed to stop dp rx (and timer) pktlog during wow suspend: %d\n", 65590bf5c8dSCarl Huang ret); 65690bf5c8dSCarl Huang goto exit; 65790bf5c8dSCarl Huang } 65890bf5c8dSCarl Huang 659ba9177fcSCarl Huang ret = ath11k_wow_cleanup(ar); 660ba9177fcSCarl Huang if (ret) { 661ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n", 662ba9177fcSCarl Huang ret); 663ba9177fcSCarl Huang goto exit; 664ba9177fcSCarl Huang } 665ba9177fcSCarl Huang 666ba9177fcSCarl Huang ret = ath11k_wow_set_wakeups(ar, wowlan); 667ba9177fcSCarl Huang if (ret) { 668ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n", 669ba9177fcSCarl Huang ret); 670ba9177fcSCarl Huang goto cleanup; 671ba9177fcSCarl Huang } 672ba9177fcSCarl Huang 673c3c36bfeSCarl Huang ret = ath11k_wow_protocol_offload(ar, true); 674c3c36bfeSCarl Huang if (ret) { 675c3c36bfeSCarl Huang ath11k_warn(ar->ab, "failed to set wow protocol offload events: %d\n", 676c3c36bfeSCarl Huang ret); 677c3c36bfeSCarl Huang goto cleanup; 678c3c36bfeSCarl Huang } 679c3c36bfeSCarl Huang 680c3c36bfeSCarl Huang ath11k_mac_drain_tx(ar); 681ba9177fcSCarl Huang ret = ath11k_mac_wait_tx_complete(ar); 682ba9177fcSCarl Huang if (ret) { 683ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret); 684ba9177fcSCarl Huang goto cleanup; 685ba9177fcSCarl Huang } 686ba9177fcSCarl Huang 687c417b247SCarl Huang ret = ath11k_wow_set_hw_filter(ar); 688c417b247SCarl Huang if (ret) { 689c417b247SCarl Huang ath11k_warn(ar->ab, "failed to set hw filter: %d\n", 690c417b247SCarl Huang ret); 691c417b247SCarl Huang goto cleanup; 692c417b247SCarl Huang } 693c417b247SCarl Huang 694ba9177fcSCarl Huang ret = ath11k_wow_enable(ar->ab); 695ba9177fcSCarl Huang if (ret) { 696ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to start wow: %d\n", ret); 697ba9177fcSCarl Huang goto cleanup; 698ba9177fcSCarl Huang } 699ba9177fcSCarl Huang 70090bf5c8dSCarl Huang ret = ath11k_dp_rx_pktlog_stop(ar->ab, false); 70190bf5c8dSCarl Huang if (ret) { 70290bf5c8dSCarl Huang ath11k_warn(ar->ab, 70390bf5c8dSCarl Huang "failed to stop dp rx pktlog during wow suspend: %d\n", 70490bf5c8dSCarl Huang ret); 70590bf5c8dSCarl Huang goto cleanup; 70690bf5c8dSCarl Huang } 70790bf5c8dSCarl Huang 708ba9177fcSCarl Huang ath11k_ce_stop_shadow_timers(ar->ab); 709ba9177fcSCarl Huang ath11k_dp_stop_shadow_timers(ar->ab); 710ba9177fcSCarl Huang 711ba9177fcSCarl Huang ath11k_hif_irq_disable(ar->ab); 712ba9177fcSCarl Huang ath11k_hif_ce_irq_disable(ar->ab); 713ba9177fcSCarl Huang 714ba9177fcSCarl Huang ret = ath11k_hif_suspend(ar->ab); 715ba9177fcSCarl Huang if (ret) { 716ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret); 717ba9177fcSCarl Huang goto wakeup; 718ba9177fcSCarl Huang } 719ba9177fcSCarl Huang 720ba9177fcSCarl Huang goto exit; 721ba9177fcSCarl Huang 722ba9177fcSCarl Huang wakeup: 723ba9177fcSCarl Huang ath11k_wow_wakeup(ar->ab); 724ba9177fcSCarl Huang 725ba9177fcSCarl Huang cleanup: 726ba9177fcSCarl Huang ath11k_wow_cleanup(ar); 727ba9177fcSCarl Huang 728ba9177fcSCarl Huang exit: 729ba9177fcSCarl Huang mutex_unlock(&ar->conf_mutex); 730ba9177fcSCarl Huang return ret ? 1 : 0; 731ba9177fcSCarl Huang } 732ba9177fcSCarl Huang 733ba9177fcSCarl Huang void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) 734ba9177fcSCarl Huang { 735ba9177fcSCarl Huang struct ath11k *ar = hw->priv; 736ba9177fcSCarl Huang 737ba9177fcSCarl Huang mutex_lock(&ar->conf_mutex); 738ba9177fcSCarl Huang device_set_wakeup_enable(ar->ab->dev, enabled); 739ba9177fcSCarl Huang mutex_unlock(&ar->conf_mutex); 740ba9177fcSCarl Huang } 741ba9177fcSCarl Huang 742ba9177fcSCarl Huang int ath11k_wow_op_resume(struct ieee80211_hw *hw) 743ba9177fcSCarl Huang { 744ba9177fcSCarl Huang struct ath11k *ar = hw->priv; 745ba9177fcSCarl Huang int ret; 746ba9177fcSCarl Huang 747ba9177fcSCarl Huang mutex_lock(&ar->conf_mutex); 748ba9177fcSCarl Huang 749ba9177fcSCarl Huang ret = ath11k_hif_resume(ar->ab); 750ba9177fcSCarl Huang if (ret) { 751ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret); 752ba9177fcSCarl Huang goto exit; 753ba9177fcSCarl Huang } 754ba9177fcSCarl Huang 755ba9177fcSCarl Huang ath11k_hif_ce_irq_enable(ar->ab); 756ba9177fcSCarl Huang ath11k_hif_irq_enable(ar->ab); 757ba9177fcSCarl Huang 75890bf5c8dSCarl Huang ret = ath11k_dp_rx_pktlog_start(ar->ab); 75990bf5c8dSCarl Huang if (ret) { 76090bf5c8dSCarl Huang ath11k_warn(ar->ab, "failed to start rx pktlog from wow: %d\n", ret); 761*60519441SYang Yingliang goto exit; 76290bf5c8dSCarl Huang } 76390bf5c8dSCarl Huang 764ba9177fcSCarl Huang ret = ath11k_wow_wakeup(ar->ab); 765fec4b898SCarl Huang if (ret) { 766ba9177fcSCarl Huang ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); 767fec4b898SCarl Huang goto exit; 768fec4b898SCarl Huang } 769fec4b898SCarl Huang 770fec4b898SCarl Huang ret = ath11k_wow_nlo_cleanup(ar); 771fec4b898SCarl Huang if (ret) { 772fec4b898SCarl Huang ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret); 773fec4b898SCarl Huang goto exit; 774fec4b898SCarl Huang } 775ba9177fcSCarl Huang 776c417b247SCarl Huang ret = ath11k_wow_clear_hw_filter(ar); 777c417b247SCarl Huang if (ret) { 778c417b247SCarl Huang ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret); 779c417b247SCarl Huang goto exit; 780c417b247SCarl Huang } 781c417b247SCarl Huang 782c3c36bfeSCarl Huang ret = ath11k_wow_protocol_offload(ar, false); 783c3c36bfeSCarl Huang if (ret) { 784c3c36bfeSCarl Huang ath11k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n", 785c3c36bfeSCarl Huang ret); 786c3c36bfeSCarl Huang goto exit; 787c3c36bfeSCarl Huang } 788c3c36bfeSCarl Huang 789ba9177fcSCarl Huang exit: 790ba9177fcSCarl Huang if (ret) { 791ba9177fcSCarl Huang switch (ar->state) { 792ba9177fcSCarl Huang case ATH11K_STATE_ON: 793ba9177fcSCarl Huang ar->state = ATH11K_STATE_RESTARTING; 794ba9177fcSCarl Huang ret = 1; 795ba9177fcSCarl Huang break; 796ba9177fcSCarl Huang case ATH11K_STATE_OFF: 797ba9177fcSCarl Huang case ATH11K_STATE_RESTARTING: 798ba9177fcSCarl Huang case ATH11K_STATE_RESTARTED: 799ba9177fcSCarl Huang case ATH11K_STATE_WEDGED: 800ba9177fcSCarl Huang ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", 801ba9177fcSCarl Huang ar->state); 802ba9177fcSCarl Huang ret = -EIO; 803ba9177fcSCarl Huang break; 804ba9177fcSCarl Huang } 805ba9177fcSCarl Huang } 806ba9177fcSCarl Huang 807ba9177fcSCarl Huang mutex_unlock(&ar->conf_mutex); 808ba9177fcSCarl Huang return ret; 809ba9177fcSCarl Huang } 810ba9177fcSCarl Huang 811ba9177fcSCarl Huang int ath11k_wow_init(struct ath11k *ar) 812ba9177fcSCarl Huang { 813633469e3SNagarajan Maran if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map)) 814633469e3SNagarajan Maran return 0; 815ba9177fcSCarl Huang 816ba9177fcSCarl Huang ar->wow.wowlan_support = ath11k_wowlan_support; 817ba9177fcSCarl Huang 818ba9177fcSCarl Huang if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode == 819ba9177fcSCarl Huang ATH11K_HW_TXRX_NATIVE_WIFI) { 820ba9177fcSCarl Huang ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; 821ba9177fcSCarl Huang ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; 822ba9177fcSCarl Huang } 823ba9177fcSCarl Huang 824fec4b898SCarl Huang if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) { 825fec4b898SCarl Huang ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; 826fec4b898SCarl Huang ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; 827fec4b898SCarl Huang } 828fec4b898SCarl Huang 829ba9177fcSCarl Huang ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS; 830ba9177fcSCarl Huang ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; 831ba9177fcSCarl Huang ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; 832ba9177fcSCarl Huang 833ba9177fcSCarl Huang device_set_wakeup_capable(ar->ab->dev, true); 834ba9177fcSCarl Huang 835ba9177fcSCarl Huang return 0; 836ba9177fcSCarl Huang } 837