643 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2015-2017 Qualcomm Atheros, Inc.4 * Copyright (c) 2018, The Linux Foundation. All rights reserved.5 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.6 */7 8#include "mac.h"9 10#include <net/mac80211.h>11#include "hif.h"12#include "core.h"13#include "debug.h"14#include "wmi.h"15#include "wmi-ops.h"16 17static const struct wiphy_wowlan_support ath10k_wowlan_support = {18 .flags = WIPHY_WOWLAN_DISCONNECT |19 WIPHY_WOWLAN_MAGIC_PKT,20 .pattern_min_len = WOW_MIN_PATTERN_SIZE,21 .pattern_max_len = WOW_MAX_PATTERN_SIZE,22 .max_pkt_offset = WOW_MAX_PKT_OFFSET,23};24 25static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)26{27 struct ath10k *ar = arvif->ar;28 int i, ret;29 30 for (i = 0; i < WOW_EVENT_MAX; i++) {31 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);32 if (ret) {33 ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",34 wow_wakeup_event(i), arvif->vdev_id, ret);35 return ret;36 }37 }38 39 for (i = 0; i < ar->wow.max_num_patterns; i++) {40 ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);41 if (ret) {42 ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",43 i, arvif->vdev_id, ret);44 return ret;45 }46 }47 48 return 0;49}50 51static int ath10k_wow_cleanup(struct ath10k *ar)52{53 struct ath10k_vif *arvif;54 int ret;55 56 lockdep_assert_held(&ar->conf_mutex);57 58 list_for_each_entry(arvif, &ar->arvifs, list) {59 ret = ath10k_wow_vif_cleanup(arvif);60 if (ret) {61 ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",62 arvif->vdev_id, ret);63 return ret;64 }65 }66 67 return 0;68}69 70/*71 * Convert a 802.3 format to a 802.11 format.72 * +------------+-----------+--------+----------------+73 * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... |74 * +------------+-----------+--------+----------------+75 * |__ |_______ |____________ |________76 * | | | |77 * +--+------------+----+-----------+---------------+-----------+78 * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... |79 * +--+------------+----+-----------+---------------+-----------+80 */81static void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,82 const struct cfg80211_pkt_pattern *old)83{84 u8 hdr_8023_pattern[ETH_HLEN] = {};85 u8 hdr_8023_bit_mask[ETH_HLEN] = {};86 u8 hdr_80211_pattern[WOW_HDR_LEN] = {};87 u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};88 89 int total_len = old->pkt_offset + old->pattern_len;90 int hdr_80211_end_offset;91 92 struct ieee80211_hdr_3addr *new_hdr_pattern =93 (struct ieee80211_hdr_3addr *)hdr_80211_pattern;94 struct ieee80211_hdr_3addr *new_hdr_mask =95 (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;96 struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;97 struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;98 int hdr_len = sizeof(*new_hdr_pattern);99 100 struct rfc1042_hdr *new_rfc_pattern =101 (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);102 struct rfc1042_hdr *new_rfc_mask =103 (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);104 int rfc_len = sizeof(*new_rfc_pattern);105 106 memcpy(hdr_8023_pattern + old->pkt_offset,107 old->pattern, ETH_HLEN - old->pkt_offset);108 memcpy(hdr_8023_bit_mask + old->pkt_offset,109 old->mask, ETH_HLEN - old->pkt_offset);110 111 /* Copy destination address */112 memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);113 memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);114 115 /* Copy source address */116 memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);117 memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);118 119 /* Copy logic link type */120 memcpy(&new_rfc_pattern->snap_type,121 &old_hdr_pattern->h_proto,122 sizeof(old_hdr_pattern->h_proto));123 memcpy(&new_rfc_mask->snap_type,124 &old_hdr_mask->h_proto,125 sizeof(old_hdr_mask->h_proto));126 127 /* Calculate new pkt_offset */128 if (old->pkt_offset < ETH_ALEN)129 new->pkt_offset = old->pkt_offset +130 offsetof(struct ieee80211_hdr_3addr, addr1);131 else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))132 new->pkt_offset = old->pkt_offset +133 offsetof(struct ieee80211_hdr_3addr, addr3) -134 offsetof(struct ethhdr, h_source);135 else136 new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;137 138 /* Calculate new hdr end offset */139 if (total_len > ETH_HLEN)140 hdr_80211_end_offset = hdr_len + rfc_len;141 else if (total_len > offsetof(struct ethhdr, h_proto))142 hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;143 else if (total_len > ETH_ALEN)144 hdr_80211_end_offset = total_len - ETH_ALEN +145 offsetof(struct ieee80211_hdr_3addr, addr3);146 else147 hdr_80211_end_offset = total_len +148 offsetof(struct ieee80211_hdr_3addr, addr1);149 150 new->pattern_len = hdr_80211_end_offset - new->pkt_offset;151 152 memcpy((u8 *)new->pattern,153 hdr_80211_pattern + new->pkt_offset,154 new->pattern_len);155 memcpy((u8 *)new->mask,156 hdr_80211_bit_mask + new->pkt_offset,157 new->pattern_len);158 159 if (total_len > ETH_HLEN) {160 /* Copy frame body */161 memcpy((u8 *)new->pattern + new->pattern_len,162 (void *)old->pattern + ETH_HLEN - old->pkt_offset,163 total_len - ETH_HLEN);164 memcpy((u8 *)new->mask + new->pattern_len,165 (void *)old->mask + ETH_HLEN - old->pkt_offset,166 total_len - ETH_HLEN);167 168 new->pattern_len += total_len - ETH_HLEN;169 }170}171 172static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id,173 struct cfg80211_sched_scan_request *nd_config,174 struct wmi_pno_scan_req *pno)175{176 int i, j, ret = 0;177 u8 ssid_len;178 179 pno->enable = 1;180 pno->vdev_id = vdev_id;181 pno->uc_networks_count = nd_config->n_match_sets;182 183 if (!pno->uc_networks_count ||184 pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)185 return -EINVAL;186 187 if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)188 return -EINVAL;189 190 /* Filling per profile params */191 for (i = 0; i < pno->uc_networks_count; i++) {192 ssid_len = nd_config->match_sets[i].ssid.ssid_len;193 194 if (ssid_len == 0 || ssid_len > 32)195 return -EINVAL;196 197 pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len);198 199 memcpy(pno->a_networks[i].ssid.ssid,200 nd_config->match_sets[i].ssid.ssid,201 nd_config->match_sets[i].ssid.ssid_len);202 pno->a_networks[i].authentication = 0;203 pno->a_networks[i].encryption = 0;204 pno->a_networks[i].bcast_nw_type = 0;205 206 /*Copying list of valid channel into request */207 pno->a_networks[i].channel_count = nd_config->n_channels;208 pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;209 210 for (j = 0; j < nd_config->n_channels; j++) {211 pno->a_networks[i].channels[j] =212 nd_config->channels[j]->center_freq;213 }214 }215 216 /* set scan to passive if no SSIDs are specified in the request */217 if (nd_config->n_ssids == 0)218 pno->do_passive_scan = true;219 else220 pno->do_passive_scan = false;221 222 for (i = 0; i < nd_config->n_ssids; i++) {223 j = 0;224 while (j < pno->uc_networks_count) {225 if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) ==226 nd_config->ssids[i].ssid_len &&227 (memcmp(pno->a_networks[j].ssid.ssid,228 nd_config->ssids[i].ssid,229 __le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) {230 pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;231 break;232 }233 j++;234 }235 }236 237 if (nd_config->n_scan_plans == 2) {238 pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;239 pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;240 pno->slow_scan_period =241 nd_config->scan_plans[1].interval * MSEC_PER_SEC;242 } else if (nd_config->n_scan_plans == 1) {243 pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;244 pno->fast_scan_max_cycles = 1;245 pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;246 } else {247 ath10k_warn(ar, "Invalid number of scan plans %d !!",248 nd_config->n_scan_plans);249 }250 251 if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {252 /* enable mac randomization */253 pno->enable_pno_scan_randomization = 1;254 memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);255 memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);256 }257 258 pno->delay_start_time = nd_config->delay;259 260 /* Current FW does not support min-max range for dwell time */261 pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;262 pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;263 return ret;264}265 266static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,267 struct cfg80211_wowlan *wowlan)268{269 int ret, i;270 unsigned long wow_mask = 0;271 struct ath10k *ar = arvif->ar;272 const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;273 int pattern_id = 0;274 275 /* Setup requested WOW features */276 switch (arvif->vdev_type) {277 case WMI_VDEV_TYPE_IBSS:278 __set_bit(WOW_BEACON_EVENT, &wow_mask);279 fallthrough;280 case WMI_VDEV_TYPE_AP:281 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);282 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);283 __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);284 __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);285 __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);286 __set_bit(WOW_HTT_EVENT, &wow_mask);287 __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);288 break;289 case WMI_VDEV_TYPE_STA:290 if (wowlan->disconnect) {291 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);292 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);293 __set_bit(WOW_BMISS_EVENT, &wow_mask);294 __set_bit(WOW_CSA_IE_EVENT, &wow_mask);295 }296 297 if (wowlan->magic_pkt)298 __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);299 300 if (wowlan->nd_config) {301 struct wmi_pno_scan_req *pno;302 int ret;303 304 pno = kzalloc(sizeof(*pno), GFP_KERNEL);305 if (!pno)306 return -ENOMEM;307 308 ar->nlo_enabled = true;309 310 ret = ath10k_wmi_pno_check(ar, arvif->vdev_id,311 wowlan->nd_config, pno);312 if (!ret) {313 ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);314 __set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);315 }316 317 kfree(pno);318 }319 break;320 default:321 break;322 }323 324 for (i = 0; i < wowlan->n_patterns; i++) {325 u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};326 u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};327 u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};328 struct cfg80211_pkt_pattern new_pattern = {};329 struct cfg80211_pkt_pattern old_pattern = patterns[i];330 int j;331 332 new_pattern.pattern = ath_pattern;333 new_pattern.mask = ath_bitmask;334 if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)335 continue;336 /* convert bytemask to bitmask */337 for (j = 0; j < patterns[i].pattern_len; j++)338 if (patterns[i].mask[j / 8] & BIT(j % 8))339 bitmask[j] = 0xff;340 old_pattern.mask = bitmask;341 342 if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {343 if (patterns[i].pkt_offset < ETH_HLEN) {344 ath10k_wow_convert_8023_to_80211(&new_pattern,345 &old_pattern);346 } else {347 new_pattern = old_pattern;348 new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;349 }350 }351 352 if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))353 return -EINVAL;354 355 ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,356 pattern_id,357 new_pattern.pattern,358 new_pattern.mask,359 new_pattern.pattern_len,360 new_pattern.pkt_offset);361 if (ret) {362 ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",363 pattern_id,364 arvif->vdev_id, ret);365 return ret;366 }367 368 pattern_id++;369 __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);370 }371 372 for (i = 0; i < WOW_EVENT_MAX; i++) {373 if (!test_bit(i, &wow_mask))374 continue;375 ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);376 if (ret) {377 ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",378 wow_wakeup_event(i), arvif->vdev_id, ret);379 return ret;380 }381 }382 383 return 0;384}385 386static int ath10k_wow_set_wakeups(struct ath10k *ar,387 struct cfg80211_wowlan *wowlan)388{389 struct ath10k_vif *arvif;390 int ret;391 392 lockdep_assert_held(&ar->conf_mutex);393 394 list_for_each_entry(arvif, &ar->arvifs, list) {395 ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);396 if (ret) {397 ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",398 arvif->vdev_id, ret);399 return ret;400 }401 }402 403 return 0;404}405 406static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif)407{408 int ret = 0;409 struct ath10k *ar = arvif->ar;410 411 switch (arvif->vdev_type) {412 case WMI_VDEV_TYPE_STA:413 if (ar->nlo_enabled) {414 struct wmi_pno_scan_req *pno;415 416 pno = kzalloc(sizeof(*pno), GFP_KERNEL);417 if (!pno)418 return -ENOMEM;419 420 pno->enable = 0;421 ar->nlo_enabled = false;422 ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);423 kfree(pno);424 }425 break;426 default:427 break;428 }429 return ret;430}431 432static int ath10k_wow_nlo_cleanup(struct ath10k *ar)433{434 struct ath10k_vif *arvif;435 int ret = 0;436 437 lockdep_assert_held(&ar->conf_mutex);438 439 list_for_each_entry(arvif, &ar->arvifs, list) {440 ret = ath10k_vif_wow_clean_nlo(arvif);441 if (ret) {442 ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n",443 arvif->vdev_id, ret);444 return ret;445 }446 }447 448 return 0;449}450 451static int ath10k_wow_enable(struct ath10k *ar)452{453 int ret;454 455 lockdep_assert_held(&ar->conf_mutex);456 457 reinit_completion(&ar->target_suspend);458 459 ret = ath10k_wmi_wow_enable(ar);460 if (ret) {461 ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);462 return ret;463 }464 465 ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);466 if (ret == 0) {467 ath10k_warn(ar, "timed out while waiting for suspend completion\n");468 return -ETIMEDOUT;469 }470 471 return 0;472}473 474static int ath10k_wow_wakeup(struct ath10k *ar)475{476 int ret;477 478 lockdep_assert_held(&ar->conf_mutex);479 480 reinit_completion(&ar->wow.wakeup_completed);481 482 ret = ath10k_wmi_wow_host_wakeup_ind(ar);483 if (ret) {484 ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",485 ret);486 return ret;487 }488 489 ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);490 if (ret == 0) {491 ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");492 return -ETIMEDOUT;493 }494 495 return 0;496}497 498int ath10k_wow_op_suspend(struct ieee80211_hw *hw,499 struct cfg80211_wowlan *wowlan)500{501 struct ath10k *ar = hw->priv;502 int ret;503 504 mutex_lock(&ar->conf_mutex);505 506 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,507 ar->running_fw->fw_file.fw_features))) {508 ret = 1;509 goto exit;510 }511 512 ret = ath10k_wow_cleanup(ar);513 if (ret) {514 ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",515 ret);516 goto exit;517 }518 519 ret = ath10k_wow_set_wakeups(ar, wowlan);520 if (ret) {521 ath10k_warn(ar, "failed to set wow wakeup events: %d\n",522 ret);523 goto cleanup;524 }525 526 ath10k_mac_wait_tx_complete(ar);527 528 ret = ath10k_wow_enable(ar);529 if (ret) {530 ath10k_warn(ar, "failed to start wow: %d\n", ret);531 goto cleanup;532 }533 534 ret = ath10k_hif_suspend(ar);535 if (ret) {536 ath10k_warn(ar, "failed to suspend hif: %d\n", ret);537 goto wakeup;538 }539 540 goto exit;541 542wakeup:543 ath10k_wow_wakeup(ar);544 545cleanup:546 ath10k_wow_cleanup(ar);547 548exit:549 mutex_unlock(&ar->conf_mutex);550 return ret ? 1 : 0;551}552 553void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)554{555 struct ath10k *ar = hw->priv;556 557 mutex_lock(&ar->conf_mutex);558 if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,559 ar->running_fw->fw_file.fw_features)) {560 device_set_wakeup_enable(ar->dev, enabled);561 }562 mutex_unlock(&ar->conf_mutex);563}564 565int ath10k_wow_op_resume(struct ieee80211_hw *hw)566{567 struct ath10k *ar = hw->priv;568 int ret;569 570 mutex_lock(&ar->conf_mutex);571 572 if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,573 ar->running_fw->fw_file.fw_features))) {574 ret = 1;575 goto exit;576 }577 578 ret = ath10k_hif_resume(ar);579 if (ret) {580 ath10k_warn(ar, "failed to resume hif: %d\n", ret);581 goto exit;582 }583 584 ret = ath10k_wow_wakeup(ar);585 if (ret)586 ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);587 588 ret = ath10k_wow_nlo_cleanup(ar);589 if (ret)590 ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret);591 592exit:593 if (ret) {594 switch (ar->state) {595 case ATH10K_STATE_ON:596 ar->state = ATH10K_STATE_RESTARTING;597 ret = 1;598 break;599 case ATH10K_STATE_OFF:600 case ATH10K_STATE_RESTARTING:601 case ATH10K_STATE_RESTARTED:602 case ATH10K_STATE_UTF:603 case ATH10K_STATE_WEDGED:604 ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",605 ar->state);606 ret = -EIO;607 break;608 }609 }610 611 mutex_unlock(&ar->conf_mutex);612 return ret;613}614 615int ath10k_wow_init(struct ath10k *ar)616{617 if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,618 ar->running_fw->fw_file.fw_features))619 return 0;620 621 if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))622 return -EINVAL;623 624 ar->wow.wowlan_support = ath10k_wowlan_support;625 626 if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {627 ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;628 ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;629 }630 631 if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {632 ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;633 ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;634 }635 636 ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;637 ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;638 639 device_set_wakeup_capable(ar->dev, true);640 641 return 0;642}643