18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015-2017 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "mac.h" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <net/mac80211.h> 108c2ecf20Sopenharmony_ci#include "hif.h" 118c2ecf20Sopenharmony_ci#include "core.h" 128c2ecf20Sopenharmony_ci#include "debug.h" 138c2ecf20Sopenharmony_ci#include "wmi.h" 148c2ecf20Sopenharmony_ci#include "wmi-ops.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic const struct wiphy_wowlan_support ath10k_wowlan_support = { 178c2ecf20Sopenharmony_ci .flags = WIPHY_WOWLAN_DISCONNECT | 188c2ecf20Sopenharmony_ci WIPHY_WOWLAN_MAGIC_PKT, 198c2ecf20Sopenharmony_ci .pattern_min_len = WOW_MIN_PATTERN_SIZE, 208c2ecf20Sopenharmony_ci .pattern_max_len = WOW_MAX_PATTERN_SIZE, 218c2ecf20Sopenharmony_ci .max_pkt_offset = WOW_MAX_PKT_OFFSET, 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct ath10k *ar = arvif->ar; 278c2ecf20Sopenharmony_ci int i, ret; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci for (i = 0; i < WOW_EVENT_MAX; i++) { 308c2ecf20Sopenharmony_ci ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); 318c2ecf20Sopenharmony_ci if (ret) { 328c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n", 338c2ecf20Sopenharmony_ci wow_wakeup_event(i), arvif->vdev_id, ret); 348c2ecf20Sopenharmony_ci return ret; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci for (i = 0; i < ar->wow.max_num_patterns; i++) { 398c2ecf20Sopenharmony_ci ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); 408c2ecf20Sopenharmony_ci if (ret) { 418c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n", 428c2ecf20Sopenharmony_ci i, arvif->vdev_id, ret); 438c2ecf20Sopenharmony_ci return ret; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int ath10k_wow_cleanup(struct ath10k *ar) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct ath10k_vif *arvif; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci list_for_each_entry(arvif, &ar->arvifs, list) { 588c2ecf20Sopenharmony_ci ret = ath10k_wow_vif_cleanup(arvif); 598c2ecf20Sopenharmony_ci if (ret) { 608c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n", 618c2ecf20Sopenharmony_ci arvif->vdev_id, ret); 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Convert a 802.3 format to a 802.11 format. 718c2ecf20Sopenharmony_ci * +------------+-----------+--------+----------------+ 728c2ecf20Sopenharmony_ci * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | 738c2ecf20Sopenharmony_ci * +------------+-----------+--------+----------------+ 748c2ecf20Sopenharmony_ci * |__ |_______ |____________ |________ 758c2ecf20Sopenharmony_ci * | | | | 768c2ecf20Sopenharmony_ci * +--+------------+----+-----------+---------------+-----------+ 778c2ecf20Sopenharmony_ci * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | 788c2ecf20Sopenharmony_ci * +--+------------+----+-----------+---------------+-----------+ 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, 818c2ecf20Sopenharmony_ci const struct cfg80211_pkt_pattern *old) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u8 hdr_8023_pattern[ETH_HLEN] = {}; 848c2ecf20Sopenharmony_ci u8 hdr_8023_bit_mask[ETH_HLEN] = {}; 858c2ecf20Sopenharmony_ci u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; 868c2ecf20Sopenharmony_ci u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci int total_len = old->pkt_offset + old->pattern_len; 898c2ecf20Sopenharmony_ci int hdr_80211_end_offset; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci struct ieee80211_hdr_3addr *new_hdr_pattern = 928c2ecf20Sopenharmony_ci (struct ieee80211_hdr_3addr *)hdr_80211_pattern; 938c2ecf20Sopenharmony_ci struct ieee80211_hdr_3addr *new_hdr_mask = 948c2ecf20Sopenharmony_ci (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; 958c2ecf20Sopenharmony_ci struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; 968c2ecf20Sopenharmony_ci struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; 978c2ecf20Sopenharmony_ci int hdr_len = sizeof(*new_hdr_pattern); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci struct rfc1042_hdr *new_rfc_pattern = 1008c2ecf20Sopenharmony_ci (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); 1018c2ecf20Sopenharmony_ci struct rfc1042_hdr *new_rfc_mask = 1028c2ecf20Sopenharmony_ci (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); 1038c2ecf20Sopenharmony_ci int rfc_len = sizeof(*new_rfc_pattern); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci memcpy(hdr_8023_pattern + old->pkt_offset, 1068c2ecf20Sopenharmony_ci old->pattern, ETH_HLEN - old->pkt_offset); 1078c2ecf20Sopenharmony_ci memcpy(hdr_8023_bit_mask + old->pkt_offset, 1088c2ecf20Sopenharmony_ci old->mask, ETH_HLEN - old->pkt_offset); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Copy destination address */ 1118c2ecf20Sopenharmony_ci memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); 1128c2ecf20Sopenharmony_ci memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Copy source address */ 1158c2ecf20Sopenharmony_ci memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); 1168c2ecf20Sopenharmony_ci memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* Copy logic link type */ 1198c2ecf20Sopenharmony_ci memcpy(&new_rfc_pattern->snap_type, 1208c2ecf20Sopenharmony_ci &old_hdr_pattern->h_proto, 1218c2ecf20Sopenharmony_ci sizeof(old_hdr_pattern->h_proto)); 1228c2ecf20Sopenharmony_ci memcpy(&new_rfc_mask->snap_type, 1238c2ecf20Sopenharmony_ci &old_hdr_mask->h_proto, 1248c2ecf20Sopenharmony_ci sizeof(old_hdr_mask->h_proto)); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Calculate new pkt_offset */ 1278c2ecf20Sopenharmony_ci if (old->pkt_offset < ETH_ALEN) 1288c2ecf20Sopenharmony_ci new->pkt_offset = old->pkt_offset + 1298c2ecf20Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr1); 1308c2ecf20Sopenharmony_ci else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) 1318c2ecf20Sopenharmony_ci new->pkt_offset = old->pkt_offset + 1328c2ecf20Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr3) - 1338c2ecf20Sopenharmony_ci offsetof(struct ethhdr, h_source); 1348c2ecf20Sopenharmony_ci else 1358c2ecf20Sopenharmony_ci new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Calculate new hdr end offset */ 1388c2ecf20Sopenharmony_ci if (total_len > ETH_HLEN) 1398c2ecf20Sopenharmony_ci hdr_80211_end_offset = hdr_len + rfc_len; 1408c2ecf20Sopenharmony_ci else if (total_len > offsetof(struct ethhdr, h_proto)) 1418c2ecf20Sopenharmony_ci hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; 1428c2ecf20Sopenharmony_ci else if (total_len > ETH_ALEN) 1438c2ecf20Sopenharmony_ci hdr_80211_end_offset = total_len - ETH_ALEN + 1448c2ecf20Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr3); 1458c2ecf20Sopenharmony_ci else 1468c2ecf20Sopenharmony_ci hdr_80211_end_offset = total_len + 1478c2ecf20Sopenharmony_ci offsetof(struct ieee80211_hdr_3addr, addr1); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci new->pattern_len = hdr_80211_end_offset - new->pkt_offset; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci memcpy((u8 *)new->pattern, 1528c2ecf20Sopenharmony_ci hdr_80211_pattern + new->pkt_offset, 1538c2ecf20Sopenharmony_ci new->pattern_len); 1548c2ecf20Sopenharmony_ci memcpy((u8 *)new->mask, 1558c2ecf20Sopenharmony_ci hdr_80211_bit_mask + new->pkt_offset, 1568c2ecf20Sopenharmony_ci new->pattern_len); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (total_len > ETH_HLEN) { 1598c2ecf20Sopenharmony_ci /* Copy frame body */ 1608c2ecf20Sopenharmony_ci memcpy((u8 *)new->pattern + new->pattern_len, 1618c2ecf20Sopenharmony_ci (void *)old->pattern + ETH_HLEN - old->pkt_offset, 1628c2ecf20Sopenharmony_ci total_len - ETH_HLEN); 1638c2ecf20Sopenharmony_ci memcpy((u8 *)new->mask + new->pattern_len, 1648c2ecf20Sopenharmony_ci (void *)old->mask + ETH_HLEN - old->pkt_offset, 1658c2ecf20Sopenharmony_ci total_len - ETH_HLEN); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci new->pattern_len += total_len - ETH_HLEN; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id, 1728c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *nd_config, 1738c2ecf20Sopenharmony_ci struct wmi_pno_scan_req *pno) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int i, j, ret = 0; 1768c2ecf20Sopenharmony_ci u8 ssid_len; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci pno->enable = 1; 1798c2ecf20Sopenharmony_ci pno->vdev_id = vdev_id; 1808c2ecf20Sopenharmony_ci pno->uc_networks_count = nd_config->n_match_sets; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (!pno->uc_networks_count || 1838c2ecf20Sopenharmony_ci pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS) 1848c2ecf20Sopenharmony_ci return -EINVAL; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Filling per profile params */ 1908c2ecf20Sopenharmony_ci for (i = 0; i < pno->uc_networks_count; i++) { 1918c2ecf20Sopenharmony_ci ssid_len = nd_config->match_sets[i].ssid.ssid_len; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (ssid_len == 0 || ssid_len > 32) 1948c2ecf20Sopenharmony_ci return -EINVAL; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci memcpy(pno->a_networks[i].ssid.ssid, 1998c2ecf20Sopenharmony_ci nd_config->match_sets[i].ssid.ssid, 2008c2ecf20Sopenharmony_ci nd_config->match_sets[i].ssid.ssid_len); 2018c2ecf20Sopenharmony_ci pno->a_networks[i].authentication = 0; 2028c2ecf20Sopenharmony_ci pno->a_networks[i].encryption = 0; 2038c2ecf20Sopenharmony_ci pno->a_networks[i].bcast_nw_type = 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /*Copying list of valid channel into request */ 2068c2ecf20Sopenharmony_ci pno->a_networks[i].channel_count = nd_config->n_channels; 2078c2ecf20Sopenharmony_ci pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci for (j = 0; j < nd_config->n_channels; j++) { 2108c2ecf20Sopenharmony_ci pno->a_networks[i].channels[j] = 2118c2ecf20Sopenharmony_ci nd_config->channels[j]->center_freq; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* set scan to passive if no SSIDs are specified in the request */ 2168c2ecf20Sopenharmony_ci if (nd_config->n_ssids == 0) 2178c2ecf20Sopenharmony_ci pno->do_passive_scan = true; 2188c2ecf20Sopenharmony_ci else 2198c2ecf20Sopenharmony_ci pno->do_passive_scan = false; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci for (i = 0; i < nd_config->n_ssids; i++) { 2228c2ecf20Sopenharmony_ci j = 0; 2238c2ecf20Sopenharmony_ci while (j < pno->uc_networks_count) { 2248c2ecf20Sopenharmony_ci if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) == 2258c2ecf20Sopenharmony_ci nd_config->ssids[i].ssid_len && 2268c2ecf20Sopenharmony_ci (memcmp(pno->a_networks[j].ssid.ssid, 2278c2ecf20Sopenharmony_ci nd_config->ssids[i].ssid, 2288c2ecf20Sopenharmony_ci __le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) { 2298c2ecf20Sopenharmony_ci pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci j++; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (nd_config->n_scan_plans == 2) { 2378c2ecf20Sopenharmony_ci pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 2388c2ecf20Sopenharmony_ci pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations; 2398c2ecf20Sopenharmony_ci pno->slow_scan_period = 2408c2ecf20Sopenharmony_ci nd_config->scan_plans[1].interval * MSEC_PER_SEC; 2418c2ecf20Sopenharmony_ci } else if (nd_config->n_scan_plans == 1) { 2428c2ecf20Sopenharmony_ci pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 2438c2ecf20Sopenharmony_ci pno->fast_scan_max_cycles = 1; 2448c2ecf20Sopenharmony_ci pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC; 2458c2ecf20Sopenharmony_ci } else { 2468c2ecf20Sopenharmony_ci ath10k_warn(ar, "Invalid number of scan plans %d !!", 2478c2ecf20Sopenharmony_ci nd_config->n_scan_plans); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 2518c2ecf20Sopenharmony_ci /* enable mac randomization */ 2528c2ecf20Sopenharmony_ci pno->enable_pno_scan_randomization = 1; 2538c2ecf20Sopenharmony_ci memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN); 2548c2ecf20Sopenharmony_ci memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci pno->delay_start_time = nd_config->delay; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Current FW does not support min-max range for dwell time */ 2608c2ecf20Sopenharmony_ci pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME; 2618c2ecf20Sopenharmony_ci pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME; 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, 2668c2ecf20Sopenharmony_ci struct cfg80211_wowlan *wowlan) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int ret, i; 2698c2ecf20Sopenharmony_ci unsigned long wow_mask = 0; 2708c2ecf20Sopenharmony_ci struct ath10k *ar = arvif->ar; 2718c2ecf20Sopenharmony_ci const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 2728c2ecf20Sopenharmony_ci int pattern_id = 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Setup requested WOW features */ 2758c2ecf20Sopenharmony_ci switch (arvif->vdev_type) { 2768c2ecf20Sopenharmony_ci case WMI_VDEV_TYPE_IBSS: 2778c2ecf20Sopenharmony_ci __set_bit(WOW_BEACON_EVENT, &wow_mask); 2788c2ecf20Sopenharmony_ci fallthrough; 2798c2ecf20Sopenharmony_ci case WMI_VDEV_TYPE_AP: 2808c2ecf20Sopenharmony_ci __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 2818c2ecf20Sopenharmony_ci __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 2828c2ecf20Sopenharmony_ci __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 2838c2ecf20Sopenharmony_ci __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 2848c2ecf20Sopenharmony_ci __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 2858c2ecf20Sopenharmony_ci __set_bit(WOW_HTT_EVENT, &wow_mask); 2868c2ecf20Sopenharmony_ci __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci case WMI_VDEV_TYPE_STA: 2898c2ecf20Sopenharmony_ci if (wowlan->disconnect) { 2908c2ecf20Sopenharmony_ci __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 2918c2ecf20Sopenharmony_ci __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 2928c2ecf20Sopenharmony_ci __set_bit(WOW_BMISS_EVENT, &wow_mask); 2938c2ecf20Sopenharmony_ci __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (wowlan->magic_pkt) 2978c2ecf20Sopenharmony_ci __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (wowlan->nd_config) { 3008c2ecf20Sopenharmony_ci struct wmi_pno_scan_req *pno; 3018c2ecf20Sopenharmony_ci int ret; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci pno = kzalloc(sizeof(*pno), GFP_KERNEL); 3048c2ecf20Sopenharmony_ci if (!pno) 3058c2ecf20Sopenharmony_ci return -ENOMEM; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ar->nlo_enabled = true; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = ath10k_wmi_pno_check(ar, arvif->vdev_id, 3108c2ecf20Sopenharmony_ci wowlan->nd_config, pno); 3118c2ecf20Sopenharmony_ci if (!ret) { 3128c2ecf20Sopenharmony_ci ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 3138c2ecf20Sopenharmony_ci __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci kfree(pno); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci default: 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci for (i = 0; i < wowlan->n_patterns; i++) { 3248c2ecf20Sopenharmony_ci u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; 3258c2ecf20Sopenharmony_ci u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; 3268c2ecf20Sopenharmony_ci u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; 3278c2ecf20Sopenharmony_ci struct cfg80211_pkt_pattern new_pattern = {}; 3288c2ecf20Sopenharmony_ci struct cfg80211_pkt_pattern old_pattern = patterns[i]; 3298c2ecf20Sopenharmony_ci int j; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci new_pattern.pattern = ath_pattern; 3328c2ecf20Sopenharmony_ci new_pattern.mask = ath_bitmask; 3338c2ecf20Sopenharmony_ci if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) 3348c2ecf20Sopenharmony_ci continue; 3358c2ecf20Sopenharmony_ci /* convert bytemask to bitmask */ 3368c2ecf20Sopenharmony_ci for (j = 0; j < patterns[i].pattern_len; j++) 3378c2ecf20Sopenharmony_ci if (patterns[i].mask[j / 8] & BIT(j % 8)) 3388c2ecf20Sopenharmony_ci bitmask[j] = 0xff; 3398c2ecf20Sopenharmony_ci old_pattern.mask = bitmask; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 3428c2ecf20Sopenharmony_ci if (patterns[i].pkt_offset < ETH_HLEN) { 3438c2ecf20Sopenharmony_ci ath10k_wow_convert_8023_to_80211(&new_pattern, 3448c2ecf20Sopenharmony_ci &old_pattern); 3458c2ecf20Sopenharmony_ci } else { 3468c2ecf20Sopenharmony_ci new_pattern = old_pattern; 3478c2ecf20Sopenharmony_ci new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) 3528c2ecf20Sopenharmony_ci return -EINVAL; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id, 3558c2ecf20Sopenharmony_ci pattern_id, 3568c2ecf20Sopenharmony_ci new_pattern.pattern, 3578c2ecf20Sopenharmony_ci new_pattern.mask, 3588c2ecf20Sopenharmony_ci new_pattern.pattern_len, 3598c2ecf20Sopenharmony_ci new_pattern.pkt_offset); 3608c2ecf20Sopenharmony_ci if (ret) { 3618c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n", 3628c2ecf20Sopenharmony_ci pattern_id, 3638c2ecf20Sopenharmony_ci arvif->vdev_id, ret); 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci pattern_id++; 3688c2ecf20Sopenharmony_ci __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci for (i = 0; i < WOW_EVENT_MAX; i++) { 3728c2ecf20Sopenharmony_ci if (!test_bit(i, &wow_mask)) 3738c2ecf20Sopenharmony_ci continue; 3748c2ecf20Sopenharmony_ci ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 3758c2ecf20Sopenharmony_ci if (ret) { 3768c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n", 3778c2ecf20Sopenharmony_ci wow_wakeup_event(i), arvif->vdev_id, ret); 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int ath10k_wow_set_wakeups(struct ath10k *ar, 3868c2ecf20Sopenharmony_ci struct cfg80211_wowlan *wowlan) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct ath10k_vif *arvif; 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci list_for_each_entry(arvif, &ar->arvifs, list) { 3948c2ecf20Sopenharmony_ci ret = ath10k_vif_wow_set_wakeups(arvif, wowlan); 3958c2ecf20Sopenharmony_ci if (ret) { 3968c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n", 3978c2ecf20Sopenharmony_ci arvif->vdev_id, ret); 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci int ret = 0; 4088c2ecf20Sopenharmony_ci struct ath10k *ar = arvif->ar; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci switch (arvif->vdev_type) { 4118c2ecf20Sopenharmony_ci case WMI_VDEV_TYPE_STA: 4128c2ecf20Sopenharmony_ci if (ar->nlo_enabled) { 4138c2ecf20Sopenharmony_ci struct wmi_pno_scan_req *pno; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci pno = kzalloc(sizeof(*pno), GFP_KERNEL); 4168c2ecf20Sopenharmony_ci if (!pno) 4178c2ecf20Sopenharmony_ci return -ENOMEM; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci pno->enable = 0; 4208c2ecf20Sopenharmony_ci ar->nlo_enabled = false; 4218c2ecf20Sopenharmony_ci ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno); 4228c2ecf20Sopenharmony_ci kfree(pno); 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci default: 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci return ret; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int ath10k_wow_nlo_cleanup(struct ath10k *ar) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct ath10k_vif *arvif; 4348c2ecf20Sopenharmony_ci int ret = 0; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci list_for_each_entry(arvif, &ar->arvifs, list) { 4398c2ecf20Sopenharmony_ci ret = ath10k_vif_wow_clean_nlo(arvif); 4408c2ecf20Sopenharmony_ci if (ret) { 4418c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n", 4428c2ecf20Sopenharmony_ci arvif->vdev_id, ret); 4438c2ecf20Sopenharmony_ci return ret; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int ath10k_wow_enable(struct ath10k *ar) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci int ret; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci reinit_completion(&ar->target_suspend); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci ret = ath10k_wmi_wow_enable(ar); 4598c2ecf20Sopenharmony_ci if (ret) { 4608c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to issue wow enable: %d\n", ret); 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ); 4658c2ecf20Sopenharmony_ci if (ret == 0) { 4668c2ecf20Sopenharmony_ci ath10k_warn(ar, "timed out while waiting for suspend completion\n"); 4678c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int ath10k_wow_wakeup(struct ath10k *ar) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci int ret; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->conf_mutex); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci reinit_completion(&ar->wow.wakeup_completed); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci ret = ath10k_wmi_wow_host_wakeup_ind(ar); 4828c2ecf20Sopenharmony_ci if (ret) { 4838c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to send wow wakeup indication: %d\n", 4848c2ecf20Sopenharmony_ci ret); 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ); 4898c2ecf20Sopenharmony_ci if (ret == 0) { 4908c2ecf20Sopenharmony_ci ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n"); 4918c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ciint ath10k_wow_op_suspend(struct ieee80211_hw *hw, 4988c2ecf20Sopenharmony_ci struct cfg80211_wowlan *wowlan) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct ath10k *ar = hw->priv; 5018c2ecf20Sopenharmony_ci int ret; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 5068c2ecf20Sopenharmony_ci ar->running_fw->fw_file.fw_features))) { 5078c2ecf20Sopenharmony_ci ret = 1; 5088c2ecf20Sopenharmony_ci goto exit; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ret = ath10k_wow_cleanup(ar); 5128c2ecf20Sopenharmony_ci if (ret) { 5138c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", 5148c2ecf20Sopenharmony_ci ret); 5158c2ecf20Sopenharmony_ci goto exit; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci ret = ath10k_wow_set_wakeups(ar, wowlan); 5198c2ecf20Sopenharmony_ci if (ret) { 5208c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to set wow wakeup events: %d\n", 5218c2ecf20Sopenharmony_ci ret); 5228c2ecf20Sopenharmony_ci goto cleanup; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci ath10k_mac_wait_tx_complete(ar); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ret = ath10k_wow_enable(ar); 5288c2ecf20Sopenharmony_ci if (ret) { 5298c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to start wow: %d\n", ret); 5308c2ecf20Sopenharmony_ci goto cleanup; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = ath10k_hif_suspend(ar); 5348c2ecf20Sopenharmony_ci if (ret) { 5358c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to suspend hif: %d\n", ret); 5368c2ecf20Sopenharmony_ci goto wakeup; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci goto exit; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciwakeup: 5428c2ecf20Sopenharmony_ci ath10k_wow_wakeup(ar); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cicleanup: 5458c2ecf20Sopenharmony_ci ath10k_wow_cleanup(ar); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ciexit: 5488c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 5498c2ecf20Sopenharmony_ci return ret ? 1 : 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_civoid ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct ath10k *ar = hw->priv; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 5578c2ecf20Sopenharmony_ci if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 5588c2ecf20Sopenharmony_ci ar->running_fw->fw_file.fw_features)) { 5598c2ecf20Sopenharmony_ci device_set_wakeup_enable(ar->dev, enabled); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciint ath10k_wow_op_resume(struct ieee80211_hw *hw) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct ath10k *ar = hw->priv; 5678c2ecf20Sopenharmony_ci int ret; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci mutex_lock(&ar->conf_mutex); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 5728c2ecf20Sopenharmony_ci ar->running_fw->fw_file.fw_features))) { 5738c2ecf20Sopenharmony_ci ret = 1; 5748c2ecf20Sopenharmony_ci goto exit; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = ath10k_hif_resume(ar); 5788c2ecf20Sopenharmony_ci if (ret) { 5798c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to resume hif: %d\n", ret); 5808c2ecf20Sopenharmony_ci goto exit; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = ath10k_wow_wakeup(ar); 5848c2ecf20Sopenharmony_ci if (ret) 5858c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci ret = ath10k_wow_nlo_cleanup(ar); 5888c2ecf20Sopenharmony_ci if (ret) 5898c2ecf20Sopenharmony_ci ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ciexit: 5928c2ecf20Sopenharmony_ci if (ret) { 5938c2ecf20Sopenharmony_ci switch (ar->state) { 5948c2ecf20Sopenharmony_ci case ATH10K_STATE_ON: 5958c2ecf20Sopenharmony_ci ar->state = ATH10K_STATE_RESTARTING; 5968c2ecf20Sopenharmony_ci ret = 1; 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case ATH10K_STATE_OFF: 5998c2ecf20Sopenharmony_ci case ATH10K_STATE_RESTARTING: 6008c2ecf20Sopenharmony_ci case ATH10K_STATE_RESTARTED: 6018c2ecf20Sopenharmony_ci case ATH10K_STATE_UTF: 6028c2ecf20Sopenharmony_ci case ATH10K_STATE_WEDGED: 6038c2ecf20Sopenharmony_ci ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n", 6048c2ecf20Sopenharmony_ci ar->state); 6058c2ecf20Sopenharmony_ci ret = -EIO; 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci mutex_unlock(&ar->conf_mutex); 6118c2ecf20Sopenharmony_ci return ret; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciint ath10k_wow_init(struct ath10k *ar) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 6178c2ecf20Sopenharmony_ci ar->running_fw->fw_file.fw_features)) 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map))) 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci ar->wow.wowlan_support = ath10k_wowlan_support; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) { 6268c2ecf20Sopenharmony_ci ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; 6278c2ecf20Sopenharmony_ci ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) { 6318c2ecf20Sopenharmony_ci ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; 6328c2ecf20Sopenharmony_ci ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; 6368c2ecf20Sopenharmony_ci ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci device_set_wakeup_capable(ar->dev, true); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci} 642