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