162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015-2017 Qualcomm Atheros, Inc. 462306a36Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "mac.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <net/mac80211.h> 1062306a36Sopenharmony_ci#include "hif.h" 1162306a36Sopenharmony_ci#include "core.h" 1262306a36Sopenharmony_ci#include "debug.h" 1362306a36Sopenharmony_ci#include "wmi.h" 1462306a36Sopenharmony_ci#include "wmi-ops.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic const struct wiphy_wowlan_support ath10k_wowlan_support = { 1762306a36Sopenharmony_ci .flags = WIPHY_WOWLAN_DISCONNECT | 1862306a36Sopenharmony_ci WIPHY_WOWLAN_MAGIC_PKT, 1962306a36Sopenharmony_ci .pattern_min_len = WOW_MIN_PATTERN_SIZE, 2062306a36Sopenharmony_ci .pattern_max_len = WOW_MAX_PATTERN_SIZE, 2162306a36Sopenharmony_ci .max_pkt_offset = WOW_MAX_PKT_OFFSET, 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct ath10k *ar = arvif->ar; 2762306a36Sopenharmony_ci int i, ret; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci for (i = 0; i < WOW_EVENT_MAX; i++) { 3062306a36Sopenharmony_ci ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); 3162306a36Sopenharmony_ci if (ret) { 3262306a36Sopenharmony_ci ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n", 3362306a36Sopenharmony_ci wow_wakeup_event(i), arvif->vdev_id, ret); 3462306a36Sopenharmony_ci return ret; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci for (i = 0; i < ar->wow.max_num_patterns; i++) { 3962306a36Sopenharmony_ci ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); 4062306a36Sopenharmony_ci if (ret) { 4162306a36Sopenharmony_ci ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n", 4262306a36Sopenharmony_ci i, arvif->vdev_id, ret); 4362306a36Sopenharmony_ci return ret; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int ath10k_wow_cleanup(struct ath10k *ar) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct ath10k_vif *arvif; 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci list_for_each_entry(arvif, &ar->arvifs, list) { 5862306a36Sopenharmony_ci ret = ath10k_wow_vif_cleanup(arvif); 5962306a36Sopenharmony_ci if (ret) { 6062306a36Sopenharmony_ci ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n", 6162306a36Sopenharmony_ci arvif->vdev_id, ret); 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Convert a 802.3 format to a 802.11 format. 7162306a36Sopenharmony_ci * +------------+-----------+--------+----------------+ 7262306a36Sopenharmony_ci * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | 7362306a36Sopenharmony_ci * +------------+-----------+--------+----------------+ 7462306a36Sopenharmony_ci * |__ |_______ |____________ |________ 7562306a36Sopenharmony_ci * | | | | 7662306a36Sopenharmony_ci * +--+------------+----+-----------+---------------+-----------+ 7762306a36Sopenharmony_ci * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | 7862306a36Sopenharmony_ci * +--+------------+----+-----------+---------------+-----------+ 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, 8162306a36Sopenharmony_ci const struct cfg80211_pkt_pattern *old) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u8 hdr_8023_pattern[ETH_HLEN] = {}; 8462306a36Sopenharmony_ci u8 hdr_8023_bit_mask[ETH_HLEN] = {}; 8562306a36Sopenharmony_ci u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; 8662306a36Sopenharmony_ci u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci int total_len = old->pkt_offset + old->pattern_len; 8962306a36Sopenharmony_ci int hdr_80211_end_offset; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci struct ieee80211_hdr_3addr *new_hdr_pattern = 9262306a36Sopenharmony_ci (struct ieee80211_hdr_3addr *)hdr_80211_pattern; 9362306a36Sopenharmony_ci struct ieee80211_hdr_3addr *new_hdr_mask = 9462306a36Sopenharmony_ci (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; 9562306a36Sopenharmony_ci struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; 9662306a36Sopenharmony_ci struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; 9762306a36Sopenharmony_ci int hdr_len = sizeof(*new_hdr_pattern); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci struct rfc1042_hdr *new_rfc_pattern = 10062306a36Sopenharmony_ci (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); 10162306a36Sopenharmony_ci struct rfc1042_hdr *new_rfc_mask = 10262306a36Sopenharmony_ci (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); 10362306a36Sopenharmony_ci int rfc_len = sizeof(*new_rfc_pattern); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci memcpy(hdr_8023_pattern + old->pkt_offset, 10662306a36Sopenharmony_ci old->pattern, ETH_HLEN - old->pkt_offset); 10762306a36Sopenharmony_ci memcpy(hdr_8023_bit_mask + old->pkt_offset, 10862306a36Sopenharmony_ci old->mask, ETH_HLEN - old->pkt_offset); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* Copy destination address */ 11162306a36Sopenharmony_ci memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); 11262306a36Sopenharmony_ci memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Copy source address */ 11562306a36Sopenharmony_ci memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); 11662306a36Sopenharmony_ci memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Copy logic link type */ 11962306a36Sopenharmony_ci memcpy(&new_rfc_pattern->snap_type, 12062306a36Sopenharmony_ci &old_hdr_pattern->h_proto, 12162306a36Sopenharmony_ci sizeof(old_hdr_pattern->h_proto)); 12262306a36Sopenharmony_ci memcpy(&new_rfc_mask->snap_type, 12362306a36Sopenharmony_ci &old_hdr_mask->h_proto, 12462306a36Sopenharmony_ci sizeof(old_hdr_mask->h_proto)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Calculate new pkt_offset */ 12762306a36Sopenharmony_ci if (old->pkt_offset < ETH_ALEN) 12862306a36Sopenharmony_ci new->pkt_offset = old->pkt_offset + 12962306a36Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr1); 13062306a36Sopenharmony_ci else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) 13162306a36Sopenharmony_ci new->pkt_offset = old->pkt_offset + 13262306a36Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr3) - 13362306a36Sopenharmony_ci offsetof(struct ethhdr, h_source); 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Calculate new hdr end offset */ 13862306a36Sopenharmony_ci if (total_len > ETH_HLEN) 13962306a36Sopenharmony_ci hdr_80211_end_offset = hdr_len + rfc_len; 14062306a36Sopenharmony_ci else if (total_len > offsetof(struct ethhdr, h_proto)) 14162306a36Sopenharmony_ci hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; 14262306a36Sopenharmony_ci else if (total_len > ETH_ALEN) 14362306a36Sopenharmony_ci hdr_80211_end_offset = total_len - ETH_ALEN + 14462306a36Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr3); 14562306a36Sopenharmony_ci else 14662306a36Sopenharmony_ci hdr_80211_end_offset = total_len + 14762306a36Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr1); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci new->pattern_len = hdr_80211_end_offset - new->pkt_offset; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci memcpy((u8 *)new->pattern, 15262306a36Sopenharmony_ci hdr_80211_pattern + new->pkt_offset, 15362306a36Sopenharmony_ci new->pattern_len); 15462306a36Sopenharmony_ci memcpy((u8 *)new->mask, 15562306a36Sopenharmony_ci hdr_80211_bit_mask + new->pkt_offset, 15662306a36Sopenharmony_ci new->pattern_len); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (total_len > ETH_HLEN) { 15962306a36Sopenharmony_ci /* Copy frame body */ 16062306a36Sopenharmony_ci memcpy((u8 *)new->pattern + new->pattern_len, 16162306a36Sopenharmony_ci (void *)old->pattern + ETH_HLEN - old->pkt_offset, 16262306a36Sopenharmony_ci total_len - ETH_HLEN); 16362306a36Sopenharmony_ci memcpy((u8 *)new->mask + new->pattern_len, 16462306a36Sopenharmony_ci (void *)old->mask + ETH_HLEN - old->pkt_offset, 16562306a36Sopenharmony_ci total_len - ETH_HLEN); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci new->pattern_len += total_len - ETH_HLEN; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id, 17262306a36Sopenharmony_ci struct cfg80211_sched_scan_request *nd_config, 17362306a36Sopenharmony_ci struct wmi_pno_scan_req *pno) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int i, j, ret = 0; 17662306a36Sopenharmony_ci u8 ssid_len; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci pno->enable = 1; 17962306a36Sopenharmony_ci pno->vdev_id = vdev_id; 18062306a36Sopenharmony_ci pno->uc_networks_count = nd_config->n_match_sets; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!pno->uc_networks_count || 18362306a36Sopenharmony_ci pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) 18462306a36Sopenharmony_ci return -EINVAL; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Filling per profile params */ 19062306a36Sopenharmony_ci for (i = 0; i < pno->uc_networks_count; i++) { 19162306a36Sopenharmony_ci ssid_len = nd_config->match_sets[i].ssid.ssid_len; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (ssid_len == 0 || ssid_len > 32) 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci memcpy(pno->a_networks[i].ssid.ssid, 19962306a36Sopenharmony_ci nd_config->match_sets[i].ssid.ssid, 20062306a36Sopenharmony_ci nd_config->match_sets[i].ssid.ssid_len); 20162306a36Sopenharmony_ci pno->a_networks[i].authentication = 0; 20262306a36Sopenharmony_ci pno->a_networks[i].encryption = 0; 20362306a36Sopenharmony_ci pno->a_networks[i].bcast_nw_type = 0; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /*Copying list of valid channel into request */ 20662306a36Sopenharmony_ci pno->a_networks[i].channel_count = nd_config->n_channels; 20762306a36Sopenharmony_ci pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (j = 0; j < nd_config->n_channels; j++) { 21062306a36Sopenharmony_ci pno->a_networks[i].channels[j] = 21162306a36Sopenharmony_ci nd_config->channels[j]->center_freq; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* set scan to passive if no SSIDs are specified in the request */ 21662306a36Sopenharmony_ci if (nd_config->n_ssids == 0) 21762306a36Sopenharmony_ci pno->do_passive_scan = true; 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci pno->do_passive_scan = false; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci for (i = 0; i < nd_config->n_ssids; i++) { 22262306a36Sopenharmony_ci j = 0; 22362306a36Sopenharmony_ci while (j < pno->uc_networks_count) { 22462306a36Sopenharmony_ci if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) == 22562306a36Sopenharmony_ci nd_config->ssids[i].ssid_len && 22662306a36Sopenharmony_ci (memcmp(pno->a_networks[j].ssid.ssid, 22762306a36Sopenharmony_ci nd_config->ssids[i].ssid, 22862306a36Sopenharmony_ci __le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) { 22962306a36Sopenharmony_ci pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci j++; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (nd_config->n_scan_plans == 2) { 23762306a36Sopenharmony_ci pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 23862306a36Sopenharmony_ci pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; 23962306a36Sopenharmony_ci pno->slow_scan_period = 24062306a36Sopenharmony_ci nd_config->scan_plans[1].interval * MSEC_PER_SEC; 24162306a36Sopenharmony_ci } else if (nd_config->n_scan_plans == 1) { 24262306a36Sopenharmony_ci pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 24362306a36Sopenharmony_ci pno->fast_scan_max_cycles = 1; 24462306a36Sopenharmony_ci pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 24562306a36Sopenharmony_ci } else { 24662306a36Sopenharmony_ci ath10k_warn(ar, "Invalid number of scan plans %d !!", 24762306a36Sopenharmony_ci nd_config->n_scan_plans); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 25162306a36Sopenharmony_ci /* enable mac randomization */ 25262306a36Sopenharmony_ci pno->enable_pno_scan_randomization = 1; 25362306a36Sopenharmony_ci memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); 25462306a36Sopenharmony_ci memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci pno->delay_start_time = nd_config->delay; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Current FW does not support min-max range for dwell time */ 26062306a36Sopenharmony_ci pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; 26162306a36Sopenharmony_ci pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, 26662306a36Sopenharmony_ci struct cfg80211_wowlan *wowlan) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int ret, i; 26962306a36Sopenharmony_ci unsigned long wow_mask = 0; 27062306a36Sopenharmony_ci struct ath10k *ar = arvif->ar; 27162306a36Sopenharmony_ci const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 27262306a36Sopenharmony_ci int pattern_id = 0; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Setup requested WOW features */ 27562306a36Sopenharmony_ci switch (arvif->vdev_type) { 27662306a36Sopenharmony_ci case WMI_VDEV_TYPE_IBSS: 27762306a36Sopenharmony_ci __set_bit(WOW_BEACON_EVENT, &wow_mask); 27862306a36Sopenharmony_ci fallthrough; 27962306a36Sopenharmony_ci case WMI_VDEV_TYPE_AP: 28062306a36Sopenharmony_ci __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 28162306a36Sopenharmony_ci __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 28262306a36Sopenharmony_ci __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 28362306a36Sopenharmony_ci __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 28462306a36Sopenharmony_ci __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 28562306a36Sopenharmony_ci __set_bit(WOW_HTT_EVENT, &wow_mask); 28662306a36Sopenharmony_ci __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci case WMI_VDEV_TYPE_STA: 28962306a36Sopenharmony_ci if (wowlan->disconnect) { 29062306a36Sopenharmony_ci __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 29162306a36Sopenharmony_ci __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 29262306a36Sopenharmony_ci __set_bit(WOW_BMISS_EVENT, &wow_mask); 29362306a36Sopenharmony_ci __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (wowlan->magic_pkt) 29762306a36Sopenharmony_ci __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (wowlan->nd_config) { 30062306a36Sopenharmony_ci struct wmi_pno_scan_req *pno; 30162306a36Sopenharmony_ci int ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci pno = kzalloc(sizeof(*pno), GFP_KERNEL); 30462306a36Sopenharmony_ci if (!pno) 30562306a36Sopenharmony_ci return -ENOMEM; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci ar->nlo_enabled = true; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = ath10k_wmi_pno_check(ar, arvif->vdev_id, 31062306a36Sopenharmony_ci wowlan->nd_config, pno); 31162306a36Sopenharmony_ci if (!ret) { 31262306a36Sopenharmony_ci ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 31362306a36Sopenharmony_ci __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci kfree(pno); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci default: 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for (i = 0; i < wowlan->n_patterns; i++) { 32462306a36Sopenharmony_ci u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; 32562306a36Sopenharmony_ci u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; 32662306a36Sopenharmony_ci u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; 32762306a36Sopenharmony_ci struct cfg80211_pkt_pattern new_pattern = {}; 32862306a36Sopenharmony_ci struct cfg80211_pkt_pattern old_pattern = patterns[i]; 32962306a36Sopenharmony_ci int j; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci new_pattern.pattern = ath_pattern; 33262306a36Sopenharmony_ci new_pattern.mask = ath_bitmask; 33362306a36Sopenharmony_ci if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) 33462306a36Sopenharmony_ci continue; 33562306a36Sopenharmony_ci /* convert bytemask to bitmask */ 33662306a36Sopenharmony_ci for (j = 0; j < patterns[i].pattern_len; j++) 33762306a36Sopenharmony_ci if (patterns[i].mask[j / 8] & BIT(j % 8)) 33862306a36Sopenharmony_ci bitmask[j] = 0xff; 33962306a36Sopenharmony_ci old_pattern.mask = bitmask; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 34262306a36Sopenharmony_ci if (patterns[i].pkt_offset < ETH_HLEN) { 34362306a36Sopenharmony_ci ath10k_wow_convert_8023_to_80211(&new_pattern, 34462306a36Sopenharmony_ci &old_pattern); 34562306a36Sopenharmony_ci } else { 34662306a36Sopenharmony_ci new_pattern = old_pattern; 34762306a36Sopenharmony_ci new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id, 35562306a36Sopenharmony_ci pattern_id, 35662306a36Sopenharmony_ci new_pattern.pattern, 35762306a36Sopenharmony_ci new_pattern.mask, 35862306a36Sopenharmony_ci new_pattern.pattern_len, 35962306a36Sopenharmony_ci new_pattern.pkt_offset); 36062306a36Sopenharmony_ci if (ret) { 36162306a36Sopenharmony_ci ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n", 36262306a36Sopenharmony_ci pattern_id, 36362306a36Sopenharmony_ci arvif->vdev_id, ret); 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci pattern_id++; 36862306a36Sopenharmony_ci __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci for (i = 0; i < WOW_EVENT_MAX; i++) { 37262306a36Sopenharmony_ci if (!test_bit(i, &wow_mask)) 37362306a36Sopenharmony_ci continue; 37462306a36Sopenharmony_ci ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 37562306a36Sopenharmony_ci if (ret) { 37662306a36Sopenharmony_ci ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n", 37762306a36Sopenharmony_ci wow_wakeup_event(i), arvif->vdev_id, ret); 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int ath10k_wow_set_wakeups(struct ath10k *ar, 38662306a36Sopenharmony_ci struct cfg80211_wowlan *wowlan) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct ath10k_vif *arvif; 38962306a36Sopenharmony_ci int ret; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci list_for_each_entry(arvif, &ar->arvifs, list) { 39462306a36Sopenharmony_ci ret = ath10k_vif_wow_set_wakeups(arvif, wowlan); 39562306a36Sopenharmony_ci if (ret) { 39662306a36Sopenharmony_ci ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n", 39762306a36Sopenharmony_ci arvif->vdev_id, ret); 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci int ret = 0; 40862306a36Sopenharmony_ci struct ath10k *ar = arvif->ar; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci switch (arvif->vdev_type) { 41162306a36Sopenharmony_ci case WMI_VDEV_TYPE_STA: 41262306a36Sopenharmony_ci if (ar->nlo_enabled) { 41362306a36Sopenharmony_ci struct wmi_pno_scan_req *pno; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pno = kzalloc(sizeof(*pno), GFP_KERNEL); 41662306a36Sopenharmony_ci if (!pno) 41762306a36Sopenharmony_ci return -ENOMEM; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci pno->enable = 0; 42062306a36Sopenharmony_ci ar->nlo_enabled = false; 42162306a36Sopenharmony_ci ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 42262306a36Sopenharmony_ci kfree(pno); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci default: 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci return ret; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int ath10k_wow_nlo_cleanup(struct ath10k *ar) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct ath10k_vif *arvif; 43462306a36Sopenharmony_ci int ret = 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci list_for_each_entry(arvif, &ar->arvifs, list) { 43962306a36Sopenharmony_ci ret = ath10k_vif_wow_clean_nlo(arvif); 44062306a36Sopenharmony_ci if (ret) { 44162306a36Sopenharmony_ci ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n", 44262306a36Sopenharmony_ci arvif->vdev_id, ret); 44362306a36Sopenharmony_ci return ret; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int ath10k_wow_enable(struct ath10k *ar) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci int ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci reinit_completion(&ar->target_suspend); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci ret = ath10k_wmi_wow_enable(ar); 45962306a36Sopenharmony_ci if (ret) { 46062306a36Sopenharmony_ci ath10k_warn(ar, "failed to issue wow enable: %d\n", ret); 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ); 46562306a36Sopenharmony_ci if (ret == 0) { 46662306a36Sopenharmony_ci ath10k_warn(ar, "timed out while waiting for suspend completion\n"); 46762306a36Sopenharmony_ci return -ETIMEDOUT; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int ath10k_wow_wakeup(struct ath10k *ar) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci int ret; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci reinit_completion(&ar->wow.wakeup_completed); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ret = ath10k_wmi_wow_host_wakeup_ind(ar); 48262306a36Sopenharmony_ci if (ret) { 48362306a36Sopenharmony_ci ath10k_warn(ar, "failed to send wow wakeup indication: %d\n", 48462306a36Sopenharmony_ci ret); 48562306a36Sopenharmony_ci return ret; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ); 48962306a36Sopenharmony_ci if (ret == 0) { 49062306a36Sopenharmony_ci ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n"); 49162306a36Sopenharmony_ci return -ETIMEDOUT; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciint ath10k_wow_op_suspend(struct ieee80211_hw *hw, 49862306a36Sopenharmony_ci struct cfg80211_wowlan *wowlan) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct ath10k *ar = hw->priv; 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci mutex_lock(&ar->conf_mutex); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 50662306a36Sopenharmony_ci ar->running_fw->fw_file.fw_features))) { 50762306a36Sopenharmony_ci ret = 1; 50862306a36Sopenharmony_ci goto exit; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = ath10k_wow_cleanup(ar); 51262306a36Sopenharmony_ci if (ret) { 51362306a36Sopenharmony_ci ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", 51462306a36Sopenharmony_ci ret); 51562306a36Sopenharmony_ci goto exit; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci ret = ath10k_wow_set_wakeups(ar, wowlan); 51962306a36Sopenharmony_ci if (ret) { 52062306a36Sopenharmony_ci ath10k_warn(ar, "failed to set wow wakeup events: %d\n", 52162306a36Sopenharmony_ci ret); 52262306a36Sopenharmony_ci goto cleanup; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ath10k_mac_wait_tx_complete(ar); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ret = ath10k_wow_enable(ar); 52862306a36Sopenharmony_ci if (ret) { 52962306a36Sopenharmony_ci ath10k_warn(ar, "failed to start wow: %d\n", ret); 53062306a36Sopenharmony_ci goto cleanup; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci ret = ath10k_hif_suspend(ar); 53462306a36Sopenharmony_ci if (ret) { 53562306a36Sopenharmony_ci ath10k_warn(ar, "failed to suspend hif: %d\n", ret); 53662306a36Sopenharmony_ci goto wakeup; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci goto exit; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ciwakeup: 54262306a36Sopenharmony_ci ath10k_wow_wakeup(ar); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cicleanup: 54562306a36Sopenharmony_ci ath10k_wow_cleanup(ar); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciexit: 54862306a36Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 54962306a36Sopenharmony_ci return ret ? 1 : 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_civoid ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct ath10k *ar = hw->priv; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci mutex_lock(&ar->conf_mutex); 55762306a36Sopenharmony_ci if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 55862306a36Sopenharmony_ci ar->running_fw->fw_file.fw_features)) { 55962306a36Sopenharmony_ci device_set_wakeup_enable(ar->dev, enabled); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ciint ath10k_wow_op_resume(struct ieee80211_hw *hw) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct ath10k *ar = hw->priv; 56762306a36Sopenharmony_ci int ret; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci mutex_lock(&ar->conf_mutex); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 57262306a36Sopenharmony_ci ar->running_fw->fw_file.fw_features))) { 57362306a36Sopenharmony_ci ret = 1; 57462306a36Sopenharmony_ci goto exit; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci ret = ath10k_hif_resume(ar); 57862306a36Sopenharmony_ci if (ret) { 57962306a36Sopenharmony_ci ath10k_warn(ar, "failed to resume hif: %d\n", ret); 58062306a36Sopenharmony_ci goto exit; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci ret = ath10k_wow_wakeup(ar); 58462306a36Sopenharmony_ci if (ret) 58562306a36Sopenharmony_ci ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci ret = ath10k_wow_nlo_cleanup(ar); 58862306a36Sopenharmony_ci if (ret) 58962306a36Sopenharmony_ci ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ciexit: 59262306a36Sopenharmony_ci if (ret) { 59362306a36Sopenharmony_ci switch (ar->state) { 59462306a36Sopenharmony_ci case ATH10K_STATE_ON: 59562306a36Sopenharmony_ci ar->state = ATH10K_STATE_RESTARTING; 59662306a36Sopenharmony_ci ret = 1; 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci case ATH10K_STATE_OFF: 59962306a36Sopenharmony_ci case ATH10K_STATE_RESTARTING: 60062306a36Sopenharmony_ci case ATH10K_STATE_RESTARTED: 60162306a36Sopenharmony_ci case ATH10K_STATE_UTF: 60262306a36Sopenharmony_ci case ATH10K_STATE_WEDGED: 60362306a36Sopenharmony_ci ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n", 60462306a36Sopenharmony_ci ar->state); 60562306a36Sopenharmony_ci ret = -EIO; 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ciint ath10k_wow_init(struct ath10k *ar) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 61762306a36Sopenharmony_ci ar->running_fw->fw_file.fw_features)) 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map))) 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ar->wow.wowlan_support = ath10k_wowlan_support; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 62662306a36Sopenharmony_ci ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; 62762306a36Sopenharmony_ci ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) { 63162306a36Sopenharmony_ci ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; 63262306a36Sopenharmony_ci ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; 63662306a36Sopenharmony_ci ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci device_set_wakeup_capable(ar->dev, true); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 642