root/drivers/net/wireless/ath/ath10k/wow.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ath10k_wow_vif_cleanup
  2. ath10k_wow_cleanup
  3. ath10k_wow_convert_8023_to_80211
  4. ath10k_wmi_pno_check
  5. ath10k_vif_wow_set_wakeups
  6. ath10k_wow_set_wakeups
  7. ath10k_vif_wow_clean_nlo
  8. ath10k_wow_nlo_cleanup
  9. ath10k_wow_enable
  10. ath10k_wow_wakeup
  11. ath10k_wow_op_suspend
  12. ath10k_wow_op_set_wakeup
  13. ath10k_wow_op_resume
  14. ath10k_wow_init

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

/* [<][>][^][v][top][bottom][index][help] */