15fd3ac3cSJanusz Dziedzic /* 28b1083d6SKalle Valo * Copyright (c) 2015-2017 Qualcomm Atheros, Inc. 3fa3440faSWen 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 80fa3440faSWen Gong /** 81fa3440faSWen Gong * Convert a 802.3 format to a 802.11 format. 82fa3440faSWen Gong * +------------+-----------+--------+----------------+ 83fa3440faSWen Gong * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | 84fa3440faSWen Gong * +------------+-----------+--------+----------------+ 85fa3440faSWen Gong * |__ |_______ |____________ |________ 86fa3440faSWen Gong * | | | | 87fa3440faSWen Gong * +--+------------+----+-----------+---------------+-----------+ 88fa3440faSWen Gong * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | 89fa3440faSWen Gong * +--+------------+----+-----------+---------------+-----------+ 90fa3440faSWen Gong */ 91fa3440faSWen Gong static void ath10k_wow_convert_8023_to_80211 92fa3440faSWen Gong (struct cfg80211_pkt_pattern *new, 93fa3440faSWen Gong const struct cfg80211_pkt_pattern *old) 94fa3440faSWen Gong { 95fa3440faSWen Gong u8 hdr_8023_pattern[ETH_HLEN] = {}; 96fa3440faSWen Gong u8 hdr_8023_bit_mask[ETH_HLEN] = {}; 97fa3440faSWen Gong u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; 98fa3440faSWen Gong u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; 99fa3440faSWen Gong 100fa3440faSWen Gong int total_len = old->pkt_offset + old->pattern_len; 101fa3440faSWen Gong int hdr_80211_end_offset; 102fa3440faSWen Gong 103fa3440faSWen Gong struct ieee80211_hdr_3addr *new_hdr_pattern = 104fa3440faSWen Gong (struct ieee80211_hdr_3addr *)hdr_80211_pattern; 105fa3440faSWen Gong struct ieee80211_hdr_3addr *new_hdr_mask = 106fa3440faSWen Gong (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; 107fa3440faSWen Gong struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; 108fa3440faSWen Gong struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; 109fa3440faSWen Gong int hdr_len = sizeof(*new_hdr_pattern); 110fa3440faSWen Gong 111fa3440faSWen Gong struct rfc1042_hdr *new_rfc_pattern = 112fa3440faSWen Gong (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); 113fa3440faSWen Gong struct rfc1042_hdr *new_rfc_mask = 114fa3440faSWen Gong (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); 115fa3440faSWen Gong int rfc_len = sizeof(*new_rfc_pattern); 116fa3440faSWen Gong 117fa3440faSWen Gong memcpy(hdr_8023_pattern + old->pkt_offset, 118fa3440faSWen Gong old->pattern, ETH_HLEN - old->pkt_offset); 119fa3440faSWen Gong memcpy(hdr_8023_bit_mask + old->pkt_offset, 120fa3440faSWen Gong old->mask, ETH_HLEN - old->pkt_offset); 121fa3440faSWen Gong 122fa3440faSWen Gong /* Copy destination address */ 123fa3440faSWen Gong memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); 124fa3440faSWen Gong memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); 125fa3440faSWen Gong 126fa3440faSWen Gong /* Copy source address */ 127fa3440faSWen Gong memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); 128fa3440faSWen Gong memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); 129fa3440faSWen Gong 130fa3440faSWen Gong /* Copy logic link type */ 131fa3440faSWen Gong memcpy(&new_rfc_pattern->snap_type, 132fa3440faSWen Gong &old_hdr_pattern->h_proto, 133fa3440faSWen Gong sizeof(old_hdr_pattern->h_proto)); 134fa3440faSWen Gong memcpy(&new_rfc_mask->snap_type, 135fa3440faSWen Gong &old_hdr_mask->h_proto, 136fa3440faSWen Gong sizeof(old_hdr_mask->h_proto)); 137fa3440faSWen Gong 138*c8cb0964SYangtao Li /* Calculate new pkt_offset */ 139fa3440faSWen Gong if (old->pkt_offset < ETH_ALEN) 140fa3440faSWen Gong new->pkt_offset = old->pkt_offset + 141fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr1); 142fa3440faSWen Gong else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) 143fa3440faSWen Gong new->pkt_offset = old->pkt_offset + 144fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr3) - 145fa3440faSWen Gong offsetof(struct ethhdr, h_source); 146fa3440faSWen Gong else 147fa3440faSWen Gong new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; 148fa3440faSWen Gong 149*c8cb0964SYangtao Li /* Calculate new hdr end offset */ 150fa3440faSWen Gong if (total_len > ETH_HLEN) 151fa3440faSWen Gong hdr_80211_end_offset = hdr_len + rfc_len; 152fa3440faSWen Gong else if (total_len > offsetof(struct ethhdr, h_proto)) 153fa3440faSWen Gong hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; 154fa3440faSWen Gong else if (total_len > ETH_ALEN) 155fa3440faSWen Gong hdr_80211_end_offset = total_len - ETH_ALEN + 156fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr3); 157fa3440faSWen Gong else 158fa3440faSWen Gong hdr_80211_end_offset = total_len + 159fa3440faSWen Gong offsetof(struct ieee80211_hdr_3addr, addr1); 160fa3440faSWen Gong 161fa3440faSWen Gong new->pattern_len = hdr_80211_end_offset - new->pkt_offset; 162fa3440faSWen Gong 163fa3440faSWen Gong memcpy((u8 *)new->pattern, 164fa3440faSWen Gong hdr_80211_pattern + new->pkt_offset, 165fa3440faSWen Gong new->pattern_len); 166fa3440faSWen Gong memcpy((u8 *)new->mask, 167fa3440faSWen Gong hdr_80211_bit_mask + new->pkt_offset, 168fa3440faSWen Gong new->pattern_len); 169fa3440faSWen Gong 170fa3440faSWen Gong if (total_len > ETH_HLEN) { 171fa3440faSWen Gong /* Copy frame body */ 172fa3440faSWen Gong memcpy((u8 *)new->pattern + new->pattern_len, 173fa3440faSWen Gong (void *)old->pattern + ETH_HLEN - old->pkt_offset, 174fa3440faSWen Gong total_len - ETH_HLEN); 175fa3440faSWen Gong memcpy((u8 *)new->mask + new->pattern_len, 176fa3440faSWen Gong (void *)old->mask + ETH_HLEN - old->pkt_offset, 177fa3440faSWen Gong total_len - ETH_HLEN); 178fa3440faSWen Gong 179fa3440faSWen Gong new->pattern_len += total_len - ETH_HLEN; 180fa3440faSWen Gong } 181fa3440faSWen Gong } 182fa3440faSWen Gong 183ce834e28SWen Gong static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id, 184ce834e28SWen Gong struct cfg80211_sched_scan_request *nd_config, 185ce834e28SWen Gong struct wmi_pno_scan_req *pno) 186ce834e28SWen Gong { 187ce834e28SWen Gong int i, j, ret = 0; 188ce834e28SWen Gong u8 ssid_len; 189ce834e28SWen Gong 190ce834e28SWen Gong pno->enable = 1; 191ce834e28SWen Gong pno->vdev_id = vdev_id; 192ce834e28SWen Gong pno->uc_networks_count = nd_config->n_match_sets; 193ce834e28SWen Gong 194ce834e28SWen Gong if (!pno->uc_networks_count || 195ce834e28SWen Gong pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) 196ce834e28SWen Gong return -EINVAL; 197ce834e28SWen Gong 198ce834e28SWen Gong if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) 199ce834e28SWen Gong return -EINVAL; 200ce834e28SWen Gong 201ce834e28SWen Gong /* Filling per profile params */ 202ce834e28SWen Gong for (i = 0; i < pno->uc_networks_count; i++) { 203ce834e28SWen Gong ssid_len = nd_config->match_sets[i].ssid.ssid_len; 204ce834e28SWen Gong 205ce834e28SWen Gong if (ssid_len == 0 || ssid_len > 32) 206ce834e28SWen Gong return -EINVAL; 207ce834e28SWen Gong 208ce834e28SWen Gong pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len); 209ce834e28SWen Gong 210ce834e28SWen Gong memcpy(pno->a_networks[i].ssid.ssid, 211ce834e28SWen Gong nd_config->match_sets[i].ssid.ssid, 212ce834e28SWen Gong nd_config->match_sets[i].ssid.ssid_len); 213ce834e28SWen Gong pno->a_networks[i].authentication = 0; 214ce834e28SWen Gong pno->a_networks[i].encryption = 0; 215ce834e28SWen Gong pno->a_networks[i].bcast_nw_type = 0; 216ce834e28SWen Gong 217ce834e28SWen Gong /*Copying list of valid channel into request */ 218ce834e28SWen Gong pno->a_networks[i].channel_count = nd_config->n_channels; 219ce834e28SWen Gong pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; 220ce834e28SWen Gong 221ce834e28SWen Gong for (j = 0; j < nd_config->n_channels; j++) { 222ce834e28SWen Gong pno->a_networks[i].channels[j] = 223ce834e28SWen Gong nd_config->channels[j]->center_freq; 224ce834e28SWen Gong } 225ce834e28SWen Gong } 226ce834e28SWen Gong 227ce834e28SWen Gong /* set scan to passive if no SSIDs are specified in the request */ 228ce834e28SWen Gong if (nd_config->n_ssids == 0) 229ce834e28SWen Gong pno->do_passive_scan = true; 230ce834e28SWen Gong else 231ce834e28SWen Gong pno->do_passive_scan = false; 232ce834e28SWen Gong 233ce834e28SWen Gong for (i = 0; i < nd_config->n_ssids; i++) { 234ce834e28SWen Gong j = 0; 235ce834e28SWen Gong while (j < pno->uc_networks_count) { 236ce834e28SWen Gong if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) == 237ce834e28SWen Gong nd_config->ssids[i].ssid_len && 238ce834e28SWen Gong (memcmp(pno->a_networks[j].ssid.ssid, 239ce834e28SWen Gong nd_config->ssids[i].ssid, 240ce834e28SWen Gong __le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) { 241ce834e28SWen Gong pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; 242ce834e28SWen Gong break; 243ce834e28SWen Gong } 244ce834e28SWen Gong j++; 245ce834e28SWen Gong } 246ce834e28SWen Gong } 247ce834e28SWen Gong 248ce834e28SWen Gong if (nd_config->n_scan_plans == 2) { 249ce834e28SWen Gong pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 250ce834e28SWen Gong pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; 251ce834e28SWen Gong pno->slow_scan_period = 252ce834e28SWen Gong nd_config->scan_plans[1].interval * MSEC_PER_SEC; 253ce834e28SWen Gong } else if (nd_config->n_scan_plans == 1) { 254ce834e28SWen Gong pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 255ce834e28SWen Gong pno->fast_scan_max_cycles = 1; 256ce834e28SWen Gong pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 257ce834e28SWen Gong } else { 258ce834e28SWen Gong ath10k_warn(ar, "Invalid number of scan plans %d !!", 259ce834e28SWen Gong nd_config->n_scan_plans); 260ce834e28SWen Gong } 261ce834e28SWen Gong 262ce834e28SWen Gong if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 263ce834e28SWen Gong /* enable mac randomization */ 264ce834e28SWen Gong pno->enable_pno_scan_randomization = 1; 265ce834e28SWen Gong memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); 266ce834e28SWen Gong memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); 267ce834e28SWen Gong } 268ce834e28SWen Gong 269ce834e28SWen Gong pno->delay_start_time = nd_config->delay; 270ce834e28SWen Gong 271ce834e28SWen Gong /* Current FW does not support min-max range for dwell time */ 272ce834e28SWen Gong pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; 273ce834e28SWen Gong pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; 274ce834e28SWen Gong return ret; 275ce834e28SWen Gong } 276ce834e28SWen Gong 2775fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, 2785fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 2795fd3ac3cSJanusz Dziedzic { 2805fd3ac3cSJanusz Dziedzic int ret, i; 2815fd3ac3cSJanusz Dziedzic unsigned long wow_mask = 0; 2825fd3ac3cSJanusz Dziedzic struct ath10k *ar = arvif->ar; 28325c86619SJanusz Dziedzic const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 28425c86619SJanusz Dziedzic int pattern_id = 0; 2855fd3ac3cSJanusz Dziedzic 2865fd3ac3cSJanusz Dziedzic /* Setup requested WOW features */ 2875fd3ac3cSJanusz Dziedzic switch (arvif->vdev_type) { 2885fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_IBSS: 2895fd3ac3cSJanusz Dziedzic __set_bit(WOW_BEACON_EVENT, &wow_mask); 2905fd3ac3cSJanusz Dziedzic /* fall through */ 2915fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_AP: 2925fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 2935fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 2945fd3ac3cSJanusz Dziedzic __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 2955fd3ac3cSJanusz Dziedzic __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 2965fd3ac3cSJanusz Dziedzic __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 2975fd3ac3cSJanusz Dziedzic __set_bit(WOW_HTT_EVENT, &wow_mask); 2985fd3ac3cSJanusz Dziedzic __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 2995fd3ac3cSJanusz Dziedzic break; 3005fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_STA: 3015fd3ac3cSJanusz Dziedzic if (wowlan->disconnect) { 3025fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 3035fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 3045fd3ac3cSJanusz Dziedzic __set_bit(WOW_BMISS_EVENT, &wow_mask); 3055fd3ac3cSJanusz Dziedzic __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 3065fd3ac3cSJanusz Dziedzic } 3075fd3ac3cSJanusz Dziedzic 3085fd3ac3cSJanusz Dziedzic if (wowlan->magic_pkt) 3095fd3ac3cSJanusz Dziedzic __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 310ce834e28SWen Gong 311ce834e28SWen Gong if (wowlan->nd_config) { 312ce834e28SWen Gong struct wmi_pno_scan_req *pno; 313ce834e28SWen Gong int ret; 314ce834e28SWen Gong 315ce834e28SWen Gong pno = kzalloc(sizeof(*pno), GFP_KERNEL); 316ce834e28SWen Gong if (!pno) 317ce834e28SWen Gong return -ENOMEM; 318ce834e28SWen Gong 319ce834e28SWen Gong ar->nlo_enabled = true; 320ce834e28SWen Gong 321ce834e28SWen Gong ret = ath10k_wmi_pno_check(ar, arvif->vdev_id, 322ce834e28SWen Gong wowlan->nd_config, pno); 323ce834e28SWen Gong if (!ret) { 324ce834e28SWen Gong ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 325ce834e28SWen Gong __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); 326ce834e28SWen Gong } 327ce834e28SWen Gong 328ce834e28SWen Gong kfree(pno); 329ce834e28SWen Gong } 3305fd3ac3cSJanusz Dziedzic break; 3315fd3ac3cSJanusz Dziedzic default: 3325fd3ac3cSJanusz Dziedzic break; 3335fd3ac3cSJanusz Dziedzic } 3345fd3ac3cSJanusz Dziedzic 33525c86619SJanusz Dziedzic for (i = 0; i < wowlan->n_patterns; i++) { 33625c86619SJanusz Dziedzic u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; 337fa3440faSWen Gong u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; 338fa3440faSWen Gong u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; 339fa3440faSWen Gong struct cfg80211_pkt_pattern new_pattern = {}; 340fa3440faSWen Gong struct cfg80211_pkt_pattern old_pattern = patterns[i]; 34125c86619SJanusz Dziedzic int j; 34225c86619SJanusz Dziedzic 343fa3440faSWen Gong new_pattern.pattern = ath_pattern; 344fa3440faSWen Gong new_pattern.mask = ath_bitmask; 34525c86619SJanusz Dziedzic if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) 34625c86619SJanusz Dziedzic continue; 34725c86619SJanusz Dziedzic /* convert bytemask to bitmask */ 34825c86619SJanusz Dziedzic for (j = 0; j < patterns[i].pattern_len; j++) 34925c86619SJanusz Dziedzic if (patterns[i].mask[j / 8] & BIT(j % 8)) 35025c86619SJanusz Dziedzic bitmask[j] = 0xff; 351fa3440faSWen Gong old_pattern.mask = bitmask; 352fa3440faSWen Gong new_pattern = old_pattern; 353fa3440faSWen Gong 354fa3440faSWen Gong if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 355fa3440faSWen Gong if (patterns[i].pkt_offset < ETH_HLEN) 356fa3440faSWen Gong ath10k_wow_convert_8023_to_80211(&new_pattern, 357fa3440faSWen Gong &old_pattern); 358fa3440faSWen Gong else 359fa3440faSWen Gong new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; 360fa3440faSWen Gong } 361fa3440faSWen Gong 362fa3440faSWen Gong if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) 363fa3440faSWen Gong return -EINVAL; 36425c86619SJanusz Dziedzic 36525c86619SJanusz Dziedzic ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id, 36625c86619SJanusz Dziedzic pattern_id, 367fa3440faSWen Gong new_pattern.pattern, 368fa3440faSWen Gong new_pattern.mask, 369fa3440faSWen Gong new_pattern.pattern_len, 370fa3440faSWen Gong new_pattern.pkt_offset); 37125c86619SJanusz Dziedzic if (ret) { 37225c86619SJanusz Dziedzic ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n", 37325c86619SJanusz Dziedzic pattern_id, 37425c86619SJanusz Dziedzic arvif->vdev_id, ret); 37525c86619SJanusz Dziedzic return ret; 37625c86619SJanusz Dziedzic } 37725c86619SJanusz Dziedzic 37825c86619SJanusz Dziedzic pattern_id++; 37925c86619SJanusz Dziedzic __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); 38025c86619SJanusz Dziedzic } 38125c86619SJanusz Dziedzic 3825fd3ac3cSJanusz Dziedzic for (i = 0; i < WOW_EVENT_MAX; i++) { 3835fd3ac3cSJanusz Dziedzic if (!test_bit(i, &wow_mask)) 3845fd3ac3cSJanusz Dziedzic continue; 3855fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 3865fd3ac3cSJanusz Dziedzic if (ret) { 3875fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n", 3885fd3ac3cSJanusz Dziedzic wow_wakeup_event(i), arvif->vdev_id, ret); 3895fd3ac3cSJanusz Dziedzic return ret; 3905fd3ac3cSJanusz Dziedzic } 3915fd3ac3cSJanusz Dziedzic } 3925fd3ac3cSJanusz Dziedzic 3935fd3ac3cSJanusz Dziedzic return 0; 3945fd3ac3cSJanusz Dziedzic } 3955fd3ac3cSJanusz Dziedzic 3965fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar, 3975fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 3985fd3ac3cSJanusz Dziedzic { 3995fd3ac3cSJanusz Dziedzic struct ath10k_vif *arvif; 4005fd3ac3cSJanusz Dziedzic int ret; 4015fd3ac3cSJanusz Dziedzic 4025fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 4035fd3ac3cSJanusz Dziedzic 4045fd3ac3cSJanusz Dziedzic list_for_each_entry(arvif, &ar->arvifs, list) { 4055fd3ac3cSJanusz Dziedzic ret = ath10k_vif_wow_set_wakeups(arvif, wowlan); 4065fd3ac3cSJanusz Dziedzic if (ret) { 4075fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n", 4085fd3ac3cSJanusz Dziedzic arvif->vdev_id, ret); 4095fd3ac3cSJanusz Dziedzic return ret; 4105fd3ac3cSJanusz Dziedzic } 4115fd3ac3cSJanusz Dziedzic } 4125fd3ac3cSJanusz Dziedzic 4135fd3ac3cSJanusz Dziedzic return 0; 4145fd3ac3cSJanusz Dziedzic } 4155fd3ac3cSJanusz Dziedzic 416ce834e28SWen Gong static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif) 417ce834e28SWen Gong { 418ce834e28SWen Gong int ret = 0; 419ce834e28SWen Gong struct ath10k *ar = arvif->ar; 420ce834e28SWen Gong 421ce834e28SWen Gong switch (arvif->vdev_type) { 422ce834e28SWen Gong case WMI_VDEV_TYPE_STA: 423ce834e28SWen Gong if (ar->nlo_enabled) { 424ce834e28SWen Gong struct wmi_pno_scan_req *pno; 425ce834e28SWen Gong 426ce834e28SWen Gong pno = kzalloc(sizeof(*pno), GFP_KERNEL); 427ce834e28SWen Gong if (!pno) 428ce834e28SWen Gong return -ENOMEM; 429ce834e28SWen Gong 430ce834e28SWen Gong pno->enable = 0; 431ce834e28SWen Gong ar->nlo_enabled = false; 432ce834e28SWen Gong ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 433ce834e28SWen Gong kfree(pno); 434ce834e28SWen Gong } 435ce834e28SWen Gong break; 436ce834e28SWen Gong default: 437ce834e28SWen Gong break; 438ce834e28SWen Gong } 439ce834e28SWen Gong return ret; 440ce834e28SWen Gong } 441ce834e28SWen Gong 442ce834e28SWen Gong static int ath10k_wow_nlo_cleanup(struct ath10k *ar) 443ce834e28SWen Gong { 444ce834e28SWen Gong struct ath10k_vif *arvif; 445ce834e28SWen Gong int ret = 0; 446ce834e28SWen Gong 447ce834e28SWen Gong lockdep_assert_held(&ar->conf_mutex); 448ce834e28SWen Gong 449ce834e28SWen Gong list_for_each_entry(arvif, &ar->arvifs, list) { 450ce834e28SWen Gong ret = ath10k_vif_wow_clean_nlo(arvif); 451ce834e28SWen Gong if (ret) { 452ce834e28SWen Gong ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n", 453ce834e28SWen Gong arvif->vdev_id, ret); 454ce834e28SWen Gong return ret; 455ce834e28SWen Gong } 456ce834e28SWen Gong } 457ce834e28SWen Gong 458ce834e28SWen Gong return 0; 459ce834e28SWen Gong } 460ce834e28SWen Gong 4615fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar) 4625fd3ac3cSJanusz Dziedzic { 4635fd3ac3cSJanusz Dziedzic int ret; 4645fd3ac3cSJanusz Dziedzic 4655fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 4665fd3ac3cSJanusz Dziedzic 4675fd3ac3cSJanusz Dziedzic reinit_completion(&ar->target_suspend); 4685fd3ac3cSJanusz Dziedzic 4695fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_enable(ar); 4705fd3ac3cSJanusz Dziedzic if (ret) { 4715fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to issue wow enable: %d\n", ret); 4725fd3ac3cSJanusz Dziedzic return ret; 4735fd3ac3cSJanusz Dziedzic } 4745fd3ac3cSJanusz Dziedzic 4755fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ); 4765fd3ac3cSJanusz Dziedzic if (ret == 0) { 4775fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for suspend completion\n"); 4785fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 4795fd3ac3cSJanusz Dziedzic } 4805fd3ac3cSJanusz Dziedzic 4815fd3ac3cSJanusz Dziedzic return 0; 4825fd3ac3cSJanusz Dziedzic } 4835fd3ac3cSJanusz Dziedzic 4845fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar) 4855fd3ac3cSJanusz Dziedzic { 4865fd3ac3cSJanusz Dziedzic int ret; 4875fd3ac3cSJanusz Dziedzic 4885fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 4895fd3ac3cSJanusz Dziedzic 4905fd3ac3cSJanusz Dziedzic reinit_completion(&ar->wow.wakeup_completed); 4915fd3ac3cSJanusz Dziedzic 4925fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_host_wakeup_ind(ar); 4935fd3ac3cSJanusz Dziedzic if (ret) { 4945fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to send wow wakeup indication: %d\n", 4955fd3ac3cSJanusz Dziedzic ret); 4965fd3ac3cSJanusz Dziedzic return ret; 4975fd3ac3cSJanusz Dziedzic } 4985fd3ac3cSJanusz Dziedzic 4995fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ); 5005fd3ac3cSJanusz Dziedzic if (ret == 0) { 5015fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n"); 5025fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 5035fd3ac3cSJanusz Dziedzic } 5045fd3ac3cSJanusz Dziedzic 5055fd3ac3cSJanusz Dziedzic return 0; 5065fd3ac3cSJanusz Dziedzic } 5075fd3ac3cSJanusz Dziedzic 5085fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw, 5095fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 5105fd3ac3cSJanusz Dziedzic { 5115fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 5125fd3ac3cSJanusz Dziedzic int ret; 5135fd3ac3cSJanusz Dziedzic 5145fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 5155fd3ac3cSJanusz Dziedzic 5165fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 517c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features))) { 5185fd3ac3cSJanusz Dziedzic ret = 1; 5195fd3ac3cSJanusz Dziedzic goto exit; 5205fd3ac3cSJanusz Dziedzic } 5215fd3ac3cSJanusz Dziedzic 5225fd3ac3cSJanusz Dziedzic ret = ath10k_wow_cleanup(ar); 5235fd3ac3cSJanusz Dziedzic if (ret) { 5245fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", 5255fd3ac3cSJanusz Dziedzic ret); 5265fd3ac3cSJanusz Dziedzic goto exit; 5275fd3ac3cSJanusz Dziedzic } 5285fd3ac3cSJanusz Dziedzic 5295fd3ac3cSJanusz Dziedzic ret = ath10k_wow_set_wakeups(ar, wowlan); 5305fd3ac3cSJanusz Dziedzic if (ret) { 5315fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeup events: %d\n", 5325fd3ac3cSJanusz Dziedzic ret); 5335fd3ac3cSJanusz Dziedzic goto cleanup; 5345fd3ac3cSJanusz Dziedzic } 5355fd3ac3cSJanusz Dziedzic 536828853acSWen Gong ath10k_mac_wait_tx_complete(ar); 537828853acSWen Gong 5385fd3ac3cSJanusz Dziedzic ret = ath10k_wow_enable(ar); 5395fd3ac3cSJanusz Dziedzic if (ret) { 5405fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to start wow: %d\n", ret); 5415fd3ac3cSJanusz Dziedzic goto cleanup; 5425fd3ac3cSJanusz Dziedzic } 5435fd3ac3cSJanusz Dziedzic 5445fd3ac3cSJanusz Dziedzic ret = ath10k_hif_suspend(ar); 5455fd3ac3cSJanusz Dziedzic if (ret) { 5465fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to suspend hif: %d\n", ret); 5475fd3ac3cSJanusz Dziedzic goto wakeup; 5485fd3ac3cSJanusz Dziedzic } 5495fd3ac3cSJanusz Dziedzic 5505fd3ac3cSJanusz Dziedzic goto exit; 5515fd3ac3cSJanusz Dziedzic 5525fd3ac3cSJanusz Dziedzic wakeup: 5535fd3ac3cSJanusz Dziedzic ath10k_wow_wakeup(ar); 5545fd3ac3cSJanusz Dziedzic 5555fd3ac3cSJanusz Dziedzic cleanup: 5565fd3ac3cSJanusz Dziedzic ath10k_wow_cleanup(ar); 5575fd3ac3cSJanusz Dziedzic 5585fd3ac3cSJanusz Dziedzic exit: 5595fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 5605fd3ac3cSJanusz Dziedzic return ret ? 1 : 0; 5615fd3ac3cSJanusz Dziedzic } 5625fd3ac3cSJanusz Dziedzic 563393b706cSRyan Hsu void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) 564393b706cSRyan Hsu { 565393b706cSRyan Hsu struct ath10k *ar = hw->priv; 566393b706cSRyan Hsu 567393b706cSRyan Hsu mutex_lock(&ar->conf_mutex); 568393b706cSRyan Hsu if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 569393b706cSRyan Hsu ar->running_fw->fw_file.fw_features)) { 570393b706cSRyan Hsu device_set_wakeup_enable(ar->dev, enabled); 571393b706cSRyan Hsu } 572393b706cSRyan Hsu mutex_unlock(&ar->conf_mutex); 573393b706cSRyan Hsu } 574393b706cSRyan Hsu 5755fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw) 5765fd3ac3cSJanusz Dziedzic { 5775fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 5785fd3ac3cSJanusz Dziedzic int ret; 5795fd3ac3cSJanusz Dziedzic 5805fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 5815fd3ac3cSJanusz Dziedzic 5825fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 583c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features))) { 5845fd3ac3cSJanusz Dziedzic ret = 1; 5855fd3ac3cSJanusz Dziedzic goto exit; 5865fd3ac3cSJanusz Dziedzic } 5875fd3ac3cSJanusz Dziedzic 5885fd3ac3cSJanusz Dziedzic ret = ath10k_hif_resume(ar); 5895fd3ac3cSJanusz Dziedzic if (ret) { 5905fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to resume hif: %d\n", ret); 5915fd3ac3cSJanusz Dziedzic goto exit; 5925fd3ac3cSJanusz Dziedzic } 5935fd3ac3cSJanusz Dziedzic 5945fd3ac3cSJanusz Dziedzic ret = ath10k_wow_wakeup(ar); 5955fd3ac3cSJanusz Dziedzic if (ret) 5965fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); 5975fd3ac3cSJanusz Dziedzic 598ce834e28SWen Gong ret = ath10k_wow_nlo_cleanup(ar); 599ce834e28SWen Gong if (ret) 600ce834e28SWen Gong ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret); 601ce834e28SWen Gong 6025fd3ac3cSJanusz Dziedzic exit: 6036f7429c2SMichal Kazior if (ret) { 6046f7429c2SMichal Kazior switch (ar->state) { 6056f7429c2SMichal Kazior case ATH10K_STATE_ON: 6066f7429c2SMichal Kazior ar->state = ATH10K_STATE_RESTARTING; 6076f7429c2SMichal Kazior ret = 1; 6086f7429c2SMichal Kazior break; 6096f7429c2SMichal Kazior case ATH10K_STATE_OFF: 6106f7429c2SMichal Kazior case ATH10K_STATE_RESTARTING: 6116f7429c2SMichal Kazior case ATH10K_STATE_RESTARTED: 6126f7429c2SMichal Kazior case ATH10K_STATE_UTF: 6136f7429c2SMichal Kazior case ATH10K_STATE_WEDGED: 6146f7429c2SMichal Kazior ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n", 6156f7429c2SMichal Kazior ar->state); 6166f7429c2SMichal Kazior ret = -EIO; 6176f7429c2SMichal Kazior break; 6186f7429c2SMichal Kazior } 6196f7429c2SMichal Kazior } 6206f7429c2SMichal Kazior 6215fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 6226f7429c2SMichal Kazior return ret; 6235fd3ac3cSJanusz Dziedzic } 6245fd3ac3cSJanusz Dziedzic 6255fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar) 6265fd3ac3cSJanusz Dziedzic { 627c4cdf753SKalle Valo if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 628c4cdf753SKalle Valo ar->running_fw->fw_file.fw_features)) 6295fd3ac3cSJanusz Dziedzic return 0; 6305fd3ac3cSJanusz Dziedzic 6315fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map))) 6325fd3ac3cSJanusz Dziedzic return -EINVAL; 6335fd3ac3cSJanusz Dziedzic 63425c86619SJanusz Dziedzic ar->wow.wowlan_support = ath10k_wowlan_support; 635fa3440faSWen Gong 636fa3440faSWen Gong if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 637fa3440faSWen Gong ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; 638fa3440faSWen Gong ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; 639fa3440faSWen Gong } 640fa3440faSWen Gong 641ce834e28SWen Gong if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) { 642ce834e28SWen Gong ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; 643ce834e28SWen Gong ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; 644ce834e28SWen Gong } 645ce834e28SWen Gong 64625c86619SJanusz Dziedzic ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; 64725c86619SJanusz Dziedzic ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; 6485fd3ac3cSJanusz Dziedzic 649393b706cSRyan Hsu device_set_wakeup_capable(ar->dev, true); 650393b706cSRyan Hsu 6515fd3ac3cSJanusz Dziedzic return 0; 6525fd3ac3cSJanusz Dziedzic } 653