1f0553ca9SKalle Valo // SPDX-License-Identifier: ISC 25fd3ac3cSJanusz Dziedzic /* 38b1083d6SKalle Valo * Copyright (c) 2015-2017 Qualcomm Atheros, Inc. 4fa3440faSWen Gong * Copyright (c) 2018, The Linux Foundation. All rights reserved. 55fd3ac3cSJanusz Dziedzic */ 65fd3ac3cSJanusz Dziedzic 75fd3ac3cSJanusz Dziedzic #include "mac.h" 85fd3ac3cSJanusz Dziedzic 95fd3ac3cSJanusz Dziedzic #include <net/mac80211.h> 105fd3ac3cSJanusz Dziedzic #include "hif.h" 115fd3ac3cSJanusz Dziedzic #include "core.h" 125fd3ac3cSJanusz Dziedzic #include "debug.h" 135fd3ac3cSJanusz Dziedzic #include "wmi.h" 145fd3ac3cSJanusz Dziedzic #include "wmi-ops.h" 155fd3ac3cSJanusz Dziedzic 165fd3ac3cSJanusz Dziedzic static const struct wiphy_wowlan_support ath10k_wowlan_support = { 175fd3ac3cSJanusz Dziedzic .flags = WIPHY_WOWLAN_DISCONNECT | 185fd3ac3cSJanusz Dziedzic WIPHY_WOWLAN_MAGIC_PKT, 1925c86619SJanusz Dziedzic .pattern_min_len = WOW_MIN_PATTERN_SIZE, 2025c86619SJanusz Dziedzic .pattern_max_len = WOW_MAX_PATTERN_SIZE, 2125c86619SJanusz Dziedzic .max_pkt_offset = WOW_MAX_PKT_OFFSET, 225fd3ac3cSJanusz Dziedzic }; 235fd3ac3cSJanusz Dziedzic 245fd3ac3cSJanusz Dziedzic static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif) 255fd3ac3cSJanusz Dziedzic { 265fd3ac3cSJanusz Dziedzic struct ath10k *ar = arvif->ar; 275fd3ac3cSJanusz Dziedzic int i, ret; 285fd3ac3cSJanusz Dziedzic 295fd3ac3cSJanusz Dziedzic for (i = 0; i < WOW_EVENT_MAX; i++) { 305fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); 315fd3ac3cSJanusz Dziedzic if (ret) { 325fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n", 335fd3ac3cSJanusz Dziedzic wow_wakeup_event(i), arvif->vdev_id, ret); 345fd3ac3cSJanusz Dziedzic return ret; 355fd3ac3cSJanusz Dziedzic } 365fd3ac3cSJanusz Dziedzic } 375fd3ac3cSJanusz Dziedzic 3825c86619SJanusz Dziedzic for (i = 0; i < ar->wow.max_num_patterns; i++) { 3925c86619SJanusz Dziedzic ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); 4025c86619SJanusz Dziedzic if (ret) { 4125c86619SJanusz Dziedzic ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n", 4225c86619SJanusz Dziedzic i, arvif->vdev_id, ret); 4325c86619SJanusz Dziedzic return ret; 4425c86619SJanusz Dziedzic } 4525c86619SJanusz Dziedzic } 4625c86619SJanusz Dziedzic 475fd3ac3cSJanusz Dziedzic return 0; 485fd3ac3cSJanusz Dziedzic } 495fd3ac3cSJanusz Dziedzic 505fd3ac3cSJanusz Dziedzic static int ath10k_wow_cleanup(struct ath10k *ar) 515fd3ac3cSJanusz Dziedzic { 525fd3ac3cSJanusz Dziedzic struct ath10k_vif *arvif; 535fd3ac3cSJanusz Dziedzic int ret; 545fd3ac3cSJanusz Dziedzic 555fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 565fd3ac3cSJanusz Dziedzic 575fd3ac3cSJanusz Dziedzic list_for_each_entry(arvif, &ar->arvifs, list) { 585fd3ac3cSJanusz Dziedzic ret = ath10k_wow_vif_cleanup(arvif); 595fd3ac3cSJanusz Dziedzic if (ret) { 605fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n", 615fd3ac3cSJanusz Dziedzic arvif->vdev_id, ret); 625fd3ac3cSJanusz Dziedzic return ret; 635fd3ac3cSJanusz Dziedzic } 645fd3ac3cSJanusz Dziedzic } 655fd3ac3cSJanusz Dziedzic 665fd3ac3cSJanusz Dziedzic return 0; 675fd3ac3cSJanusz Dziedzic } 685fd3ac3cSJanusz Dziedzic 69bdf2bd9aSKalle Valo /* 70fa3440faSWen Gong * Convert a 802.3 format to a 802.11 format. 71fa3440faSWen Gong * +------------+-----------+--------+----------------+ 72fa3440faSWen Gong * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | 73fa3440faSWen Gong * +------------+-----------+--------+----------------+ 74fa3440faSWen Gong * |__ |_______ |____________ |________ 75fa3440faSWen Gong * | | | | 76fa3440faSWen Gong * +--+------------+----+-----------+---------------+-----------+ 77fa3440faSWen Gong * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | 78fa3440faSWen Gong * +--+------------+----+-----------+---------------+-----------+ 79fa3440faSWen Gong */ 80bdf2bd9aSKalle Valo static void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, 81fa3440faSWen Gong const struct cfg80211_pkt_pattern *old) 82fa3440faSWen Gong { 83fa3440faSWen Gong u8 hdr_8023_pattern[ETH_HLEN] = {}; 84fa3440faSWen Gong u8 hdr_8023_bit_mask[ETH_HLEN] = {}; 85fa3440faSWen Gong u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; 86fa3440faSWen Gong u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; 87fa3440faSWen Gong 88fa3440faSWen Gong int total_len = old->pkt_offset + old->pattern_len; 89fa3440faSWen Gong int hdr_80211_end_offset; 90fa3440faSWen Gong 91fa3440faSWen Gong struct ieee80211_hdr_3addr *new_hdr_pattern = 92fa3440faSWen Gong (struct ieee80211_hdr_3addr *)hdr_80211_pattern; 93fa3440faSWen Gong struct ieee80211_hdr_3addr *new_hdr_mask = 94fa3440faSWen Gong (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; 95fa3440faSWen Gong struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; 96fa3440faSWen Gong struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; 97fa3440faSWen Gong int hdr_len = sizeof(*new_hdr_pattern); 98fa3440faSWen Gong 99fa3440faSWen Gong struct rfc1042_hdr *new_rfc_pattern = 100fa3440faSWen Gong (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); 101fa3440faSWen Gong struct rfc1042_hdr *new_rfc_mask = 102fa3440faSWen Gong (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); 103fa3440faSWen Gong int rfc_len = sizeof(*new_rfc_pattern); 104fa3440faSWen Gong 105fa3440faSWen Gong memcpy(hdr_8023_pattern + old->pkt_offset, 106fa3440faSWen Gong old->pattern, ETH_HLEN - old->pkt_offset); 107fa3440faSWen Gong memcpy(hdr_8023_bit_mask + old->pkt_offset, 108fa3440faSWen Gong old->mask, ETH_HLEN - old->pkt_offset); 109fa3440faSWen Gong 110fa3440faSWen Gong /* Copy destination address */ 111fa3440faSWen Gong memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); 112fa3440faSWen Gong memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); 113fa3440faSWen Gong 114fa3440faSWen Gong /* Copy source address */ 115fa3440faSWen Gong memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); 116fa3440faSWen Gong memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); 117fa3440faSWen Gong 118fa3440faSWen Gong /* Copy logic link type */ 119fa3440faSWen Gong memcpy(&new_rfc_pattern->snap_type, 120fa3440faSWen Gong &old_hdr_pattern->h_proto, 121fa3440faSWen Gong sizeof(old_hdr_pattern->h_proto)); 122fa3440faSWen Gong memcpy(&new_rfc_mask->snap_type, 123fa3440faSWen Gong &old_hdr_mask->h_proto, 124fa3440faSWen Gong sizeof(old_hdr_mask->h_proto)); 125fa3440faSWen Gong 126c8cb0964SYangtao Li /* Calculate new pkt_offset */ 127fa3440faSWen Gong if (old->pkt_offset < ETH_ALEN) 128fa3440faSWen Gong new->pkt_offset = old->pkt_offset + 129fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr1); 130fa3440faSWen Gong else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) 131fa3440faSWen Gong new->pkt_offset = old->pkt_offset + 132fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr3) - 133fa3440faSWen Gong offsetof(struct ethhdr, h_source); 134fa3440faSWen Gong else 135fa3440faSWen Gong new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; 136fa3440faSWen Gong 137c8cb0964SYangtao Li /* Calculate new hdr end offset */ 138fa3440faSWen Gong if (total_len > ETH_HLEN) 139fa3440faSWen Gong hdr_80211_end_offset = hdr_len + rfc_len; 140fa3440faSWen Gong else if (total_len > offsetof(struct ethhdr, h_proto)) 141fa3440faSWen Gong hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; 142fa3440faSWen Gong else if (total_len > ETH_ALEN) 143fa3440faSWen Gong hdr_80211_end_offset = total_len - ETH_ALEN + 144fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr3); 145fa3440faSWen Gong else 146fa3440faSWen Gong hdr_80211_end_offset = total_len + 147fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr1); 148fa3440faSWen Gong 149fa3440faSWen Gong new->pattern_len = hdr_80211_end_offset - new->pkt_offset; 150fa3440faSWen Gong 151fa3440faSWen Gong memcpy((u8 *)new->pattern, 152fa3440faSWen Gong hdr_80211_pattern + new->pkt_offset, 153fa3440faSWen Gong new->pattern_len); 154fa3440faSWen Gong memcpy((u8 *)new->mask, 155fa3440faSWen Gong hdr_80211_bit_mask + new->pkt_offset, 156fa3440faSWen Gong new->pattern_len); 157fa3440faSWen Gong 158fa3440faSWen Gong if (total_len > ETH_HLEN) { 159fa3440faSWen Gong /* Copy frame body */ 160fa3440faSWen Gong memcpy((u8 *)new->pattern + new->pattern_len, 161fa3440faSWen Gong (void *)old->pattern + ETH_HLEN - old->pkt_offset, 162fa3440faSWen Gong total_len - ETH_HLEN); 163fa3440faSWen Gong memcpy((u8 *)new->mask + new->pattern_len, 164fa3440faSWen Gong (void *)old->mask + ETH_HLEN - old->pkt_offset, 165fa3440faSWen Gong total_len - ETH_HLEN); 166fa3440faSWen Gong 167fa3440faSWen Gong new->pattern_len += total_len - ETH_HLEN; 168fa3440faSWen Gong } 169fa3440faSWen Gong } 170fa3440faSWen Gong 171ce834e28SWen Gong static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id, 172ce834e28SWen Gong struct cfg80211_sched_scan_request *nd_config, 173ce834e28SWen Gong struct wmi_pno_scan_req *pno) 174ce834e28SWen Gong { 175ce834e28SWen Gong int i, j, ret = 0; 176ce834e28SWen Gong u8 ssid_len; 177ce834e28SWen Gong 178ce834e28SWen Gong pno->enable = 1; 179ce834e28SWen Gong pno->vdev_id = vdev_id; 180ce834e28SWen Gong pno->uc_networks_count = nd_config->n_match_sets; 181ce834e28SWen Gong 182ce834e28SWen Gong if (!pno->uc_networks_count || 183ce834e28SWen Gong pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) 184ce834e28SWen Gong return -EINVAL; 185ce834e28SWen Gong 186ce834e28SWen Gong if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) 187ce834e28SWen Gong return -EINVAL; 188ce834e28SWen Gong 189ce834e28SWen Gong /* Filling per profile params */ 190ce834e28SWen Gong for (i = 0; i < pno->uc_networks_count; i++) { 191ce834e28SWen Gong ssid_len = nd_config->match_sets[i].ssid.ssid_len; 192ce834e28SWen Gong 193ce834e28SWen Gong if (ssid_len == 0 || ssid_len > 32) 194ce834e28SWen Gong return -EINVAL; 195ce834e28SWen Gong 196ce834e28SWen Gong pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len); 197ce834e28SWen Gong 198ce834e28SWen Gong memcpy(pno->a_networks[i].ssid.ssid, 199ce834e28SWen Gong nd_config->match_sets[i].ssid.ssid, 200ce834e28SWen Gong nd_config->match_sets[i].ssid.ssid_len); 201ce834e28SWen Gong pno->a_networks[i].authentication = 0; 202ce834e28SWen Gong pno->a_networks[i].encryption = 0; 203ce834e28SWen Gong pno->a_networks[i].bcast_nw_type = 0; 204ce834e28SWen Gong 205ce834e28SWen Gong /*Copying list of valid channel into request */ 206ce834e28SWen Gong pno->a_networks[i].channel_count = nd_config->n_channels; 207ce834e28SWen Gong pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; 208ce834e28SWen Gong 209ce834e28SWen Gong for (j = 0; j < nd_config->n_channels; j++) { 210ce834e28SWen Gong pno->a_networks[i].channels[j] = 211ce834e28SWen Gong nd_config->channels[j]->center_freq; 212ce834e28SWen Gong } 213ce834e28SWen Gong } 214ce834e28SWen Gong 215ce834e28SWen Gong /* set scan to passive if no SSIDs are specified in the request */ 216ce834e28SWen Gong if (nd_config->n_ssids == 0) 217ce834e28SWen Gong pno->do_passive_scan = true; 218ce834e28SWen Gong else 219ce834e28SWen Gong pno->do_passive_scan = false; 220ce834e28SWen Gong 221ce834e28SWen Gong for (i = 0; i < nd_config->n_ssids; i++) { 222ce834e28SWen Gong j = 0; 223ce834e28SWen Gong while (j < pno->uc_networks_count) { 224ce834e28SWen Gong if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) == 225ce834e28SWen Gong nd_config->ssids[i].ssid_len && 226ce834e28SWen Gong (memcmp(pno->a_networks[j].ssid.ssid, 227ce834e28SWen Gong nd_config->ssids[i].ssid, 228ce834e28SWen Gong __le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) { 229ce834e28SWen Gong pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; 230ce834e28SWen Gong break; 231ce834e28SWen Gong } 232ce834e28SWen Gong j++; 233ce834e28SWen Gong } 234ce834e28SWen Gong } 235ce834e28SWen Gong 236ce834e28SWen Gong if (nd_config->n_scan_plans == 2) { 237ce834e28SWen Gong pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 238ce834e28SWen Gong pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; 239ce834e28SWen Gong pno->slow_scan_period = 240ce834e28SWen Gong nd_config->scan_plans[1].interval * MSEC_PER_SEC; 241ce834e28SWen Gong } else if (nd_config->n_scan_plans == 1) { 242ce834e28SWen Gong pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 243ce834e28SWen Gong pno->fast_scan_max_cycles = 1; 244ce834e28SWen Gong pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 245ce834e28SWen Gong } else { 246ce834e28SWen Gong ath10k_warn(ar, "Invalid number of scan plans %d !!", 247ce834e28SWen Gong nd_config->n_scan_plans); 248ce834e28SWen Gong } 249ce834e28SWen Gong 250ce834e28SWen Gong if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 251ce834e28SWen Gong /* enable mac randomization */ 252ce834e28SWen Gong pno->enable_pno_scan_randomization = 1; 253ce834e28SWen Gong memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); 254ce834e28SWen Gong memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); 255ce834e28SWen Gong } 256ce834e28SWen Gong 257ce834e28SWen Gong pno->delay_start_time = nd_config->delay; 258ce834e28SWen Gong 259ce834e28SWen Gong /* Current FW does not support min-max range for dwell time */ 260ce834e28SWen Gong pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; 261ce834e28SWen Gong pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; 262ce834e28SWen Gong return ret; 263ce834e28SWen Gong } 264ce834e28SWen Gong 2655fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, 2665fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 2675fd3ac3cSJanusz Dziedzic { 2685fd3ac3cSJanusz Dziedzic int ret, i; 2695fd3ac3cSJanusz Dziedzic unsigned long wow_mask = 0; 2705fd3ac3cSJanusz Dziedzic struct ath10k *ar = arvif->ar; 27125c86619SJanusz Dziedzic const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 27225c86619SJanusz Dziedzic int pattern_id = 0; 2735fd3ac3cSJanusz Dziedzic 2745fd3ac3cSJanusz Dziedzic /* Setup requested WOW features */ 2755fd3ac3cSJanusz Dziedzic switch (arvif->vdev_type) { 2765fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_IBSS: 2775fd3ac3cSJanusz Dziedzic __set_bit(WOW_BEACON_EVENT, &wow_mask); 2781885c0f7SGustavo A. R. Silva fallthrough; 2795fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_AP: 2805fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 2815fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 2825fd3ac3cSJanusz Dziedzic __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 2835fd3ac3cSJanusz Dziedzic __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 2845fd3ac3cSJanusz Dziedzic __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 2855fd3ac3cSJanusz Dziedzic __set_bit(WOW_HTT_EVENT, &wow_mask); 2865fd3ac3cSJanusz Dziedzic __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 2875fd3ac3cSJanusz Dziedzic break; 2885fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_STA: 2895fd3ac3cSJanusz Dziedzic if (wowlan->disconnect) { 2905fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 2915fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 2925fd3ac3cSJanusz Dziedzic __set_bit(WOW_BMISS_EVENT, &wow_mask); 2935fd3ac3cSJanusz Dziedzic __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 2945fd3ac3cSJanusz Dziedzic } 2955fd3ac3cSJanusz Dziedzic 2965fd3ac3cSJanusz Dziedzic if (wowlan->magic_pkt) 2975fd3ac3cSJanusz Dziedzic __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 298ce834e28SWen Gong 299ce834e28SWen Gong if (wowlan->nd_config) { 300ce834e28SWen Gong struct wmi_pno_scan_req *pno; 301ce834e28SWen Gong int ret; 302ce834e28SWen Gong 303ce834e28SWen Gong pno = kzalloc(sizeof(*pno), GFP_KERNEL); 304ce834e28SWen Gong if (!pno) 305ce834e28SWen Gong return -ENOMEM; 306ce834e28SWen Gong 307ce834e28SWen Gong ar->nlo_enabled = true; 308ce834e28SWen Gong 309ce834e28SWen Gong ret = ath10k_wmi_pno_check(ar, arvif->vdev_id, 310ce834e28SWen Gong wowlan->nd_config, pno); 311ce834e28SWen Gong if (!ret) { 312ce834e28SWen Gong ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 313ce834e28SWen Gong __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); 314ce834e28SWen Gong } 315ce834e28SWen Gong 316ce834e28SWen Gong kfree(pno); 317ce834e28SWen Gong } 3185fd3ac3cSJanusz Dziedzic break; 3195fd3ac3cSJanusz Dziedzic default: 3205fd3ac3cSJanusz Dziedzic break; 3215fd3ac3cSJanusz Dziedzic } 3225fd3ac3cSJanusz Dziedzic 32325c86619SJanusz Dziedzic for (i = 0; i < wowlan->n_patterns; i++) { 32425c86619SJanusz Dziedzic u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; 325fa3440faSWen Gong u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; 326fa3440faSWen Gong u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; 327fa3440faSWen Gong struct cfg80211_pkt_pattern new_pattern = {}; 328fa3440faSWen Gong struct cfg80211_pkt_pattern old_pattern = patterns[i]; 32925c86619SJanusz Dziedzic int j; 33025c86619SJanusz Dziedzic 331fa3440faSWen Gong new_pattern.pattern = ath_pattern; 332fa3440faSWen Gong new_pattern.mask = ath_bitmask; 33325c86619SJanusz Dziedzic if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) 33425c86619SJanusz Dziedzic continue; 33525c86619SJanusz Dziedzic /* convert bytemask to bitmask */ 33625c86619SJanusz Dziedzic for (j = 0; j < patterns[i].pattern_len; j++) 33725c86619SJanusz Dziedzic if (patterns[i].mask[j / 8] & BIT(j % 8)) 33825c86619SJanusz Dziedzic bitmask[j] = 0xff; 339fa3440faSWen Gong old_pattern.mask = bitmask; 340fa3440faSWen Gong 341fa3440faSWen Gong if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 342*e3fb3d44SWen Gong if (patterns[i].pkt_offset < ETH_HLEN) { 343fa3440faSWen Gong ath10k_wow_convert_8023_to_80211(&new_pattern, 344fa3440faSWen Gong &old_pattern); 345*e3fb3d44SWen Gong } else { 346*e3fb3d44SWen Gong new_pattern = old_pattern; 347fa3440faSWen Gong new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; 348fa3440faSWen Gong } 349*e3fb3d44SWen Gong } 350fa3440faSWen Gong 351fa3440faSWen Gong if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) 352fa3440faSWen Gong return -EINVAL; 35325c86619SJanusz Dziedzic 35425c86619SJanusz Dziedzic ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id, 35525c86619SJanusz Dziedzic pattern_id, 356fa3440faSWen Gong new_pattern.pattern, 357fa3440faSWen Gong new_pattern.mask, 358fa3440faSWen Gong new_pattern.pattern_len, 359fa3440faSWen Gong new_pattern.pkt_offset); 36025c86619SJanusz Dziedzic if (ret) { 36125c86619SJanusz Dziedzic ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n", 36225c86619SJanusz Dziedzic pattern_id, 36325c86619SJanusz Dziedzic arvif->vdev_id, ret); 36425c86619SJanusz Dziedzic return ret; 36525c86619SJanusz Dziedzic } 36625c86619SJanusz Dziedzic 36725c86619SJanusz Dziedzic pattern_id++; 36825c86619SJanusz Dziedzic __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); 36925c86619SJanusz Dziedzic } 37025c86619SJanusz Dziedzic 3715fd3ac3cSJanusz Dziedzic for (i = 0; i < WOW_EVENT_MAX; i++) { 3725fd3ac3cSJanusz Dziedzic if (!test_bit(i, &wow_mask)) 3735fd3ac3cSJanusz Dziedzic continue; 3745fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 3755fd3ac3cSJanusz Dziedzic if (ret) { 3765fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n", 3775fd3ac3cSJanusz Dziedzic wow_wakeup_event(i), arvif->vdev_id, ret); 3785fd3ac3cSJanusz Dziedzic return ret; 3795fd3ac3cSJanusz Dziedzic } 3805fd3ac3cSJanusz Dziedzic } 3815fd3ac3cSJanusz Dziedzic 3825fd3ac3cSJanusz Dziedzic return 0; 3835fd3ac3cSJanusz Dziedzic } 3845fd3ac3cSJanusz Dziedzic 3855fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar, 3865fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 3875fd3ac3cSJanusz Dziedzic { 3885fd3ac3cSJanusz Dziedzic struct ath10k_vif *arvif; 3895fd3ac3cSJanusz Dziedzic int ret; 3905fd3ac3cSJanusz Dziedzic 3915fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 3925fd3ac3cSJanusz Dziedzic 3935fd3ac3cSJanusz Dziedzic list_for_each_entry(arvif, &ar->arvifs, list) { 3945fd3ac3cSJanusz Dziedzic ret = ath10k_vif_wow_set_wakeups(arvif, wowlan); 3955fd3ac3cSJanusz Dziedzic if (ret) { 3965fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n", 3975fd3ac3cSJanusz Dziedzic arvif->vdev_id, ret); 3985fd3ac3cSJanusz Dziedzic return ret; 3995fd3ac3cSJanusz Dziedzic } 4005fd3ac3cSJanusz Dziedzic } 4015fd3ac3cSJanusz Dziedzic 4025fd3ac3cSJanusz Dziedzic return 0; 4035fd3ac3cSJanusz Dziedzic } 4045fd3ac3cSJanusz Dziedzic 405ce834e28SWen Gong static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif) 406ce834e28SWen Gong { 407ce834e28SWen Gong int ret = 0; 408ce834e28SWen Gong struct ath10k *ar = arvif->ar; 409ce834e28SWen Gong 410ce834e28SWen Gong switch (arvif->vdev_type) { 411ce834e28SWen Gong case WMI_VDEV_TYPE_STA: 412ce834e28SWen Gong if (ar->nlo_enabled) { 413ce834e28SWen Gong struct wmi_pno_scan_req *pno; 414ce834e28SWen Gong 415ce834e28SWen Gong pno = kzalloc(sizeof(*pno), GFP_KERNEL); 416ce834e28SWen Gong if (!pno) 417ce834e28SWen Gong return -ENOMEM; 418ce834e28SWen Gong 419ce834e28SWen Gong pno->enable = 0; 420ce834e28SWen Gong ar->nlo_enabled = false; 421ce834e28SWen Gong ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 422ce834e28SWen Gong kfree(pno); 423ce834e28SWen Gong } 424ce834e28SWen Gong break; 425ce834e28SWen Gong default: 426ce834e28SWen Gong break; 427ce834e28SWen Gong } 428ce834e28SWen Gong return ret; 429ce834e28SWen Gong } 430ce834e28SWen Gong 431ce834e28SWen Gong static int ath10k_wow_nlo_cleanup(struct ath10k *ar) 432ce834e28SWen Gong { 433ce834e28SWen Gong struct ath10k_vif *arvif; 434ce834e28SWen Gong int ret = 0; 435ce834e28SWen Gong 436ce834e28SWen Gong lockdep_assert_held(&ar->conf_mutex); 437ce834e28SWen Gong 438ce834e28SWen Gong list_for_each_entry(arvif, &ar->arvifs, list) { 439ce834e28SWen Gong ret = ath10k_vif_wow_clean_nlo(arvif); 440ce834e28SWen Gong if (ret) { 441ce834e28SWen Gong ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n", 442ce834e28SWen Gong arvif->vdev_id, ret); 443ce834e28SWen Gong return ret; 444ce834e28SWen Gong } 445ce834e28SWen Gong } 446ce834e28SWen Gong 447ce834e28SWen Gong return 0; 448ce834e28SWen Gong } 449ce834e28SWen Gong 4505fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar) 4515fd3ac3cSJanusz Dziedzic { 4525fd3ac3cSJanusz Dziedzic int ret; 4535fd3ac3cSJanusz Dziedzic 4545fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 4555fd3ac3cSJanusz Dziedzic 4565fd3ac3cSJanusz Dziedzic reinit_completion(&ar->target_suspend); 4575fd3ac3cSJanusz Dziedzic 4585fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_enable(ar); 4595fd3ac3cSJanusz Dziedzic if (ret) { 4605fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to issue wow enable: %d\n", ret); 4615fd3ac3cSJanusz Dziedzic return ret; 4625fd3ac3cSJanusz Dziedzic } 4635fd3ac3cSJanusz Dziedzic 4645fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ); 4655fd3ac3cSJanusz Dziedzic if (ret == 0) { 4665fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for suspend completion\n"); 4675fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 4685fd3ac3cSJanusz Dziedzic } 4695fd3ac3cSJanusz Dziedzic 4705fd3ac3cSJanusz Dziedzic return 0; 4715fd3ac3cSJanusz Dziedzic } 4725fd3ac3cSJanusz Dziedzic 4735fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar) 4745fd3ac3cSJanusz Dziedzic { 4755fd3ac3cSJanusz Dziedzic int ret; 4765fd3ac3cSJanusz Dziedzic 4775fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 4785fd3ac3cSJanusz Dziedzic 4795fd3ac3cSJanusz Dziedzic reinit_completion(&ar->wow.wakeup_completed); 4805fd3ac3cSJanusz Dziedzic 4815fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_host_wakeup_ind(ar); 4825fd3ac3cSJanusz Dziedzic if (ret) { 4835fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to send wow wakeup indication: %d\n", 4845fd3ac3cSJanusz Dziedzic ret); 4855fd3ac3cSJanusz Dziedzic return ret; 4865fd3ac3cSJanusz Dziedzic } 4875fd3ac3cSJanusz Dziedzic 4885fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ); 4895fd3ac3cSJanusz Dziedzic if (ret == 0) { 4905fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n"); 4915fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 4925fd3ac3cSJanusz Dziedzic } 4935fd3ac3cSJanusz Dziedzic 4945fd3ac3cSJanusz Dziedzic return 0; 4955fd3ac3cSJanusz Dziedzic } 4965fd3ac3cSJanusz Dziedzic 4975fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw, 4985fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 4995fd3ac3cSJanusz Dziedzic { 5005fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 5015fd3ac3cSJanusz Dziedzic int ret; 5025fd3ac3cSJanusz Dziedzic 5035fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 5045fd3ac3cSJanusz Dziedzic 5055fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 506c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features))) { 5075fd3ac3cSJanusz Dziedzic ret = 1; 5085fd3ac3cSJanusz Dziedzic goto exit; 5095fd3ac3cSJanusz Dziedzic } 5105fd3ac3cSJanusz Dziedzic 5115fd3ac3cSJanusz Dziedzic ret = ath10k_wow_cleanup(ar); 5125fd3ac3cSJanusz Dziedzic if (ret) { 5135fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", 5145fd3ac3cSJanusz Dziedzic ret); 5155fd3ac3cSJanusz Dziedzic goto exit; 5165fd3ac3cSJanusz Dziedzic } 5175fd3ac3cSJanusz Dziedzic 5185fd3ac3cSJanusz Dziedzic ret = ath10k_wow_set_wakeups(ar, wowlan); 5195fd3ac3cSJanusz Dziedzic if (ret) { 5205fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeup events: %d\n", 5215fd3ac3cSJanusz Dziedzic ret); 5225fd3ac3cSJanusz Dziedzic goto cleanup; 5235fd3ac3cSJanusz Dziedzic } 5245fd3ac3cSJanusz Dziedzic 525828853acSWen Gong ath10k_mac_wait_tx_complete(ar); 526828853acSWen Gong 5275fd3ac3cSJanusz Dziedzic ret = ath10k_wow_enable(ar); 5285fd3ac3cSJanusz Dziedzic if (ret) { 5295fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to start wow: %d\n", ret); 5305fd3ac3cSJanusz Dziedzic goto cleanup; 5315fd3ac3cSJanusz Dziedzic } 5325fd3ac3cSJanusz Dziedzic 5335fd3ac3cSJanusz Dziedzic ret = ath10k_hif_suspend(ar); 5345fd3ac3cSJanusz Dziedzic if (ret) { 5355fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to suspend hif: %d\n", ret); 5365fd3ac3cSJanusz Dziedzic goto wakeup; 5375fd3ac3cSJanusz Dziedzic } 5385fd3ac3cSJanusz Dziedzic 5395fd3ac3cSJanusz Dziedzic goto exit; 5405fd3ac3cSJanusz Dziedzic 5415fd3ac3cSJanusz Dziedzic wakeup: 5425fd3ac3cSJanusz Dziedzic ath10k_wow_wakeup(ar); 5435fd3ac3cSJanusz Dziedzic 5445fd3ac3cSJanusz Dziedzic cleanup: 5455fd3ac3cSJanusz Dziedzic ath10k_wow_cleanup(ar); 5465fd3ac3cSJanusz Dziedzic 5475fd3ac3cSJanusz Dziedzic exit: 5485fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 5495fd3ac3cSJanusz Dziedzic return ret ? 1 : 0; 5505fd3ac3cSJanusz Dziedzic } 5515fd3ac3cSJanusz Dziedzic 552393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) 553393b706cSRyan Hsu { 554393b706cSRyan Hsu struct ath10k *ar = hw->priv; 555393b706cSRyan Hsu 556393b706cSRyan Hsu mutex_lock(&ar->conf_mutex); 557393b706cSRyan Hsu if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 558393b706cSRyan Hsu ar->running_fw->fw_file.fw_features)) { 559393b706cSRyan Hsu device_set_wakeup_enable(ar->dev, enabled); 560393b706cSRyan Hsu } 561393b706cSRyan Hsu mutex_unlock(&ar->conf_mutex); 562393b706cSRyan Hsu } 563393b706cSRyan Hsu 5645fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw) 5655fd3ac3cSJanusz Dziedzic { 5665fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 5675fd3ac3cSJanusz Dziedzic int ret; 5685fd3ac3cSJanusz Dziedzic 5695fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 5705fd3ac3cSJanusz Dziedzic 5715fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 572c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features))) { 5735fd3ac3cSJanusz Dziedzic ret = 1; 5745fd3ac3cSJanusz Dziedzic goto exit; 5755fd3ac3cSJanusz Dziedzic } 5765fd3ac3cSJanusz Dziedzic 5775fd3ac3cSJanusz Dziedzic ret = ath10k_hif_resume(ar); 5785fd3ac3cSJanusz Dziedzic if (ret) { 5795fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to resume hif: %d\n", ret); 5805fd3ac3cSJanusz Dziedzic goto exit; 5815fd3ac3cSJanusz Dziedzic } 5825fd3ac3cSJanusz Dziedzic 5835fd3ac3cSJanusz Dziedzic ret = ath10k_wow_wakeup(ar); 5845fd3ac3cSJanusz Dziedzic if (ret) 5855fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); 5865fd3ac3cSJanusz Dziedzic 587ce834e28SWen Gong ret = ath10k_wow_nlo_cleanup(ar); 588ce834e28SWen Gong if (ret) 589ce834e28SWen Gong ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret); 590ce834e28SWen Gong 5915fd3ac3cSJanusz Dziedzic exit: 5926f7429c2SMichal Kazior if (ret) { 5936f7429c2SMichal Kazior switch (ar->state) { 5946f7429c2SMichal Kazior case ATH10K_STATE_ON: 5956f7429c2SMichal Kazior ar->state = ATH10K_STATE_RESTARTING; 5966f7429c2SMichal Kazior ret = 1; 5976f7429c2SMichal Kazior break; 5986f7429c2SMichal Kazior case ATH10K_STATE_OFF: 5996f7429c2SMichal Kazior case ATH10K_STATE_RESTARTING: 6006f7429c2SMichal Kazior case ATH10K_STATE_RESTARTED: 6016f7429c2SMichal Kazior case ATH10K_STATE_UTF: 6026f7429c2SMichal Kazior case ATH10K_STATE_WEDGED: 6036f7429c2SMichal Kazior ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n", 6046f7429c2SMichal Kazior ar->state); 6056f7429c2SMichal Kazior ret = -EIO; 6066f7429c2SMichal Kazior break; 6076f7429c2SMichal Kazior } 6086f7429c2SMichal Kazior } 6096f7429c2SMichal Kazior 6105fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 6116f7429c2SMichal Kazior return ret; 6125fd3ac3cSJanusz Dziedzic } 6135fd3ac3cSJanusz Dziedzic 6145fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar) 6155fd3ac3cSJanusz Dziedzic { 616c4cdf753SKalle Valo if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 617c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features)) 6185fd3ac3cSJanusz Dziedzic return 0; 6195fd3ac3cSJanusz Dziedzic 6205fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map))) 6215fd3ac3cSJanusz Dziedzic return -EINVAL; 6225fd3ac3cSJanusz Dziedzic 62325c86619SJanusz Dziedzic ar->wow.wowlan_support = ath10k_wowlan_support; 624fa3440faSWen Gong 625fa3440faSWen Gong if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 626fa3440faSWen Gong ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; 627fa3440faSWen Gong ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; 628fa3440faSWen Gong } 629fa3440faSWen Gong 630ce834e28SWen Gong if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) { 631ce834e28SWen Gong ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; 632ce834e28SWen Gong ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; 633ce834e28SWen Gong } 634ce834e28SWen Gong 63525c86619SJanusz Dziedzic ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; 63625c86619SJanusz Dziedzic ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; 6375fd3ac3cSJanusz Dziedzic 638393b706cSRyan Hsu device_set_wakeup_capable(ar->dev, true); 639393b706cSRyan Hsu 6405fd3ac3cSJanusz Dziedzic return 0; 6415fd3ac3cSJanusz Dziedzic } 642