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