linux/drivers/net/wireless/ath/ath10k/wow.c
<<
>>
Prefs
   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
  16static 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
  24static 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
  50static 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 */
  80static 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
 171static 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
 265static 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                fallthrough;
 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
 384static 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
 404static 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
 430static 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
 449static 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
 472static 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
 496int 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
 540wakeup:
 541        ath10k_wow_wakeup(ar);
 542
 543cleanup:
 544        ath10k_wow_cleanup(ar);
 545
 546exit:
 547        mutex_unlock(&ar->conf_mutex);
 548        return ret ? 1 : 0;
 549}
 550
 551void 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
 563int 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
 590exit:
 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
 613int 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}
 641