Merge commit 'c1e140bf79d817d4a7aa9932eb98b0359c87af33' from mac80211-next
authorKalle Valo <kvalo@codeaurora.org>
Thu, 22 Jan 2015 12:49:44 +0000 (14:49 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 22 Jan 2015 12:49:44 +0000 (14:49 +0200)
Patch "ath9k: Fix no-ack frame status" needs these mac80211 patches:

commit 5cf16616e152dd5c274a65954c77f64892d025a8
Author: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Date:   Wed Dec 10 21:26:11 2014 +0530

    mac80211: Fix accounting of multicast frames

commit 6b127c71fbdd3daacfd8b9f80b8e6ebfb70a889e
Author: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Date:   Wed Dec 10 21:26:10 2014 +0530

    mac80211: Move IEEE80211_TX_CTL_PS_RESPONSE

Conflicts:
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c

14 files changed:
1  2 
Documentation/kernel-parameters.txt
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/cw1200/main.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/uap_event.c
drivers/net/wireless/ti/wlcore/main.c
drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c

@@@ -829,15 -829,6 +829,15 @@@ bytes respectively. Such letter suffixe
                        CONFIG_DEBUG_PAGEALLOC, hence this option will not help
                        tracking down these problems.
  
 +      debug_pagealloc=
 +                      [KNL] When CONFIG_DEBUG_PAGEALLOC is set, this
 +                      parameter enables the feature at boot time. In
 +                      default, it is disabled. We can avoid allocating huge
 +                      chunk of memory for debug pagealloc if we don't enable
 +                      it at boot time and the system will work mostly same
 +                      with the kernel built without CONFIG_DEBUG_PAGEALLOC.
 +                      on: enable the feature
 +
        debugpat        [X86] Enable PAT debugging
  
        decnet.addr=    [HW,NET]
                        multiple times interleaved with hugepages= to reserve
                        huge pages of different sizes. Valid pages sizes on
                        x86-64 are 2M (when the CPU supports "pse") and 1G
 -                      (when the CPU supports the "pdpe1gb" cpuinfo flag)
 -                      Note that 1GB pages can only be allocated at boot time
 -                      using hugepages= and not freed afterwards.
 +                      (when the CPU supports the "pdpe1gb" cpuinfo flag).
  
        hvc_iucv=       [S390] Number of z/VM IUCV hypervisor console (HVC)
                               terminal devices. Valid values: 0..8
                        Formats: { "ima" | "ima-ng" }
                        Default: "ima-ng"
  
 +      ima_template_fmt=
 +                      [IMA] Define a custom template format.
 +                      Format: { "field1|...|fieldN" }
 +
        ima.ahash_minsize= [IMA] Minimum file size for asynchronous hash usage
                        Format: <min_file_size>
                        Set the minimal file size for using asynchronous hash.
                       disable
                         Do not enable intel_pstate as the default
                         scaling driver for the supported processors
 +                     force
 +                       Enable intel_pstate on systems that prohibit it by default
 +                       in favor of acpi-cpufreq. Forcing the intel_pstate driver
 +                       instead of acpi-cpufreq may disable platform features, such
 +                       as thermal controls and power capping, that rely on ACPI
 +                       P-States information being indicated to OSPM and therefore
 +                       should be used with caution. This option does not work with
 +                       processors that aren't supported by the intel_pstate driver
 +                       or on platforms that use pcc-cpufreq instead of acpi-cpufreq.
                       no_hwp
                         Do not enable hardware P state control (HWP)
                         if available.
        OSS             [HW,OSS]
                        See Documentation/sound/oss/oss-parameters.txt
  
 +      page_owner=     [KNL] Boot-time page_owner enabling option.
 +                      Storage of the information about who allocated
 +                      each page is disabled in default. With this switch,
 +                      we can turn it on.
 +                      on: enable the feature
 +
        panic=          [KNL] Kernel behaviour on panic: delay <timeout>
                        timeout > 0: seconds before rebooting
                        timeout = 0: wait forever
  
        retain_initrd   [RAM] Keep initrd memory after extraction
  
+       rfkill.default_state=
+               0       "airplane mode".  All wifi, bluetooth, wimax, gps, fm,
+                       etc. communication is blocked by default.
+               1       Unblocked.
+       rfkill.master_switch_mode=
+               0       The "airplane mode" button does nothing.
+               1       The "airplane mode" button toggles between everything
+                       blocked and the previous configuration.
+               2       The "airplane mode" button toggles between everything
+                       blocked and everything unblocked.
        rhash_entries=  [KNL,NET]
                        Set number of hash buckets for route cache
  
                        neutralize any effect of /proc/sys/kernel/sysrq.
                        Useful for debugging.
  
 +      tcpmhash_entries= [KNL,NET]
 +                      Set the number of tcp_metrics_hash slots.
 +                      Default value is 8192 or 16384 depending on total
 +                      ram pages. This is used to specify the TCP metrics
 +                      cache size. See Documentation/networking/ip-sysctl.txt
 +                      "tcp_no_metrics_save" section for more details.
 +
        tdfx=           [HW,DRM]
  
        test_suspend=   [SUSPEND][,N]
                        See also Documentation/trace/ftrace.txt "trace options"
                        section.
  
 +      tp_printk[FTRACE]
 +                      Have the tracepoints sent to printk as well as the
 +                      tracing ring buffer. This is useful for early boot up
 +                      where the system hangs or reboots and does not give the
 +                      option for reading the tracing buffer or performing a
 +                      ftrace_dump_on_oops.
 +
 +                      To turn off having tracepoints sent to printk,
 +                       echo 0 > /proc/sys/kernel/tracepoint_printk
 +                      Note, echoing 1 into this file without the
 +                      tracepoint_printk kernel cmdline option has no effect.
 +
 +                      ** CAUTION **
 +
 +                      Having tracepoints sent to printk() and activating high
 +                      frequency tracepoints such as irq or sched, can cause
 +                      the system to live lock.
 +
        traceoff_on_warning
                        [FTRACE] enable this option to disable tracing when a
                        warning is hit. This turns off "tracing_on". Tracing can
@@@ -27,8 -27,6 +27,8 @@@
  #include "htt.h"
  #include "txrx.h"
  #include "testmode.h"
 +#include "wmi.h"
 +#include "wmi-ops.h"
  
  /**********/
  /* Crypto */
@@@ -269,10 -267,7 +269,10 @@@ chan_to_phymode(const struct cfg80211_c
        case IEEE80211_BAND_2GHZ:
                switch (chandef->width) {
                case NL80211_CHAN_WIDTH_20_NOHT:
 -                      phymode = MODE_11G;
 +                      if (chandef->chan->flags & IEEE80211_CHAN_NO_OFDM)
 +                              phymode = MODE_11B;
 +                      else
 +                              phymode = MODE_11G;
                        break;
                case NL80211_CHAN_WIDTH_20:
                        phymode = MODE_11NG_HT20;
@@@ -1051,85 -1046,28 +1051,85 @@@ static void ath10k_control_ibss(struct 
                            arvif->vdev_id, ret);
  }
  
 -/*
 - * Review this when mac80211 gains per-interface powersave support.
 - */
 +static int ath10k_mac_vif_recalc_ps_wake_threshold(struct ath10k_vif *arvif)
 +{
 +      struct ath10k *ar = arvif->ar;
 +      u32 param;
 +      u32 value;
 +      int ret;
 +
 +      lockdep_assert_held(&arvif->ar->conf_mutex);
 +
 +      if (arvif->u.sta.uapsd)
 +              value = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER;
 +      else
 +              value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
 +
 +      param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
 +      ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to submit ps wake threshold %u on vdev %i: %d\n",
 +                          value, arvif->vdev_id, ret);
 +              return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif)
 +{
 +      struct ath10k *ar = arvif->ar;
 +      u32 param;
 +      u32 value;
 +      int ret;
 +
 +      lockdep_assert_held(&arvif->ar->conf_mutex);
 +
 +      if (arvif->u.sta.uapsd)
 +              value = WMI_STA_PS_PSPOLL_COUNT_UAPSD;
 +      else
 +              value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
 +
 +      param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
 +      ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
 +                                        param, value);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to submit ps poll count %u on vdev %i: %d\n",
 +                          value, arvif->vdev_id, ret);
 +              return ret;
 +      }
 +
 +      return 0;
 +}
 +
  static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
  {
        struct ath10k *ar = arvif->ar;
 +      struct ieee80211_vif *vif = arvif->vif;
        struct ieee80211_conf *conf = &ar->hw->conf;
        enum wmi_sta_powersave_param param;
        enum wmi_sta_ps_mode psmode;
        int ret;
 +      int ps_timeout;
  
        lockdep_assert_held(&arvif->ar->conf_mutex);
  
        if (arvif->vif->type != NL80211_IFTYPE_STATION)
                return 0;
  
 -      if (conf->flags & IEEE80211_CONF_PS) {
 +      if (vif->bss_conf.ps) {
                psmode = WMI_STA_PS_MODE_ENABLED;
                param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
  
 +              ps_timeout = conf->dynamic_ps_timeout;
 +              if (ps_timeout == 0) {
 +                      /* Firmware doesn't like 0 */
 +                      ps_timeout = ieee80211_tu_to_usec(
 +                              vif->bss_conf.beacon_int) / 1000;
 +              }
 +
                ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
 -                                                conf->dynamic_ps_timeout);
 +                                                ps_timeout);
                if (ret) {
                        ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n",
                                    arvif->vdev_id, ret);
@@@ -1471,22 -1409,9 +1471,22 @@@ static void ath10k_peer_assoc_h_qos(str
                if (vif->bss_conf.qos)
                        arg->peer_flags |= WMI_PEER_QOS;
                break;
 +      case WMI_VDEV_TYPE_IBSS:
 +              if (sta->wme)
 +                      arg->peer_flags |= WMI_PEER_QOS;
 +              break;
        default:
                break;
        }
 +
 +      ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n",
 +                 sta->addr, !!(arg->peer_flags & WMI_PEER_QOS));
 +}
 +
 +static bool ath10k_mac_sta_has_11g_rates(struct ieee80211_sta *sta)
 +{
 +      /* First 4 rates in ath10k_rates are CCK (11b) rates. */
 +      return sta->supp_rates[IEEE80211_BAND_2GHZ] >> 4;
  }
  
  static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
                                phymode = MODE_11NG_HT40;
                        else
                                phymode = MODE_11NG_HT20;
 -              } else {
 +              } else if (ath10k_mac_sta_has_11g_rates(sta)) {
                        phymode = MODE_11G;
 +              } else {
 +                      phymode = MODE_11B;
                }
  
                break;
@@@ -2948,6 -2871,8 +2948,8 @@@ static int ath10k_add_interface(struct 
        int bit;
        u32 vdev_param;
  
+       vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
        mutex_lock(&ar->conf_mutex);
  
        memset(arvif, 0, sizeof(*arvif));
        arvif->vdev_id = bit;
        arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
  
 -      if (ar->p2p)
 -              arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
 -
        switch (vif->type) {
 +      case NL80211_IFTYPE_P2P_DEVICE:
 +              arvif->vdev_type = WMI_VDEV_TYPE_STA;
 +              arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE;
 +              break;
        case NL80211_IFTYPE_UNSPECIFIED:
        case NL80211_IFTYPE_STATION:
                arvif->vdev_type = WMI_VDEV_TYPE_STA;
                        goto err_peer_delete;
                }
  
 -              param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD;
 -              value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS;
 -              ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
 -                                                param, value);
 +              ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
                if (ret) {
 -                      ath10k_warn(ar, "failed to set vdev %i TX wake thresh: %d\n",
 +                      ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
  
 -              param = WMI_STA_PS_PARAM_PSPOLL_COUNT;
 -              value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX;
 -              ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
 -                                                param, value);
 +              ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
                if (ret) {
 -                      ath10k_warn(ar, "failed to set vdev %i PSPOLL count: %d\n",
 +                      ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
                                    arvif->vdev_id, ret);
                        goto err_peer_delete;
                }
@@@ -3386,13 -3316,6 +3388,13 @@@ static void ath10k_bss_info_changed(str
                        ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
        }
  
 +      if (changed & BSS_CHANGED_PS) {
 +              ret = ath10k_mac_vif_setup_ps(arvif);
 +              if (ret)
 +                      ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n",
 +                                  arvif->vdev_id, ret);
 +      }
 +
        mutex_unlock(&ar->conf_mutex);
  }
  
@@@ -3662,9 -3585,8 +3664,9 @@@ static void ath10k_sta_rc_update_wk(str
                                    sta->addr, smps, err);
        }
  
 -      if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
 -              ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
 +      if (changed & IEEE80211_RC_SUPP_RATES_CHANGED ||
 +          changed & IEEE80211_RC_NSS_CHANGED) {
 +              ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n",
                           sta->addr);
  
                err = ath10k_station_assoc(ar, arvif->vif, sta, true);
@@@ -3888,20 -3810,6 +3890,20 @@@ static int ath10k_conf_tx_uapsd(struct 
        if (ret)
                ath10k_warn(ar, "failed to set rx wake param: %d\n", ret);
  
 +      ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
 +                          arvif->vdev_id, ret);
 +              return ret;
 +      }
 +
 +      ret = ath10k_mac_vif_recalc_ps_poll_count(arvif);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
 +                          arvif->vdev_id, ret);
 +              return ret;
 +      }
 +
  exit:
        return ret;
  }
@@@ -4083,6 -3991,29 +4085,6 @@@ static int ath10k_set_rts_threshold(str
        return ret;
  }
  
 -static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 -{
 -      struct ath10k *ar = hw->priv;
 -      struct ath10k_vif *arvif;
 -      int ret = 0;
 -
 -      mutex_lock(&ar->conf_mutex);
 -      list_for_each_entry(arvif, &ar->arvifs, list) {
 -              ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
 -                         arvif->vdev_id, value);
 -
 -              ret = ath10k_mac_set_frag(arvif, value);
 -              if (ret) {
 -                      ath10k_warn(ar, "failed to set fragmentation threshold for vdev %d: %d\n",
 -                                  arvif->vdev_id, ret);
 -                      break;
 -              }
 -      }
 -      mutex_unlock(&ar->conf_mutex);
 -
 -      return ret;
 -}
 -
  static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                         u32 queues, bool drop)
  {
@@@ -4726,6 -4657,7 +4728,6 @@@ static const struct ieee80211_ops ath10
        .remain_on_channel              = ath10k_remain_on_channel,
        .cancel_remain_on_channel       = ath10k_cancel_remain_on_channel,
        .set_rts_threshold              = ath10k_set_rts_threshold,
 -      .set_frag_threshold             = ath10k_set_frag_threshold,
        .flush                          = ath10k_flush,
        .tx_last_beacon                 = ath10k_tx_last_beacon,
        .set_antenna                    = ath10k_set_antenna,
@@@ -4816,9 -4748,6 +4818,9 @@@ static const struct ieee80211_channel a
        CHAN5G(165, 5825, 0),
  };
  
 +/* Note: Be careful if you re-order these. There is code which depends on this
 + * ordering.
 + */
  static struct ieee80211_rate ath10k_rates[] = {
        /* CCK */
        RATETAB_ENT(10,  0x82, 0),
@@@ -4872,10 -4801,6 +4874,10 @@@ static const struct ieee80211_iface_lim
        .types  = BIT(NL80211_IFTYPE_P2P_GO)
        },
        {
 +      .max    = 1,
 +      .types  = BIT(NL80211_IFTYPE_P2P_DEVICE)
 +      },
 +      {
        .max    = 7,
        .types  = BIT(NL80211_IFTYPE_AP)
        },
@@@ -5095,14 -5020,12 +5097,13 @@@ int ath10k_mac_register(struct ath10k *
  
        if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
                ar->hw->wiphy->interface_modes |=
 +                      BIT(NL80211_IFTYPE_P2P_DEVICE) |
                        BIT(NL80211_IFTYPE_P2P_CLIENT) |
                        BIT(NL80211_IFTYPE_P2P_GO);
  
        ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                        IEEE80211_HW_SUPPORTS_PS |
                        IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
-                       IEEE80211_HW_SUPPORTS_UAPSD |
                        IEEE80211_HW_MFP_CAPABLE |
                        IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                        IEEE80211_HW_HAS_RATE_CONTROL |
         */
        ar->hw->queues = 4;
  
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
 -              ar->hw->wiphy->n_iface_combinations =
 -                      ARRAY_SIZE(ath10k_10x_if_comb);
 -      } else {
 +      switch (ar->wmi.op_version) {
 +      case ATH10K_FW_WMI_OP_VERSION_MAIN:
 +      case ATH10K_FW_WMI_OP_VERSION_TLV:
                ar->hw->wiphy->iface_combinations = ath10k_if_comb;
                ar->hw->wiphy->n_iface_combinations =
                        ARRAY_SIZE(ath10k_if_comb);
 -
                ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
 +              break;
 +      case ATH10K_FW_WMI_OP_VERSION_10_1:
 +      case ATH10K_FW_WMI_OP_VERSION_10_2:
 +      case ATH10K_FW_WMI_OP_VERSION_10_2_4:
 +              ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
 +              ar->hw->wiphy->n_iface_combinations =
 +                      ARRAY_SIZE(ath10k_10x_if_comb);
 +              break;
 +      case ATH10K_FW_WMI_OP_VERSION_UNSET:
 +      case ATH10K_FW_WMI_OP_VERSION_MAX:
 +              WARN_ON(1);
 +              ret = -EINVAL;
 +              goto err_free;
        }
  
        ar->hw->netdev_features = NETIF_F_HW_CSUM;
  #include "htc.h"
  #include "debug.h"
  #include "wmi.h"
 +#include "wmi-tlv.h"
  #include "mac.h"
  #include "testmode.h"
 +#include "wmi-ops.h"
  
  /* MAIN WMI cmd track */
  static struct wmi_cmd_map wmi_cmd_map = {
        .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID,
        .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID,
        .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID,
 +      .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
  };
  
  /* 10.X WMI cmd track */
@@@ -268,129 -265,6 +268,129 @@@ static struct wmi_cmd_map wmi_10x_cmd_m
        .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
        .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID,
        .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID,
 +      .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
 +};
 +
 +/* 10.2.4 WMI cmd track */
 +static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
 +      .init_cmdid = WMI_10_2_INIT_CMDID,
 +      .start_scan_cmdid = WMI_10_2_START_SCAN_CMDID,
 +      .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID,
 +      .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID,
 +      .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED,
 +      .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID,
 +      .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID,
 +      .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID,
 +      .pdev_pktlog_enable_cmdid = WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID,
 +      .pdev_pktlog_disable_cmdid = WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID,
 +      .pdev_set_wmm_params_cmdid = WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID,
 +      .pdev_set_ht_cap_ie_cmdid = WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID,
 +      .pdev_set_vht_cap_ie_cmdid = WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID,
 +      .pdev_set_quiet_mode_cmdid = WMI_10_2_PDEV_SET_QUIET_MODE_CMDID,
 +      .pdev_green_ap_ps_enable_cmdid = WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID,
 +      .pdev_get_tpc_config_cmdid = WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID,
 +      .pdev_set_base_macaddr_cmdid = WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID,
 +      .vdev_create_cmdid = WMI_10_2_VDEV_CREATE_CMDID,
 +      .vdev_delete_cmdid = WMI_10_2_VDEV_DELETE_CMDID,
 +      .vdev_start_request_cmdid = WMI_10_2_VDEV_START_REQUEST_CMDID,
 +      .vdev_restart_request_cmdid = WMI_10_2_VDEV_RESTART_REQUEST_CMDID,
 +      .vdev_up_cmdid = WMI_10_2_VDEV_UP_CMDID,
 +      .vdev_stop_cmdid = WMI_10_2_VDEV_STOP_CMDID,
 +      .vdev_down_cmdid = WMI_10_2_VDEV_DOWN_CMDID,
 +      .vdev_set_param_cmdid = WMI_10_2_VDEV_SET_PARAM_CMDID,
 +      .vdev_install_key_cmdid = WMI_10_2_VDEV_INSTALL_KEY_CMDID,
 +      .peer_create_cmdid = WMI_10_2_PEER_CREATE_CMDID,
 +      .peer_delete_cmdid = WMI_10_2_PEER_DELETE_CMDID,
 +      .peer_flush_tids_cmdid = WMI_10_2_PEER_FLUSH_TIDS_CMDID,
 +      .peer_set_param_cmdid = WMI_10_2_PEER_SET_PARAM_CMDID,
 +      .peer_assoc_cmdid = WMI_10_2_PEER_ASSOC_CMDID,
 +      .peer_add_wds_entry_cmdid = WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID,
 +      .peer_remove_wds_entry_cmdid = WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID,
 +      .peer_mcast_group_cmdid = WMI_10_2_PEER_MCAST_GROUP_CMDID,
 +      .bcn_tx_cmdid = WMI_10_2_BCN_TX_CMDID,
 +      .pdev_send_bcn_cmdid = WMI_10_2_PDEV_SEND_BCN_CMDID,
 +      .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
 +      .bcn_filter_rx_cmdid = WMI_10_2_BCN_FILTER_RX_CMDID,
 +      .prb_req_filter_rx_cmdid = WMI_10_2_PRB_REQ_FILTER_RX_CMDID,
 +      .mgmt_tx_cmdid = WMI_10_2_MGMT_TX_CMDID,
 +      .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED,
 +      .addba_clear_resp_cmdid = WMI_10_2_ADDBA_CLEAR_RESP_CMDID,
 +      .addba_send_cmdid = WMI_10_2_ADDBA_SEND_CMDID,
 +      .addba_status_cmdid = WMI_10_2_ADDBA_STATUS_CMDID,
 +      .delba_send_cmdid = WMI_10_2_DELBA_SEND_CMDID,
 +      .addba_set_resp_cmdid = WMI_10_2_ADDBA_SET_RESP_CMDID,
 +      .send_singleamsdu_cmdid = WMI_10_2_SEND_SINGLEAMSDU_CMDID,
 +      .sta_powersave_mode_cmdid = WMI_10_2_STA_POWERSAVE_MODE_CMDID,
 +      .sta_powersave_param_cmdid = WMI_10_2_STA_POWERSAVE_PARAM_CMDID,
 +      .sta_mimo_ps_mode_cmdid = WMI_10_2_STA_MIMO_PS_MODE_CMDID,
 +      .pdev_dfs_enable_cmdid = WMI_10_2_PDEV_DFS_ENABLE_CMDID,
 +      .pdev_dfs_disable_cmdid = WMI_10_2_PDEV_DFS_DISABLE_CMDID,
 +      .roam_scan_mode = WMI_10_2_ROAM_SCAN_MODE,
 +      .roam_scan_rssi_threshold = WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD,
 +      .roam_scan_period = WMI_10_2_ROAM_SCAN_PERIOD,
 +      .roam_scan_rssi_change_threshold =
 +                              WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD,
 +      .roam_ap_profile = WMI_10_2_ROAM_AP_PROFILE,
 +      .ofl_scan_add_ap_profile = WMI_10_2_OFL_SCAN_ADD_AP_PROFILE,
 +      .ofl_scan_remove_ap_profile = WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE,
 +      .ofl_scan_period = WMI_10_2_OFL_SCAN_PERIOD,
 +      .p2p_dev_set_device_info = WMI_10_2_P2P_DEV_SET_DEVICE_INFO,
 +      .p2p_dev_set_discoverability = WMI_10_2_P2P_DEV_SET_DISCOVERABILITY,
 +      .p2p_go_set_beacon_ie = WMI_10_2_P2P_GO_SET_BEACON_IE,
 +      .p2p_go_set_probe_resp_ie = WMI_10_2_P2P_GO_SET_PROBE_RESP_IE,
 +      .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
 +      .ap_ps_peer_param_cmdid = WMI_10_2_AP_PS_PEER_PARAM_CMDID,
 +      .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
 +      .peer_rate_retry_sched_cmdid = WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID,
 +      .wlan_profile_trigger_cmdid = WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID,
 +      .wlan_profile_set_hist_intvl_cmdid =
 +                              WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID,
 +      .wlan_profile_get_profile_data_cmdid =
 +                              WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID,
 +      .wlan_profile_enable_profile_id_cmdid =
 +                              WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID,
 +      .wlan_profile_list_profile_id_cmdid =
 +                              WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID,
 +      .pdev_suspend_cmdid = WMI_10_2_PDEV_SUSPEND_CMDID,
 +      .pdev_resume_cmdid = WMI_10_2_PDEV_RESUME_CMDID,
 +      .add_bcn_filter_cmdid = WMI_10_2_ADD_BCN_FILTER_CMDID,
 +      .rmv_bcn_filter_cmdid = WMI_10_2_RMV_BCN_FILTER_CMDID,
 +      .wow_add_wake_pattern_cmdid = WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID,
 +      .wow_del_wake_pattern_cmdid = WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID,
 +      .wow_enable_disable_wake_event_cmdid =
 +                              WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID,
 +      .wow_enable_cmdid = WMI_10_2_WOW_ENABLE_CMDID,
 +      .wow_hostwakeup_from_sleep_cmdid =
 +                              WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID,
 +      .rtt_measreq_cmdid = WMI_10_2_RTT_MEASREQ_CMDID,
 +      .rtt_tsf_cmdid = WMI_10_2_RTT_TSF_CMDID,
 +      .vdev_spectral_scan_configure_cmdid =
 +                              WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID,
 +      .vdev_spectral_scan_enable_cmdid =
 +                              WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID,
 +      .request_stats_cmdid = WMI_10_2_REQUEST_STATS_CMDID,
 +      .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED,
 +      .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED,
 +      .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED,
 +      .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED,
 +      .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED,
 +      .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED,
 +      .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED,
 +      .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED,
 +      .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED,
 +      .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED,
 +      .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED,
 +      .echo_cmdid = WMI_10_2_ECHO_CMDID,
 +      .pdev_utf_cmdid = WMI_10_2_PDEV_UTF_CMDID,
 +      .dbglog_cfg_cmdid = WMI_10_2_DBGLOG_CFG_CMDID,
 +      .pdev_qvit_cmdid = WMI_10_2_PDEV_QVIT_CMDID,
 +      .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED,
 +      .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
 +      .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED,
 +      .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
 +      .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
 +      .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
 +      .pdev_get_temperature_cmdid = WMI_10_2_PDEV_GET_TEMPERATURE_CMDID,
  };
  
  /* MAIN WMI VDEV param map */
@@@ -511,64 -385,6 +511,64 @@@ static struct wmi_vdev_param_map wmi_10
                WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
  };
  
 +static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = {
 +      .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD,
 +      .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
 +      .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL,
 +      .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL,
 +      .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE,
 +      .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE,
 +      .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME,
 +      .preamble = WMI_10X_VDEV_PARAM_PREAMBLE,
 +      .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME,
 +      .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD,
 +      .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME,
 +      .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL,
 +      .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD,
 +      .wmi_vdev_oc_scheduler_air_time_limit =
 +                              WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT,
 +      .wds = WMI_10X_VDEV_PARAM_WDS,
 +      .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW,
 +      .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX,
 +      .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
 +      .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED,
 +      .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM,
 +      .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH,
 +      .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET,
 +      .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION,
 +      .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT,
 +      .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE,
 +      .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE,
 +      .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE,
 +      .sgi = WMI_10X_VDEV_PARAM_SGI,
 +      .ldpc = WMI_10X_VDEV_PARAM_LDPC,
 +      .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC,
 +      .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC,
 +      .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD,
 +      .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID,
 +      .nss = WMI_10X_VDEV_PARAM_NSS,
 +      .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE,
 +      .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE,
 +      .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE,
 +      .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE,
 +      .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE,
 +      .ap_keepalive_min_idle_inactive_time_secs =
 +              WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS,
 +      .ap_keepalive_max_idle_inactive_time_secs =
 +              WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS,
 +      .ap_keepalive_max_unresponsive_time_secs =
 +              WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS,
 +      .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS,
 +      .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET,
 +      .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS,
 +      .txbf = WMI_VDEV_PARAM_UNSUPPORTED,
 +      .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED,
 +      .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED,
 +      .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED,
 +      .ap_detect_out_of_sync_sleeping_sta_time_secs =
 +              WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS,
 +};
 +
  static struct wmi_pdev_param_map wmi_pdev_param_map = {
        .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK,
        .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK,
        .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED,
        .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED,
        .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .cal_period = WMI_PDEV_PARAM_UNSUPPORTED,
  };
  
  static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
        .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
        .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
        .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
 +      .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD,
 +};
 +
 +static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = {
 +      .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK,
 +      .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK,
 +      .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G,
 +      .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G,
 +      .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE,
 +      .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE,
 +      .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE,
 +      .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE,
 +      .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE,
 +      .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW,
 +      .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
 +      .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH,
 +      .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH,
 +      .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING,
 +      .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE,
 +      .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE,
 +      .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK,
 +      .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI,
 +      .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO,
 +      .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT,
 +      .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE,
 +      .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE,
 +      .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT,
 +      .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE,
 +      .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE,
 +      .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD,
 +      .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD,
 +      .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD,
 +      .bcnflt_stats_update_period =
 +                              WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
 +      .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
 +      .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
 +      .dcs = WMI_10X_PDEV_PARAM_DCS,
 +      .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
 +      .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
 +      .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD,
 +      .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL,
 +      .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL,
 +      .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN,
 +      .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED,
 +      .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET,
 +      .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR,
 +      .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE,
 +      .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD,
  };
  
  /* firmware 10.2 specific mappings */
@@@ -846,11 -607,11 +846,11 @@@ static struct wmi_cmd_map wmi_10_2_cmd_
        .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED,
        .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID,
        .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
 +      .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED,
  };
  
 -static void
 -ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
 -                         const struct wmi_channel_arg *arg)
 +void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
 +                              const struct wmi_channel_arg *arg)
  {
        u32 flags = 0;
  
@@@ -924,8 -685,8 +924,8 @@@ static void ath10k_wmi_htc_tx_complete(
        dev_kfree_skb(skb);
  }
  
 -static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
 -                                    u32 cmd_id)
 +int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb,
 +                             u32 cmd_id)
  {
        struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
        struct wmi_cmd_hdr *cmd_hdr;
@@@ -1031,23 -792,24 +1031,23 @@@ int ath10k_wmi_cmd_send(struct ath10k *
        return ret;
  }
  
 -int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu)
  {
 -      int ret = 0;
        struct wmi_mgmt_tx_cmd *cmd;
        struct ieee80211_hdr *hdr;
 -      struct sk_buff *wmi_skb;
 -      struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 +      struct sk_buff *skb;
        int len;
 -      u32 buf_len = skb->len;
 +      u32 buf_len = msdu->len;
        u16 fc;
  
 -      hdr = (struct ieee80211_hdr *)skb->data;
 +      hdr = (struct ieee80211_hdr *)msdu->data;
        fc = le16_to_cpu(hdr->frame_control);
  
        if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control)))
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
  
 -      len = sizeof(cmd->hdr) + skb->len;
 +      len = sizeof(cmd->hdr) + msdu->len;
  
        if ((ieee80211_is_action(hdr->frame_control) ||
             ieee80211_is_deauth(hdr->frame_control) ||
  
        len = round_up(len, 4);
  
 -      wmi_skb = ath10k_wmi_alloc_skb(ar, len);
 -      if (!wmi_skb)
 -              return -ENOMEM;
 +      skb = ath10k_wmi_alloc_skb(ar, len);
 +      if (!skb)
 +              return ERR_PTR(-ENOMEM);
  
 -      cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data;
 +      cmd = (struct wmi_mgmt_tx_cmd *)skb->data;
  
 -      cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id);
 +      cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id);
        cmd->hdr.tx_rate = 0;
        cmd->hdr.tx_power = 0;
        cmd->hdr.buf_len = __cpu_to_le32(buf_len);
  
        ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr));
 -      memcpy(cmd->buf, skb->data, skb->len);
 +      memcpy(cmd->buf, msdu->data, msdu->len);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
 -                 wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
 +                 msdu, skb->len, fc & IEEE80211_FCTL_FTYPE,
                   fc & IEEE80211_FCTL_STYPE);
        trace_ath10k_tx_hdr(ar, skb->data, skb->len);
        trace_ath10k_tx_payload(ar, skb->data, skb->len);
  
 -      /* Send the management frame buffer to the target */
 -      ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
 -      if (ret)
 -              return ret;
 -
 -      /* TODO: report tx status to mac80211 - temporary just ACK */
 -      info->flags |= IEEE80211_TX_STAT_ACK;
 -      ieee80211_tx_status_irqsafe(ar->hw, skb);
 -
 -      return ret;
 +      return skb;
  }
  
  static void ath10k_wmi_event_scan_started(struct ath10k *ar)
@@@ -1206,48 -977,22 +1206,48 @@@ ath10k_wmi_event_scan_type_str(enum wmi
        }
  }
  
 -static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
 +static int ath10k_wmi_op_pull_scan_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                    struct wmi_scan_ev_arg *arg)
 +{
 +      struct wmi_scan_event *ev = (void *)skb->data;
 +
 +      if (skb->len < sizeof(*ev))
 +              return -EPROTO;
 +
 +      skb_pull(skb, sizeof(*ev));
 +      arg->event_type = ev->event_type;
 +      arg->reason = ev->reason;
 +      arg->channel_freq = ev->channel_freq;
 +      arg->scan_req_id = ev->scan_req_id;
 +      arg->scan_id = ev->scan_id;
 +      arg->vdev_id = ev->vdev_id;
 +
 +      return 0;
 +}
 +
 +int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
  {
 -      struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data;
 +      struct wmi_scan_ev_arg arg = {};
        enum wmi_scan_event_type event_type;
        enum wmi_scan_completion_reason reason;
        u32 freq;
        u32 req_id;
        u32 scan_id;
        u32 vdev_id;
 +      int ret;
 +
 +      ret = ath10k_wmi_pull_scan(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse scan event: %d\n", ret);
 +              return ret;
 +      }
  
 -      event_type = __le32_to_cpu(event->event_type);
 -      reason     = __le32_to_cpu(event->reason);
 -      freq       = __le32_to_cpu(event->channel_freq);
 -      req_id     = __le32_to_cpu(event->scan_req_id);
 -      scan_id    = __le32_to_cpu(event->scan_id);
 -      vdev_id    = __le32_to_cpu(event->vdev_id);
 +      event_type = __le32_to_cpu(arg.event_type);
 +      reason = __le32_to_cpu(arg.reason);
 +      freq = __le32_to_cpu(arg.channel_freq);
 +      req_id = __le32_to_cpu(arg.scan_req_id);
 +      scan_id = __le32_to_cpu(arg.scan_id);
 +      vdev_id = __le32_to_cpu(arg.vdev_id);
  
        spin_lock_bh(&ar->data_lock);
  
@@@ -1402,51 -1147,11 +1402,51 @@@ static void ath10k_wmi_handle_wep_reaut
        }
  }
  
 -static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
 +static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                       struct wmi_mgmt_rx_ev_arg *arg)
  {
        struct wmi_mgmt_rx_event_v1 *ev_v1;
        struct wmi_mgmt_rx_event_v2 *ev_v2;
        struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
 +      size_t pull_len;
 +      u32 msdu_len;
 +
 +      if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
 +              ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
 +              ev_hdr = &ev_v2->hdr.v1;
 +              pull_len = sizeof(*ev_v2);
 +      } else {
 +              ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data;
 +              ev_hdr = &ev_v1->hdr;
 +              pull_len = sizeof(*ev_v1);
 +      }
 +
 +      if (skb->len < pull_len)
 +              return -EPROTO;
 +
 +      skb_pull(skb, pull_len);
 +      arg->channel = ev_hdr->channel;
 +      arg->buf_len = ev_hdr->buf_len;
 +      arg->status = ev_hdr->status;
 +      arg->snr = ev_hdr->snr;
 +      arg->phy_mode = ev_hdr->phy_mode;
 +      arg->rate = ev_hdr->rate;
 +
 +      msdu_len = __le32_to_cpu(arg->buf_len);
 +      if (skb->len < msdu_len)
 +              return -EPROTO;
 +
 +      /* the WMI buffer might've ended up being padded to 4 bytes due to HTC
 +       * trailer with credit update. Trim the excess garbage.
 +       */
 +      skb_trim(skb, msdu_len);
 +
 +      return 0;
 +}
 +
 +int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
 +{
 +      struct wmi_mgmt_rx_ev_arg arg = {};
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr;
        u32 rx_status;
        u32 rate;
        u32 buf_len;
        u16 fc;
 -      int pull_len;
 +      int ret;
  
 -      if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
 -              ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
 -              ev_hdr = &ev_v2->hdr.v1;
 -              pull_len = sizeof(*ev_v2);
 -      } else {
 -              ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data;
 -              ev_hdr = &ev_v1->hdr;
 -              pull_len = sizeof(*ev_v1);
 +      ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse mgmt rx event: %d\n", ret);
 +              return ret;
        }
  
 -      channel   = __le32_to_cpu(ev_hdr->channel);
 -      buf_len   = __le32_to_cpu(ev_hdr->buf_len);
 -      rx_status = __le32_to_cpu(ev_hdr->status);
 -      snr       = __le32_to_cpu(ev_hdr->snr);
 -      phy_mode  = __le32_to_cpu(ev_hdr->phy_mode);
 -      rate      = __le32_to_cpu(ev_hdr->rate);
 +      channel = __le32_to_cpu(arg.channel);
 +      buf_len = __le32_to_cpu(arg.buf_len);
 +      rx_status = __le32_to_cpu(arg.status);
 +      snr = __le32_to_cpu(arg.snr);
 +      phy_mode = __le32_to_cpu(arg.phy_mode);
 +      rate = __le32_to_cpu(arg.rate);
  
        memset(status, 0, sizeof(*status));
  
        status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
        status->rate_idx = get_rate_idx(rate, status->band);
  
 -      skb_pull(skb, pull_len);
 -
        hdr = (struct ieee80211_hdr *)skb->data;
        fc = le16_to_cpu(hdr->frame_control);
  
                   status->freq, status->band, status->signal,
                   status->rate_idx);
  
 -      /*
 -       * packets from HTC come aligned to 4byte boundaries
 -       * because they can originally come in along with a trailer
 -       */
 -      skb_trim(skb, buf_len);
 -
        ieee80211_rx(ar->hw, skb);
        return 0;
  }
@@@ -1578,44 -1295,21 +1578,44 @@@ exit
        return idx;
  }
  
 -static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
 +static int ath10k_wmi_op_pull_ch_info_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                       struct wmi_ch_info_ev_arg *arg)
 +{
 +      struct wmi_chan_info_event *ev = (void *)skb->data;
 +
 +      if (skb->len < sizeof(*ev))
 +              return -EPROTO;
 +
 +      skb_pull(skb, sizeof(*ev));
 +      arg->err_code = ev->err_code;
 +      arg->freq = ev->freq;
 +      arg->cmd_flags = ev->cmd_flags;
 +      arg->noise_floor = ev->noise_floor;
 +      arg->rx_clear_count = ev->rx_clear_count;
 +      arg->cycle_count = ev->cycle_count;
 +
 +      return 0;
 +}
 +
 +void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
  {
 -      struct wmi_chan_info_event *ev;
 +      struct wmi_ch_info_ev_arg arg = {};
        struct survey_info *survey;
        u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
 -      int idx;
 +      int idx, ret;
  
 -      ev = (struct wmi_chan_info_event *)skb->data;
 +      ret = ath10k_wmi_pull_ch_info(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse chan info event: %d\n", ret);
 +              return;
 +      }
  
 -      err_code = __le32_to_cpu(ev->err_code);
 -      freq = __le32_to_cpu(ev->freq);
 -      cmd_flags = __le32_to_cpu(ev->cmd_flags);
 -      noise_floor = __le32_to_cpu(ev->noise_floor);
 -      rx_clear_count = __le32_to_cpu(ev->rx_clear_count);
 -      cycle_count = __le32_to_cpu(ev->cycle_count);
 +      err_code = __le32_to_cpu(arg.err_code);
 +      freq = __le32_to_cpu(arg.freq);
 +      cmd_flags = __le32_to_cpu(arg.cmd_flags);
 +      noise_floor = __le32_to_cpu(arg.noise_floor);
 +      rx_clear_count = __le32_to_cpu(arg.rx_clear_count);
 +      cycle_count = __le32_to_cpu(arg.cycle_count);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
                rx_clear_count -= ar->survey_last_rx_clear_count;
  
                survey = &ar->survey[idx];
-               survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count);
-               survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
+               survey->time = WMI_CHAN_INFO_MSEC(cycle_count);
+               survey->time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
                survey->noise = noise_floor;
-               survey->filled = SURVEY_INFO_CHANNEL_TIME |
-                                SURVEY_INFO_CHANNEL_TIME_RX |
+               survey->filled = SURVEY_INFO_TIME |
+                                SURVEY_INFO_TIME_RX |
                                 SURVEY_INFO_NOISE_DBM;
        }
  
@@@ -1665,12 -1359,12 +1665,12 @@@ exit
        spin_unlock_bh(&ar->data_lock);
  }
  
 -static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
 +void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
  }
  
 -static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
 +int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug mesg len %d\n",
                   skb->len);
        return 0;
  }
  
 -static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
 -                                     struct ath10k_fw_stats_pdev *dst)
 +void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
 +                              struct ath10k_fw_stats_pdev *dst)
  {
        const struct wal_dbg_tx_stats *tx = &src->wal.tx;
        const struct wal_dbg_rx_stats *rx = &src->wal.rx;
        dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs);
  }
  
 -static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
 -                                     struct ath10k_fw_stats_peer *dst)
 +void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
 +                              struct ath10k_fw_stats_peer *dst)
  {
        ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
        dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
        dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
  }
  
 -static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
 -                                       struct sk_buff *skb,
 -                                       struct ath10k_fw_stats *stats)
 +static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar,
 +                                          struct sk_buff *skb,
 +                                          struct ath10k_fw_stats *stats)
  {
        const struct wmi_stats_event *ev = (void *)skb->data;
        u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
        return 0;
  }
  
 -static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
 -                                      struct sk_buff *skb,
 -                                      struct ath10k_fw_stats *stats)
 +static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar,
 +                                         struct sk_buff *skb,
 +                                         struct ath10k_fw_stats *stats)
  {
        const struct wmi_stats_event *ev = (void *)skb->data;
        u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
        return 0;
  }
  
 -int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
 -                           struct ath10k_fw_stats *stats)
 +void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb)
  {
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
 -              return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats);
 -      else
 -              return ath10k_wmi_main_pull_fw_stats(ar, skb, stats);
 +      ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
 +      ath10k_debug_fw_stats_process(ar, skb);
  }
  
 -static void ath10k_wmi_event_update_stats(struct ath10k *ar,
 -                                        struct sk_buff *skb)
 +static int
 +ath10k_wmi_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb,
 +                               struct wmi_vdev_start_ev_arg *arg)
  {
 -      ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
 -      ath10k_debug_fw_stats_process(ar, skb);
 +      struct wmi_vdev_start_response_event *ev = (void *)skb->data;
 +
 +      if (skb->len < sizeof(*ev))
 +              return -EPROTO;
 +
 +      skb_pull(skb, sizeof(*ev));
 +      arg->vdev_id = ev->vdev_id;
 +      arg->req_id = ev->req_id;
 +      arg->resp_type = ev->resp_type;
 +      arg->status = ev->status;
 +
 +      return 0;
  }
  
 -static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
 -                                           struct sk_buff *skb)
 +void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb)
  {
 -      struct wmi_vdev_start_response_event *ev;
 +      struct wmi_vdev_start_ev_arg arg = {};
 +      int ret;
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n");
  
 -      ev = (struct wmi_vdev_start_response_event *)skb->data;
 +      ret = ath10k_wmi_pull_vdev_start(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret);
 +              return;
 +      }
  
 -      if (WARN_ON(__le32_to_cpu(ev->status)))
 +      if (WARN_ON(__le32_to_cpu(arg.status)))
                return;
  
        complete(&ar->vdev_setup_done);
  }
  
 -static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
 -                                        struct sk_buff *skb)
 +void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n");
        complete(&ar->vdev_setup_done);
  }
  
 -static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
 -                                            struct sk_buff *skb)
 +static int
 +ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb,
 +                              struct wmi_peer_kick_ev_arg *arg)
 +{
 +      struct wmi_peer_sta_kickout_event *ev = (void *)skb->data;
 +
 +      if (skb->len < sizeof(*ev))
 +              return -EPROTO;
 +
 +      skb_pull(skb, sizeof(*ev));
 +      arg->mac_addr = ev->peer_macaddr.addr;
 +
 +      return 0;
 +}
 +
 +void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb)
  {
 -      struct wmi_peer_sta_kickout_event *ev;
 +      struct wmi_peer_kick_ev_arg arg = {};
        struct ieee80211_sta *sta;
 +      int ret;
  
 -      ev = (struct wmi_peer_sta_kickout_event *)skb->data;
 +      ret = ath10k_wmi_pull_peer_kick(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse peer kickout event: %d\n",
 +                          ret);
 +              return;
 +      }
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n",
 -                 ev->peer_macaddr.addr);
 +                 arg.mac_addr);
  
        rcu_read_lock();
  
 -      sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL);
 +      sta = ieee80211_find_sta_by_ifaddr(ar->hw, arg.mac_addr, NULL);
        if (!sta) {
                ath10k_warn(ar, "Spurious quick kickout for STA %pM\n",
 -                          ev->peer_macaddr.addr);
 +                          arg.mac_addr);
                goto exit;
        }
  
@@@ -1978,7 -1641,7 +1978,7 @@@ exit
  static void ath10k_wmi_update_tim(struct ath10k *ar,
                                  struct ath10k_vif *arvif,
                                  struct sk_buff *bcn,
 -                                struct wmi_bcn_info *bcn_info)
 +                                const struct wmi_tim_info *tim_info)
  {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data;
        struct ieee80211_tim_ie *tim;
  
        /* if next SWBA has no tim_changed the tim_bitmap is garbage.
         * we must copy the bitmap upon change and reuse it later */
 -      if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) {
 +      if (__le32_to_cpu(tim_info->tim_changed)) {
                int i;
  
                BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) !=
 -                           sizeof(bcn_info->tim_info.tim_bitmap));
 +                           sizeof(tim_info->tim_bitmap));
  
                for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) {
 -                      t = bcn_info->tim_info.tim_bitmap[i / 4];
 +                      t = tim_info->tim_bitmap[i / 4];
                        v = __le32_to_cpu(t);
                        arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF;
                }
                return;
        }
  
 -      tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
 +      tim->bitmap_ctrl = !!__le32_to_cpu(tim_info->tim_mcast);
        memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
  
        if (tim->dtim_count == 0) {
                ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
  
 -              if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1)
 +              if (__le32_to_cpu(tim_info->tim_mcast) == 1)
                        ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
        }
  
  }
  
  static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len,
 -                                 struct wmi_p2p_noa_info *noa)
 +                                 const struct wmi_p2p_noa_info *noa)
  {
        struct ieee80211_p2p_noa_attr *noa_attr;
        u8  ctwindow_oppps = noa->ctwindow_oppps;
        *noa_attr_len = __cpu_to_le16(attr_len);
  }
  
 -static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa)
 +static u32 ath10k_p2p_calc_noa_ie_len(const struct wmi_p2p_noa_info *noa)
  {
        u32 len = 0;
        u8 noa_descriptors = noa->num_descriptors;
  
  static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif,
                                  struct sk_buff *bcn,
 -                                struct wmi_bcn_info *bcn_info)
 +                                const struct wmi_p2p_noa_info *noa)
  {
 -      struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info;
        u8 *new_data, *old_data = arvif->u.ap.noa_data;
        u32 new_len;
  
@@@ -2168,59 -1832,22 +2168,59 @@@ cleanup
        kfree(old_data);
  }
  
 -static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
 +static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                    struct wmi_swba_ev_arg *arg)
  {
 -      struct wmi_host_swba_event *ev;
 +      struct wmi_host_swba_event *ev = (void *)skb->data;
 +      u32 map;
 +      size_t i;
 +
 +      if (skb->len < sizeof(*ev))
 +              return -EPROTO;
 +
 +      skb_pull(skb, sizeof(*ev));
 +      arg->vdev_map = ev->vdev_map;
 +
 +      for (i = 0, map = __le32_to_cpu(ev->vdev_map); map; map >>= 1) {
 +              if (!(map & BIT(0)))
 +                      continue;
 +
 +              /* If this happens there were some changes in firmware and
 +               * ath10k should update the max size of tim_info array.
 +               */
 +              if (WARN_ON_ONCE(i == ARRAY_SIZE(arg->tim_info)))
 +                      break;
 +
 +              arg->tim_info[i] = &ev->bcn_info[i].tim_info;
 +              arg->noa_info[i] = &ev->bcn_info[i].p2p_noa_info;
 +              i++;
 +      }
 +
 +      return 0;
 +}
 +
 +void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
 +{
 +      struct wmi_swba_ev_arg arg = {};
        u32 map;
        int i = -1;
 -      struct wmi_bcn_info *bcn_info;
 +      const struct wmi_tim_info *tim_info;
 +      const struct wmi_p2p_noa_info *noa_info;
        struct ath10k_vif *arvif;
        struct sk_buff *bcn;
        dma_addr_t paddr;
        int ret, vdev_id = 0;
  
 -      ev = (struct wmi_host_swba_event *)skb->data;
 -      map = __le32_to_cpu(ev->vdev_map);
 +      ret = ath10k_wmi_pull_swba(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse swba event: %d\n", ret);
 +              return;
 +      }
 +
 +      map = __le32_to_cpu(arg.vdev_map);
  
        ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n",
 -                 ev->vdev_map);
 +                 map);
  
        for (; map; map >>= 1, vdev_id++) {
                if (!(map & 0x1))
                        break;
                }
  
 -              bcn_info = &ev->bcn_info[i];
 +              tim_info = arg.tim_info[i];
 +              noa_info = arg.noa_info[i];
  
                ath10k_dbg(ar, ATH10K_DBG_MGMT,
                           "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d bitmap 0x%08x%08x%08x%08x\n",
                           i,
 -                         __le32_to_cpu(bcn_info->tim_info.tim_len),
 -                         __le32_to_cpu(bcn_info->tim_info.tim_mcast),
 -                         __le32_to_cpu(bcn_info->tim_info.tim_changed),
 -                         __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending),
 -                         __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]),
 -                         __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]),
 -                         __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]),
 -                         __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0]));
 +                         __le32_to_cpu(tim_info->tim_len),
 +                         __le32_to_cpu(tim_info->tim_mcast),
 +                         __le32_to_cpu(tim_info->tim_changed),
 +                         __le32_to_cpu(tim_info->tim_num_ps_pending),
 +                         __le32_to_cpu(tim_info->tim_bitmap[3]),
 +                         __le32_to_cpu(tim_info->tim_bitmap[2]),
 +                         __le32_to_cpu(tim_info->tim_bitmap[1]),
 +                         __le32_to_cpu(tim_info->tim_bitmap[0]));
  
                arvif = ath10k_get_arvif(ar, vdev_id);
                if (arvif == NULL) {
                }
  
                ath10k_tx_h_seq_no(arvif->vif, bcn);
 -              ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info);
 -              ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
 +              ath10k_wmi_update_tim(ar, arvif, bcn, tim_info);
 +              ath10k_wmi_update_noa(ar, arvif, bcn, noa_info);
  
                spin_lock_bh(&ar->data_lock);
  
@@@ -2320,7 -1946,8 +2320,7 @@@ skip
        }
  }
  
 -static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar,
 -                                             struct sk_buff *skb)
 +void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
  }
@@@ -2441,9 -2068,9 +2441,9 @@@ static int ath10k_dfs_fft_report(struc
        return 0;
  }
  
 -static void ath10k_wmi_event_dfs(struct ath10k *ar,
 -                               const struct wmi_phyerr *phyerr,
 -                               u64 tsf)
 +void ath10k_wmi_event_dfs(struct ath10k *ar,
 +                        const struct wmi_phyerr *phyerr,
 +                        u64 tsf)
  {
        int buf_len, tlv_len, res, i = 0;
        const struct phyerr_tlv *tlv;
        }
  }
  
 -static void
 -ath10k_wmi_event_spectral_scan(struct ath10k *ar,
 -                             const struct wmi_phyerr *phyerr,
 -                             u64 tsf)
 +void ath10k_wmi_event_spectral_scan(struct ath10k *ar,
 +                                  const struct wmi_phyerr *phyerr,
 +                                  u64 tsf)
  {
        int buf_len, tlv_len, res, i = 0;
        struct phyerr_tlv *tlv;
        }
  }
  
 -static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
 +static int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                      struct wmi_phyerr_ev_arg *arg)
  {
 -      const struct wmi_phyerr_event *ev;
 +      struct wmi_phyerr_event *ev = (void *)skb->data;
 +
 +      if (skb->len < sizeof(*ev))
 +              return -EPROTO;
 +
 +      arg->num_phyerrs = ev->num_phyerrs;
 +      arg->tsf_l32 = ev->tsf_l32;
 +      arg->tsf_u32 = ev->tsf_u32;
 +      arg->buf_len = __cpu_to_le32(skb->len - sizeof(*ev));
 +      arg->phyerrs = ev->phyerrs;
 +
 +      return 0;
 +}
 +
 +void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
 +{
 +      struct wmi_phyerr_ev_arg arg = {};
        const struct wmi_phyerr *phyerr;
        u32 count, i, buf_len, phy_err_code;
        u64 tsf;
 -      int left_len = skb->len;
 +      int left_len, ret;
  
        ATH10K_DFS_STAT_INC(ar, phy_errors);
  
 -      /* Check if combined event available */
 -      if (left_len < sizeof(*ev)) {
 -              ath10k_warn(ar, "wmi phyerr combined event wrong len\n");
 +      ret = ath10k_wmi_pull_phyerr(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse phyerr event: %d\n", ret);
                return;
        }
  
 -      left_len -= sizeof(*ev);
 +      left_len = __le32_to_cpu(arg.buf_len);
  
        /* Check number of included events */
 -      ev = (const struct wmi_phyerr_event *)skb->data;
 -      count = __le32_to_cpu(ev->num_phyerrs);
 +      count = __le32_to_cpu(arg.num_phyerrs);
  
 -      tsf = __le32_to_cpu(ev->tsf_u32);
 +      tsf = __le32_to_cpu(arg.tsf_u32);
        tsf <<= 32;
 -      tsf |= __le32_to_cpu(ev->tsf_l32);
 +      tsf |= __le32_to_cpu(arg.tsf_l32);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi event phyerr count %d tsf64 0x%llX\n",
                   count, tsf);
  
 -      phyerr = ev->phyerrs;
 +      phyerr = arg.phyerrs;
        for (i = 0; i < count; i++) {
                /* Check if we can read event header */
                if (left_len < sizeof(*phyerr)) {
        }
  }
  
 -static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
 +void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_profile_match(struct ath10k *ar,
 -                                         struct sk_buff *skb)
 +void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n");
  }
  
 -static void ath10k_wmi_event_debug_print(struct ath10k *ar,
 -                                       struct sk_buff *skb)
 +void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb)
  {
        char buf[101], c;
        int i;
        ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf);
  }
  
 -static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
 +void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar,
 -                                             struct sk_buff *skb)
 +void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
 -                                                  struct sk_buff *skb)
 +void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar,
 +                                           struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
 -                                                  struct sk_buff *skb)
 +void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar,
 +                                           struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar,
 -                                            struct sk_buff *skb)
 +void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar,
 -                                           struct sk_buff *skb)
 +void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_dcs_interference(struct ath10k *ar,
 -                                            struct sk_buff *skb)
 +void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar,
 -                                           struct sk_buff *skb)
 +void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar,
 -                                         struct sk_buff *skb)
 +void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar,
 -                                              struct sk_buff *skb)
 +void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar,
 -                                          struct sk_buff *skb)
 +void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_delba_complete(struct ath10k *ar,
 -                                          struct sk_buff *skb)
 +void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_addba_complete(struct ath10k *ar,
 -                                          struct sk_buff *skb)
 +void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
 -                                                     struct sk_buff *skb)
 +void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar,
 +                                              struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar,
 -                                           struct sk_buff *skb)
 +void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar,
 -                                            struct sk_buff *skb)
 +void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n");
  }
  
 -static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar,
 -                                           struct sk_buff *skb)
 +void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb)
  {
        ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
  }
@@@ -2808,9 -2435,8 +2808,9 @@@ static int ath10k_wmi_alloc_host_mem(st
        return 0;
  }
  
 -static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb,
 -                                         struct wmi_svc_rdy_ev_arg *arg)
 +static int
 +ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                 struct wmi_svc_rdy_ev_arg *arg)
  {
        struct wmi_service_ready_event *ev;
        size_t i, n;
        return 0;
  }
  
 -static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb,
 -                                        struct wmi_svc_rdy_ev_arg *arg)
 +static int
 +ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                struct wmi_svc_rdy_ev_arg *arg)
  {
        struct wmi_10x_service_ready_event *ev;
        int i, n;
        return 0;
  }
  
 -static void ath10k_wmi_event_service_ready(struct ath10k *ar,
 -                                         struct sk_buff *skb)
 +void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb)
  {
        struct wmi_svc_rdy_ev_arg arg = {};
        u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
        int ret;
  
 -      memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
 -
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg);
 -              wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map,
 -                              arg.service_map_len);
 -      } else {
 -              ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg);
 -              wmi_main_svc_map(arg.service_map, ar->wmi.svc_map,
 -                               arg.service_map_len);
 -      }
 -
 +      ret = ath10k_wmi_pull_svc_rdy(ar, skb, &arg);
        if (ret) {
                ath10k_warn(ar, "failed to parse service ready: %d\n", ret);
                return;
        }
  
 +      memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
 +      ath10k_wmi_map_svc(ar, arg.service_map, ar->wmi.svc_map,
 +                         arg.service_map_len);
 +
        ar->hw_min_tx_power = __le32_to_cpu(arg.min_tx_power);
        ar->hw_max_tx_power = __le32_to_cpu(arg.max_tx_power);
        ar->ht_cap_info = __le32_to_cpu(arg.ht_cap);
        }
  
        ath10k_dbg(ar, ATH10K_DBG_WMI,
 -                 "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
 +                 "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x fw_build 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
                   __le32_to_cpu(arg.min_tx_power),
                   __le32_to_cpu(arg.max_tx_power),
                   __le32_to_cpu(arg.ht_cap),
                   __le32_to_cpu(arg.vht_cap),
                   __le32_to_cpu(arg.sw_ver0),
                   __le32_to_cpu(arg.sw_ver1),
 +                 __le32_to_cpu(arg.fw_build),
                   __le32_to_cpu(arg.phy_capab),
                   __le32_to_cpu(arg.num_rf_chains),
                   __le32_to_cpu(arg.eeprom_rd),
        complete(&ar->wmi.service_ready);
  }
  
 -static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
 +static int ath10k_wmi_op_pull_rdy_ev(struct ath10k *ar, struct sk_buff *skb,
 +                                   struct wmi_rdy_ev_arg *arg)
 +{
 +      struct wmi_ready_event *ev = (void *)skb->data;
 +
 +      if (skb->len < sizeof(*ev))
 +              return -EPROTO;
 +
 +      skb_pull(skb, sizeof(*ev));
 +      arg->sw_version = ev->sw_version;
 +      arg->abi_version = ev->abi_version;
 +      arg->status = ev->status;
 +      arg->mac_addr = ev->mac_addr.addr;
 +
 +      return 0;
 +}
 +
 +int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
 +{
 +      struct wmi_rdy_ev_arg arg = {};
 +      int ret;
 +
 +      ret = ath10k_wmi_pull_rdy(ar, skb, &arg);
 +      if (ret) {
 +              ath10k_warn(ar, "failed to parse ready event: %d\n", ret);
 +              return ret;
 +      }
 +
 +      ath10k_dbg(ar, ATH10K_DBG_WMI,
 +                 "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
 +                 __le32_to_cpu(arg.sw_version),
 +                 __le32_to_cpu(arg.abi_version),
 +                 arg.mac_addr,
 +                 __le32_to_cpu(arg.status));
 +
 +      ether_addr_copy(ar->mac_addr, arg.mac_addr);
 +      complete(&ar->wmi.unified_ready);
 +      return 0;
 +}
 +
 +static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb)
  {
 -      struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
 +      const struct wmi_pdev_temperature_event *ev;
  
 +      ev = (struct wmi_pdev_temperature_event *)skb->data;
        if (WARN_ON(skb->len < sizeof(*ev)))
 -              return -EINVAL;
 -
 -      ether_addr_copy(ar->mac_addr, ev->mac_addr.addr);
 -
 -      ath10k_dbg(ar, ATH10K_DBG_WMI,
 -                 "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n",
 -                 __le32_to_cpu(ev->sw_version),
 -                 __le32_to_cpu(ev->abi_version),
 -                 ev->mac_addr.addr,
 -                 __le32_to_cpu(ev->status), skb->len, sizeof(*ev));
 +              return -EPROTO;
  
 -      complete(&ar->wmi.unified_ready);
 +      ath10k_thermal_event_temperature(ar, __le32_to_cpu(ev->temperature));
        return 0;
  }
  
 -static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
 +static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb)
  {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_event_id id;
        dev_kfree_skb(skb);
  }
  
 -static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
 +static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb)
  {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_10x_event_id id;
@@@ -3282,7 -2882,7 +3282,7 @@@ out
        dev_kfree_skb(skb);
  }
  
 -static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb)
 +static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb)
  {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_10_2_event_id id;
        case WMI_10_2_READY_EVENTID:
                ath10k_wmi_event_ready(ar, skb);
                break;
 +      case WMI_10_2_PDEV_TEMPERATURE_EVENTID:
 +              ath10k_wmi_event_temperature(ar, skb);
 +              break;
        case WMI_10_2_RTT_KEEPALIVE_EVENTID:
        case WMI_10_2_GPIO_INPUT_EVENTID:
        case WMI_10_2_PEER_RATECODE_LIST_EVENTID:
  
  static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb)
  {
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
 -                      ath10k_wmi_10_2_process_rx(ar, skb);
 -              else
 -                      ath10k_wmi_10x_process_rx(ar, skb);
 -      } else {
 -              ath10k_wmi_main_process_rx(ar, skb);
 -      }
 +      int ret;
 +
 +      ret = ath10k_wmi_rx(ar, skb);
 +      if (ret)
 +              ath10k_warn(ar, "failed to process wmi rx: %d\n", ret);
  }
  
  int ath10k_wmi_connect(struct ath10k *ar)
        return 0;
  }
  
 -static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd,
 -                                            u16 rd2g, u16 rd5g, u16 ctl2g,
 -                                            u16 ctl5g)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g,
 +                            u16 ctl2g, u16 ctl5g,
 +                            enum wmi_dfs_region dfs_reg)
  {
        struct wmi_pdev_set_regdomain_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data;
        cmd->reg_domain = __cpu_to_le32(rd);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n",
                   rd, rd2g, rd5g, ctl2g, ctl5g);
 -
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->pdev_set_regdomain_cmdid);
 +      return skb;
  }
  
 -static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd,
 -                                           u16 rd2g, u16 rd5g,
 -                                           u16 ctl2g, u16 ctl5g,
 -                                           enum wmi_dfs_region dfs_reg)
 +static struct sk_buff *
 +ath10k_wmi_10x_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16
 +                                rd5g, u16 ctl2g, u16 ctl5g,
 +                                enum wmi_dfs_region dfs_reg)
  {
        struct wmi_pdev_set_regdomain_cmd_10x *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)skb->data;
        cmd->reg_domain = __cpu_to_le32(rd);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n",
                   rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg);
 -
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->pdev_set_regdomain_cmdid);
 -}
 -
 -int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
 -                                u16 rd5g, u16 ctl2g, u16 ctl5g,
 -                                enum wmi_dfs_region dfs_reg)
 -{
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
 -              return ath10k_wmi_10x_pdev_set_regdomain(ar, rd, rd2g, rd5g,
 -                                                      ctl2g, ctl5g, dfs_reg);
 -      else
 -              return ath10k_wmi_main_pdev_set_regdomain(ar, rd, rd2g, rd5g,
 -                                                       ctl2g, ctl5g);
 +      return skb;
  }
  
 -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pdev_suspend(struct ath10k *ar, u32 suspend_opt)
  {
        struct wmi_pdev_suspend_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
        cmd->suspend_opt = __cpu_to_le32(suspend_opt);
  
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_pdev_resume_target(struct ath10k *ar)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pdev_resume(struct ath10k *ar)
  {
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, 0);
 -      if (skb == NULL)
 -              return -ENOMEM;
 +      if (!skb)
 +              return ERR_PTR(-ENOMEM);
  
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pdev_set_param(struct ath10k *ar, u32 id, u32 value)
  {
        struct wmi_pdev_set_param_cmd *cmd;
        struct sk_buff *skb;
        if (id == WMI_PDEV_PARAM_UNSUPPORTED) {
                ath10k_warn(ar, "pdev param %d not supported by firmware\n",
                            id);
 -              return -EOPNOTSUPP;
 +              return ERR_PTR(-EOPNOTSUPP);
        }
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_pdev_set_param_cmd *)skb->data;
        cmd->param_id    = __cpu_to_le32(id);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n",
                   id, value);
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
 +      return skb;
  }
  
 -static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
 -                                         struct wmi_host_mem_chunks *chunks)
 +void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
 +                                  struct wmi_host_mem_chunks *chunks)
  {
        struct host_memory_chunk *chunk;
        int i;
        }
  }
  
 -static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
 +static struct sk_buff *ath10k_wmi_op_gen_init(struct ath10k *ar)
  {
        struct wmi_init_cmd *cmd;
        struct sk_buff *buf;
  
        buf = ath10k_wmi_alloc_skb(ar, len);
        if (!buf)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_init_cmd *)buf->data;
  
        ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n");
 -      return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
 +      return buf;
  }
  
 -static int ath10k_wmi_10x_cmd_init(struct ath10k *ar)
 +static struct sk_buff *ath10k_wmi_10_1_op_gen_init(struct ath10k *ar)
  {
        struct wmi_init_cmd_10x *cmd;
        struct sk_buff *buf;
  
        buf = ath10k_wmi_alloc_skb(ar, len);
        if (!buf)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_init_cmd_10x *)buf->data;
  
        ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n");
 -      return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
 +      return buf;
  }
  
 -static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar)
 +static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
  {
        struct wmi_init_cmd_10_2 *cmd;
        struct sk_buff *buf;
  
        buf = ath10k_wmi_alloc_skb(ar, len);
        if (!buf)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_init_cmd_10_2 *)buf->data;
  
        ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n");
 -      return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
 -}
 -
 -int ath10k_wmi_cmd_init(struct ath10k *ar)
 -{
 -      int ret;
 -
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
 -                      ret = ath10k_wmi_10_2_cmd_init(ar);
 -              else
 -                      ret = ath10k_wmi_10x_cmd_init(ar);
 -      } else {
 -              ret = ath10k_wmi_main_cmd_init(ar);
 -      }
 -
 -      return ret;
 +      return buf;
  }
  
 -static int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg)
 +int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg)
  {
        if (arg->ie_len && !arg->ie)
                return -EINVAL;
@@@ -3822,8 -3450,9 +3822,8 @@@ ath10k_wmi_start_scan_tlvs_len(const st
        return len;
  }
  
 -static void
 -ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
 -                               const struct wmi_start_scan_arg *arg)
 +void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
 +                                    const struct wmi_start_scan_arg *arg)
  {
        u32 scan_id;
        u32 scan_req_id;
@@@ -3917,60 -3546,46 +3917,60 @@@ ath10k_wmi_put_start_scan_tlvs(struct w
        }
  }
  
 -int ath10k_wmi_start_scan(struct ath10k *ar,
 -                        const struct wmi_start_scan_arg *arg)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_start_scan(struct ath10k *ar,
 +                           const struct wmi_start_scan_arg *arg)
  {
 +      struct wmi_start_scan_cmd *cmd;
        struct sk_buff *skb;
        size_t len;
        int ret;
  
        ret = ath10k_wmi_start_scan_verify(arg);
        if (ret)
 -              return ret;
 -
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
 -              len = sizeof(struct wmi_10x_start_scan_cmd) +
 -                    ath10k_wmi_start_scan_tlvs_len(arg);
 -      else
 -              len = sizeof(struct wmi_start_scan_cmd) +
 -                    ath10k_wmi_start_scan_tlvs_len(arg);
 +              return ERR_PTR(ret);
  
 +      len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg);
        skb = ath10k_wmi_alloc_skb(ar, len);
        if (!skb)
 -              return -ENOMEM;
 -
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              struct wmi_10x_start_scan_cmd *cmd;
 +              return ERR_PTR(-ENOMEM);
  
 -              cmd = (struct wmi_10x_start_scan_cmd *)skb->data;
 -              ath10k_wmi_put_start_scan_common(&cmd->common, arg);
 -              ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
 -      } else {
 -              struct wmi_start_scan_cmd *cmd;
 +      cmd = (struct wmi_start_scan_cmd *)skb->data;
  
 -              cmd = (struct wmi_start_scan_cmd *)skb->data;
 -              cmd->burst_duration_ms = __cpu_to_le32(0);
 +      ath10k_wmi_put_start_scan_common(&cmd->common, arg);
 +      ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
  
 -              ath10k_wmi_put_start_scan_common(&cmd->common, arg);
 -              ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
 -      }
 +      cmd->burst_duration_ms = __cpu_to_le32(0);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n");
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid);
 +      return skb;
 +}
 +
 +static struct sk_buff *
 +ath10k_wmi_10x_op_gen_start_scan(struct ath10k *ar,
 +                               const struct wmi_start_scan_arg *arg)
 +{
 +      struct wmi_10x_start_scan_cmd *cmd;
 +      struct sk_buff *skb;
 +      size_t len;
 +      int ret;
 +
 +      ret = ath10k_wmi_start_scan_verify(arg);
 +      if (ret)
 +              return ERR_PTR(ret);
 +
 +      len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg);
 +      skb = ath10k_wmi_alloc_skb(ar, len);
 +      if (!skb)
 +              return ERR_PTR(-ENOMEM);
 +
 +      cmd = (struct wmi_10x_start_scan_cmd *)skb->data;
 +
 +      ath10k_wmi_put_start_scan_common(&cmd->common, arg);
 +      ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
 +
 +      ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi 10x start scan\n");
 +      return skb;
  }
  
  void ath10k_wmi_start_scan_init(struct ath10k *ar,
        arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF";
  }
  
 -int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_stop_scan(struct ath10k *ar,
 +                          const struct wmi_stop_scan_arg *arg)
  {
        struct wmi_stop_scan_cmd *cmd;
        struct sk_buff *skb;
        u32 req_id;
  
        if (arg->req_id > 0xFFF)
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
        if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF)
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        scan_id = arg->u.scan_id;
        scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX;
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n",
                   arg->req_id, arg->req_type, arg->u.scan_id);
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id,
 -                         enum wmi_vdev_type type,
 -                         enum wmi_vdev_subtype subtype,
 -                         const u8 macaddr[ETH_ALEN])
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_create(struct ath10k *ar, u32 vdev_id,
 +                            enum wmi_vdev_type type,
 +                            enum wmi_vdev_subtype subtype,
 +                            const u8 macaddr[ETH_ALEN])
  {
        struct wmi_vdev_create_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_create_cmd *)skb->data;
        cmd->vdev_id      = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "WMI vdev create: id %d type %d subtype %d macaddr %pM\n",
                   vdev_id, type, subtype, macaddr);
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id)
  {
        struct wmi_vdev_delete_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_delete_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "WMI vdev delete id %d\n", vdev_id);
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid);
 +      return skb;
  }
  
 -static int
 -ath10k_wmi_vdev_start_restart(struct ath10k *ar,
 -                            const struct wmi_vdev_start_request_arg *arg,
 -                            u32 cmd_id)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_start(struct ath10k *ar,
 +                           const struct wmi_vdev_start_request_arg *arg,
 +                           bool restart)
  {
        struct wmi_vdev_start_request_cmd *cmd;
        struct sk_buff *skb;
        const char *cmdname;
        u32 flags = 0;
  
 -      if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
 -          cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
 -              return -EINVAL;
        if (WARN_ON(arg->ssid && arg->ssid_len == 0))
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
        if (WARN_ON(arg->hidden_ssid && !arg->ssid))
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
        if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid)))
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
  
 -      if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid)
 -              cmdname = "start";
 -      else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid)
 +      if (restart)
                cmdname = "restart";
        else
 -              return -EINVAL; /* should not happen, we already check cmd_id */
 +              cmdname = "start";
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        if (arg->hidden_ssid)
                flags |= WMI_VDEV_START_HIDDEN_SSID;
                   flags, arg->channel.freq, arg->channel.mode,
                   cmd->chan.flags, arg->channel.max_power);
  
 -      return ath10k_wmi_cmd_send(ar, skb, cmd_id);
 -}
 -
 -int ath10k_wmi_vdev_start(struct ath10k *ar,
 -                        const struct wmi_vdev_start_request_arg *arg)
 -{
 -      u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid;
 -
 -      return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 -}
 -
 -int ath10k_wmi_vdev_restart(struct ath10k *ar,
 -                          const struct wmi_vdev_start_request_arg *arg)
 -{
 -      u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid;
 -
 -      return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id)
  {
        struct wmi_vdev_stop_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_stop_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id);
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid,
 +                        const u8 *bssid)
  {
        struct wmi_vdev_up_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_up_cmd *)skb->data;
        cmd->vdev_id       = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n",
                   vdev_id, aid, bssid);
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id)
  {
        struct wmi_vdev_down_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_down_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi mgmt vdev down id 0x%x\n", vdev_id);
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id,
 -                            u32 param_id, u32 param_value)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id,
 +                               u32 param_id, u32 param_value)
  {
        struct wmi_vdev_set_param_cmd *cmd;
        struct sk_buff *skb;
                ath10k_dbg(ar, ATH10K_DBG_WMI,
                           "vdev param %d not supported by firmware\n",
                            param_id);
 -              return -EOPNOTSUPP;
 +              return ERR_PTR(-EOPNOTSUPP);
        }
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_set_param_cmd *)skb->data;
        cmd->vdev_id     = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi vdev id 0x%x set param %d value %d\n",
                   vdev_id, param_id, param_value);
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_install_key(struct ath10k *ar,
 -                              const struct wmi_vdev_install_key_arg *arg)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_install_key(struct ath10k *ar,
 +                                 const struct wmi_vdev_install_key_arg *arg)
  {
        struct wmi_vdev_install_key_cmd *cmd;
        struct sk_buff *skb;
  
        if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL)
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
        if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL)
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + arg->key_len);
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_install_key_cmd *)skb->data;
        cmd->vdev_id       = __cpu_to_le32(arg->vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi vdev install key idx %d cipher %d len %d\n",
                   arg->key_idx, arg->key_cipher, arg->key_len);
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->vdev_install_key_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar,
 -                                const struct wmi_vdev_spectral_conf_arg *arg)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_spectral_conf(struct ath10k *ar,
 +                                   const struct wmi_vdev_spectral_conf_arg *arg)
  {
        struct wmi_vdev_spectral_conf_cmd *cmd;
        struct sk_buff *skb;
 -      u32 cmdid;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_spectral_conf_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
        cmd->scan_dbm_adj = __cpu_to_le32(arg->scan_dbm_adj);
        cmd->scan_chn_mask = __cpu_to_le32(arg->scan_chn_mask);
  
 -      cmdid = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid;
 -      return ath10k_wmi_cmd_send(ar, skb, cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger,
 -                                  u32 enable)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id,
 +                                     u32 trigger, u32 enable)
  {
        struct wmi_vdev_spectral_enable_cmd *cmd;
        struct sk_buff *skb;
 -      u32 cmdid;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_vdev_spectral_enable_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
        cmd->trigger_cmd = __cpu_to_le32(trigger);
        cmd->enable_cmd = __cpu_to_le32(enable);
  
 -      cmdid = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid;
 -      return ath10k_wmi_cmd_send(ar, skb, cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id,
 -                         const u8 peer_addr[ETH_ALEN])
 +static struct sk_buff *
 +ath10k_wmi_op_gen_peer_create(struct ath10k *ar, u32 vdev_id,
 +                            const u8 peer_addr[ETH_ALEN])
  {
        struct wmi_peer_create_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_peer_create_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi peer create vdev_id %d peer_addr %pM\n",
                   vdev_id, peer_addr);
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id,
 -                         const u8 peer_addr[ETH_ALEN])
 +static struct sk_buff *
 +ath10k_wmi_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id,
 +                            const u8 peer_addr[ETH_ALEN])
  {
        struct wmi_peer_delete_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_peer_delete_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi peer delete vdev_id %d peer_addr %pM\n",
                   vdev_id, peer_addr);
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id,
 -                        const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id,
 +                           const u8 peer_addr[ETH_ALEN], u32 tid_bitmap)
  {
        struct wmi_peer_flush_tids_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_peer_flush_tids_cmd *)skb->data;
        cmd->vdev_id         = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n",
                   vdev_id, peer_addr, tid_bitmap);
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id,
 -                            const u8 *peer_addr, enum wmi_peer_param param_id,
 -                            u32 param_value)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id,
 +                               const u8 *peer_addr,
 +                               enum wmi_peer_param param_id,
 +                               u32 param_value)
  {
        struct wmi_peer_set_param_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_peer_set_param_cmd *)skb->data;
        cmd->vdev_id     = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi vdev %d peer 0x%pM set param %d value %d\n",
                   vdev_id, peer_addr, param_id, param_value);
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id,
 -                        enum wmi_sta_ps_mode psmode)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id,
 +                           enum wmi_sta_ps_mode psmode)
  {
        struct wmi_sta_powersave_mode_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data;
        cmd->vdev_id     = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi set powersave id 0x%x mode %d\n",
                   vdev_id, psmode);
 -
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->sta_powersave_mode_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id,
 -                              enum wmi_sta_powersave_param param_id,
 -                              u32 value)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id,
 +                           enum wmi_sta_powersave_param param_id,
 +                           u32 value)
  {
        struct wmi_sta_powersave_param_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_sta_powersave_param_cmd *)skb->data;
        cmd->vdev_id     = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi sta ps param vdev_id 0x%x param %d value %d\n",
                   vdev_id, param_id, value);
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->sta_powersave_param_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
 -                             enum wmi_ap_ps_peer_param param_id, u32 value)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac,
 +                          enum wmi_ap_ps_peer_param param_id, u32 value)
  {
        struct wmi_ap_ps_peer_cmd *cmd;
        struct sk_buff *skb;
  
        if (!mac)
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_ap_ps_peer_cmd *)skb->data;
        cmd->vdev_id = __cpu_to_le32(vdev_id);
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n",
                   vdev_id, param_id, value, mac);
 -
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->ap_ps_peer_param_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_scan_chan_list(struct ath10k *ar,
 -                            const struct wmi_scan_chan_list_arg *arg)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_scan_chan_list(struct ath10k *ar,
 +                               const struct wmi_scan_chan_list_arg *arg)
  {
        struct wmi_scan_chan_list_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, len);
        if (!skb)
 -              return -EINVAL;
 +              return ERR_PTR(-EINVAL);
  
        cmd = (struct wmi_scan_chan_list_cmd *)skb->data;
        cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
                ath10k_wmi_put_wmi_channel(ci, ch);
        }
  
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
 +      return skb;
  }
  
  static void
@@@ -4577,9 -4209,12 +4577,9 @@@ ath10k_wmi_peer_assoc_fill_10_2(struct 
        cmd->info0 = __cpu_to_le32(info0);
  }
  
 -int ath10k_wmi_peer_assoc(struct ath10k *ar,
 -                        const struct wmi_peer_assoc_complete_arg *arg)
 +static int
 +ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg)
  {
 -      struct sk_buff *skb;
 -      int len;
 -
        if (arg->peer_mpdu_density > 16)
                return -EINVAL;
        if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES)
        if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES)
                return -EINVAL;
  
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
 -                      len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd);
 -              else
 -                      len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd);
 -      } else {
 -              len = sizeof(struct wmi_main_peer_assoc_complete_cmd);
 -      }
 +      return 0;
 +}
 +
 +static struct sk_buff *
 +ath10k_wmi_op_gen_peer_assoc(struct ath10k *ar,
 +                           const struct wmi_peer_assoc_complete_arg *arg)
 +{
 +      size_t len = sizeof(struct wmi_main_peer_assoc_complete_cmd);
 +      struct sk_buff *skb;
 +      int ret;
 +
 +      ret = ath10k_wmi_peer_assoc_check_arg(arg);
 +      if (ret)
 +              return ERR_PTR(ret);
  
        skb = ath10k_wmi_alloc_skb(ar, len);
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
 -                      ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg);
 -              else
 -                      ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
 -      } else {
 -              ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg);
 -      }
 +      ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg);
 +
 +      ath10k_dbg(ar, ATH10K_DBG_WMI,
 +                 "wmi peer assoc vdev %d addr %pM (%s)\n",
 +                 arg->vdev_id, arg->addr,
 +                 arg->peer_reassoc ? "reassociate" : "new");
 +      return skb;
 +}
 +
 +static struct sk_buff *
 +ath10k_wmi_10_1_op_gen_peer_assoc(struct ath10k *ar,
 +                                const struct wmi_peer_assoc_complete_arg *arg)
 +{
 +      size_t len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd);
 +      struct sk_buff *skb;
 +      int ret;
 +
 +      ret = ath10k_wmi_peer_assoc_check_arg(arg);
 +      if (ret)
 +              return ERR_PTR(ret);
 +
 +      skb = ath10k_wmi_alloc_skb(ar, len);
 +      if (!skb)
 +              return ERR_PTR(-ENOMEM);
 +
 +      ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
 +
 +      ath10k_dbg(ar, ATH10K_DBG_WMI,
 +                 "wmi peer assoc vdev %d addr %pM (%s)\n",
 +                 arg->vdev_id, arg->addr,
 +                 arg->peer_reassoc ? "reassociate" : "new");
 +      return skb;
 +}
 +
 +static struct sk_buff *
 +ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar,
 +                                const struct wmi_peer_assoc_complete_arg *arg)
 +{
 +      size_t len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd);
 +      struct sk_buff *skb;
 +      int ret;
 +
 +      ret = ath10k_wmi_peer_assoc_check_arg(arg);
 +      if (ret)
 +              return ERR_PTR(ret);
 +
 +      skb = ath10k_wmi_alloc_skb(ar, len);
 +      if (!skb)
 +              return ERR_PTR(-ENOMEM);
 +
 +      ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI,
                   "wmi peer assoc vdev %d addr %pM (%s)\n",
                   arg->vdev_id, arg->addr,
                   arg->peer_reassoc ? "reassociate" : "new");
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
 +      return skb;
 +}
 +
 +static struct sk_buff *
 +ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar)
 +{
 +      struct sk_buff *skb;
 +
 +      skb = ath10k_wmi_alloc_skb(ar, 0);
 +      if (!skb)
 +              return ERR_PTR(-ENOMEM);
 +
 +      ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev get temperature\n");
 +      return skb;
  }
  
  /* This function assumes the beacon is already DMA mapped */
 -int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_beacon_dma(struct ath10k_vif *arvif)
  {
 +      struct ath10k *ar = arvif->ar;
        struct wmi_bcn_tx_ref_cmd *cmd;
        struct sk_buff *skb;
        struct sk_buff *beacon = arvif->beacon;
 -      struct ath10k *ar = arvif->ar;
        struct ieee80211_hdr *hdr;
 -      int ret;
        u16 fc;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        hdr = (struct ieee80211_hdr *)beacon->data;
        fc = le16_to_cpu(hdr->frame_control);
        if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
                cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
  
 -      ret = ath10k_wmi_cmd_send_nowait(ar, skb,
 -                                       ar->wmi.cmd->pdev_send_bcn_cmdid);
 -
 -      if (ret)
 -              dev_kfree_skb(skb);
 -
 -      return ret;
 +      return skb;
  }
  
 -static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
 -                                        const struct wmi_wmm_params_arg *arg)
 +void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params,
 +                                 const struct wmi_wmm_params_arg *arg)
  {
        params->cwmin  = __cpu_to_le32(arg->cwmin);
        params->cwmax  = __cpu_to_le32(arg->cwmax);
        params->no_ack = __cpu_to_le32(arg->no_ack);
  }
  
 -int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
 -                                 const struct wmi_pdev_set_wmm_params_arg *arg)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar,
 +                             const struct wmi_pdev_set_wmm_params_arg *arg)
  {
        struct wmi_pdev_set_wmm_params *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_pdev_set_wmm_params *)skb->data;
        ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be);
        ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set wmm params\n");
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->pdev_set_wmm_params_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id)
  {
        struct wmi_request_stats_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_request_stats_cmd *)skb->data;
        cmd->stats_id = __cpu_to_le32(stats_id);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_force_fw_hang(struct ath10k *ar,
 -                           enum wmi_force_fw_hang_type type, u32 delay_ms)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_force_fw_hang(struct ath10k *ar,
 +                              enum wmi_force_fw_hang_type type, u32 delay_ms)
  {
        struct wmi_force_fw_hang_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
        cmd->type = __cpu_to_le32(type);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
                   type, delay_ms);
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable)
  {
        struct wmi_dbglog_cfg_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        cmd = (struct wmi_dbglog_cfg_cmd *)skb->data;
  
                   __le32_to_cpu(cmd->module_valid),
                   __le32_to_cpu(cmd->config_enable),
                   __le32_to_cpu(cmd->config_valid));
 -
 -      return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
 +      return skb;
  }
  
 -int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_bitmap)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pktlog_enable(struct ath10k *ar, u32 ev_bitmap)
  {
        struct wmi_pdev_pktlog_enable_cmd *cmd;
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        ev_bitmap &= ATH10K_PKTLOG_ANY;
 -      ath10k_dbg(ar, ATH10K_DBG_WMI,
 -                 "wmi enable pktlog filter:%x\n", ev_bitmap);
  
        cmd = (struct wmi_pdev_pktlog_enable_cmd *)skb->data;
        cmd->ev_bitmap = __cpu_to_le32(ev_bitmap);
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->pdev_pktlog_enable_cmdid);
 +
 +      ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi enable pktlog filter 0x%08x\n",
 +                 ev_bitmap);
 +      return skb;
  }
  
 -int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar)
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pktlog_disable(struct ath10k *ar)
  {
        struct sk_buff *skb;
  
        skb = ath10k_wmi_alloc_skb(ar, 0);
        if (!skb)
 -              return -ENOMEM;
 +              return ERR_PTR(-ENOMEM);
  
        ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi disable pktlog\n");
 +      return skb;
 +}
 +
 +static struct sk_buff *
 +ath10k_wmi_op_gen_pdev_set_quiet_mode(struct ath10k *ar, u32 period,
 +                                    u32 duration, u32 next_offset,
 +                                    u32 enabled)
 +{
 +      struct wmi_pdev_set_quiet_cmd *cmd;
 +      struct sk_buff *skb;
 +
 +      skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
 +      if (!skb)
 +              return ERR_PTR(-ENOMEM);
  
 -      return ath10k_wmi_cmd_send(ar, skb,
 -                                 ar->wmi.cmd->pdev_pktlog_disable_cmdid);
 +      cmd = (struct wmi_pdev_set_quiet_cmd *)skb->data;
 +      cmd->period = __cpu_to_le32(period);
 +      cmd->duration = __cpu_to_le32(duration);
 +      cmd->next_start = __cpu_to_le32(next_offset);
 +      cmd->enabled = __cpu_to_le32(enabled);
 +
 +      ath10k_dbg(ar, ATH10K_DBG_WMI,
 +                 "wmi quiet param: period %u duration %u enabled %d\n",
 +                 period, duration, enabled);
 +      return skb;
  }
  
 +static const struct wmi_ops wmi_ops = {
 +      .rx = ath10k_wmi_op_rx,
 +      .map_svc = wmi_main_svc_map,
 +
 +      .pull_scan = ath10k_wmi_op_pull_scan_ev,
 +      .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
 +      .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
 +      .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
 +      .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
 +      .pull_swba = ath10k_wmi_op_pull_swba_ev,
 +      .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
 +      .pull_svc_rdy = ath10k_wmi_main_op_pull_svc_rdy_ev,
 +      .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
 +      .pull_fw_stats = ath10k_wmi_main_op_pull_fw_stats,
 +
 +      .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
 +      .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
 +      .gen_pdev_set_rd = ath10k_wmi_op_gen_pdev_set_rd,
 +      .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
 +      .gen_init = ath10k_wmi_op_gen_init,
 +      .gen_start_scan = ath10k_wmi_op_gen_start_scan,
 +      .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
 +      .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
 +      .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
 +      .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
 +      .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
 +      .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
 +      .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
 +      .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
 +      .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
 +      .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
 +      .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
 +      .gen_peer_create = ath10k_wmi_op_gen_peer_create,
 +      .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
 +      .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
 +      .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
 +      .gen_peer_assoc = ath10k_wmi_op_gen_peer_assoc,
 +      .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
 +      .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
 +      .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
 +      .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
 +      .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
 +      .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
 +      .gen_request_stats = ath10k_wmi_op_gen_request_stats,
 +      .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
 +      .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
 +      .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
 +      .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
 +      .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
 +      .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
 +      /* .gen_pdev_get_temperature not implemented */
 +};
 +
 +static const struct wmi_ops wmi_10_1_ops = {
 +      .rx = ath10k_wmi_10_1_op_rx,
 +      .map_svc = wmi_10x_svc_map,
 +      .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
 +      .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats,
 +      .gen_init = ath10k_wmi_10_1_op_gen_init,
 +      .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
 +      .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
 +      .gen_peer_assoc = ath10k_wmi_10_1_op_gen_peer_assoc,
 +      /* .gen_pdev_get_temperature not implemented */
 +
 +      /* shared with main branch */
 +      .pull_scan = ath10k_wmi_op_pull_scan_ev,
 +      .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
 +      .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
 +      .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
 +      .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
 +      .pull_swba = ath10k_wmi_op_pull_swba_ev,
 +      .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
 +      .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
 +
 +      .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
 +      .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
 +      .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
 +      .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
 +      .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
 +      .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
 +      .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
 +      .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
 +      .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
 +      .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
 +      .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
 +      .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
 +      .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
 +      .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
 +      .gen_peer_create = ath10k_wmi_op_gen_peer_create,
 +      .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
 +      .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
 +      .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
 +      .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
 +      .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
 +      .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
 +      .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
 +      .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
 +      .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
 +      .gen_request_stats = ath10k_wmi_op_gen_request_stats,
 +      .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
 +      .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
 +      .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
 +      .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
 +      .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
 +      .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
 +};
 +
 +static const struct wmi_ops wmi_10_2_ops = {
 +      .rx = ath10k_wmi_10_2_op_rx,
 +      .gen_init = ath10k_wmi_10_2_op_gen_init,
 +      .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
 +      /* .gen_pdev_get_temperature not implemented */
 +
 +      /* shared with 10.1 */
 +      .map_svc = wmi_10x_svc_map,
 +      .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
 +      .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats,
 +      .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
 +      .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
 +
 +      .pull_scan = ath10k_wmi_op_pull_scan_ev,
 +      .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
 +      .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
 +      .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
 +      .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
 +      .pull_swba = ath10k_wmi_op_pull_swba_ev,
 +      .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
 +      .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
 +
 +      .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
 +      .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
 +      .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
 +      .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
 +      .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
 +      .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
 +      .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
 +      .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
 +      .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
 +      .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
 +      .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
 +      .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
 +      .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
 +      .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
 +      .gen_peer_create = ath10k_wmi_op_gen_peer_create,
 +      .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
 +      .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
 +      .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
 +      .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
 +      .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
 +      .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
 +      .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
 +      .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
 +      .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
 +      .gen_request_stats = ath10k_wmi_op_gen_request_stats,
 +      .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
 +      .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
 +      .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
 +      .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
 +      .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
 +      .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
 +};
 +
 +static const struct wmi_ops wmi_10_2_4_ops = {
 +      .rx = ath10k_wmi_10_2_op_rx,
 +      .gen_init = ath10k_wmi_10_2_op_gen_init,
 +      .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc,
 +      .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature,
 +
 +      /* shared with 10.1 */
 +      .map_svc = wmi_10x_svc_map,
 +      .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev,
 +      .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats,
 +      .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd,
 +      .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan,
 +
 +      .pull_scan = ath10k_wmi_op_pull_scan_ev,
 +      .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev,
 +      .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev,
 +      .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev,
 +      .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev,
 +      .pull_swba = ath10k_wmi_op_pull_swba_ev,
 +      .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev,
 +      .pull_rdy = ath10k_wmi_op_pull_rdy_ev,
 +
 +      .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
 +      .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume,
 +      .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param,
 +      .gen_stop_scan = ath10k_wmi_op_gen_stop_scan,
 +      .gen_vdev_create = ath10k_wmi_op_gen_vdev_create,
 +      .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete,
 +      .gen_vdev_start = ath10k_wmi_op_gen_vdev_start,
 +      .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop,
 +      .gen_vdev_up = ath10k_wmi_op_gen_vdev_up,
 +      .gen_vdev_down = ath10k_wmi_op_gen_vdev_down,
 +      .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param,
 +      .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key,
 +      .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf,
 +      .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable,
 +      .gen_peer_create = ath10k_wmi_op_gen_peer_create,
 +      .gen_peer_delete = ath10k_wmi_op_gen_peer_delete,
 +      .gen_peer_flush = ath10k_wmi_op_gen_peer_flush,
 +      .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param,
 +      .gen_set_psmode = ath10k_wmi_op_gen_set_psmode,
 +      .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps,
 +      .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps,
 +      .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list,
 +      .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma,
 +      .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm,
 +      .gen_request_stats = ath10k_wmi_op_gen_request_stats,
 +      .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang,
 +      .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx,
 +      .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg,
 +      .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable,
 +      .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable,
 +      .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode,
 +};
 +
  int ath10k_wmi_attach(struct ath10k *ar)
  {
 -      if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
 -              if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
 -                      ar->wmi.cmd = &wmi_10_2_cmd_map;
 -              else
 -                      ar->wmi.cmd = &wmi_10x_cmd_map;
 -
 +      switch (ar->wmi.op_version) {
 +      case ATH10K_FW_WMI_OP_VERSION_10_2_4:
 +              ar->wmi.cmd = &wmi_10_2_4_cmd_map;
 +              ar->wmi.ops = &wmi_10_2_4_ops;
 +              ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map;
 +              ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map;
 +              break;
 +      case ATH10K_FW_WMI_OP_VERSION_10_2:
 +              ar->wmi.cmd = &wmi_10_2_cmd_map;
 +              ar->wmi.ops = &wmi_10_2_ops;
                ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
                ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
 -      } else {
 +              break;
 +      case ATH10K_FW_WMI_OP_VERSION_10_1:
 +              ar->wmi.cmd = &wmi_10x_cmd_map;
 +              ar->wmi.ops = &wmi_10_1_ops;
 +              ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
 +              ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
 +              break;
 +      case ATH10K_FW_WMI_OP_VERSION_MAIN:
                ar->wmi.cmd = &wmi_cmd_map;
 +              ar->wmi.ops = &wmi_ops;
                ar->wmi.vdev_param = &wmi_vdev_param_map;
                ar->wmi.pdev_param = &wmi_pdev_param_map;
 +              break;
 +      case ATH10K_FW_WMI_OP_VERSION_TLV:
 +              ath10k_wmi_tlv_attach(ar);
 +              break;
 +      case ATH10K_FW_WMI_OP_VERSION_UNSET:
 +      case ATH10K_FW_WMI_OP_VERSION_MAX:
 +              ath10k_err(ar, "unsupported WMI op version: %d\n",
 +                         ar->wmi.op_version);
 +              return -EINVAL;
        }
  
        init_completion(&ar->wmi.service_ready);
@@@ -1097,65 -1097,24 +1097,65 @@@ void ath_update_max_aggr_framelen(struc
  }
  
  static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
 -                             u8 rateidx)
 +                             u8 rateidx, bool is_40, bool is_cck)
  {
        u8 max_power;
 +      struct sk_buff *skb;
 +      struct ath_frame_info *fi;
 +      struct ieee80211_tx_info *info;
        struct ath_hw *ah = sc->sc_ah;
  
 -      if (sc->tx99_state)
 +      if (sc->tx99_state || !ah->tpc_enabled)
                return MAX_RATE_POWER;
  
 +      skb = bf->bf_mpdu;
 +      fi = get_frame_info(skb);
 +      info = IEEE80211_SKB_CB(skb);
 +
        if (!AR_SREV_9300_20_OR_LATER(ah)) {
 -              /* ar9002 is not sipported for the moment */
 -              return MAX_RATE_POWER;
 -      }
 +              int txpower = fi->tx_power;
  
 -      if (!bf->bf_state.bfs_paprd) {
 -              struct sk_buff *skb = bf->bf_mpdu;
 -              struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 -              struct ath_frame_info *fi = get_frame_info(skb);
 +              if (is_40) {
 +                      u8 power_ht40delta;
 +                      struct ar5416_eeprom_def *eep = &ah->eeprom.def;
  
 +                      if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_2) {
 +                              bool is_2ghz;
 +                              struct modal_eep_header *pmodal;
 +
 +                              is_2ghz = info->band == IEEE80211_BAND_2GHZ;
 +                              pmodal = &eep->modalHeader[is_2ghz];
 +                              power_ht40delta = pmodal->ht40PowerIncForPdadc;
 +                      } else {
 +                              power_ht40delta = 2;
 +                      }
 +                      txpower += power_ht40delta;
 +              }
 +
 +              if (AR_SREV_9287(ah) || AR_SREV_9285(ah) ||
 +                  AR_SREV_9271(ah)) {
 +                      txpower -= 2 * AR9287_PWR_TABLE_OFFSET_DB;
 +              } else if (AR_SREV_9280_20_OR_LATER(ah)) {
 +                      s8 power_offset;
 +
 +                      power_offset = ah->eep_ops->get_eeprom(ah,
 +                                                      EEP_PWR_TABLE_OFFSET);
 +                      txpower -= 2 * power_offset;
 +              }
 +
 +              if (OLC_FOR_AR9280_20_LATER && is_cck)
 +                      txpower -= 2;
 +
 +              txpower = max(txpower, 0);
 +              max_power = min_t(u8, ah->tx_power[rateidx], txpower);
 +
 +              /* XXX: clamp minimum TX power at 1 for AR9160 since if
 +               * max_power is set to 0, frames are transmitted at max
 +               * TX power
 +               */
 +              if (!max_power && !AR_SREV_9280_20_OR_LATER(ah))
 +                      max_power = 1;
 +      } else if (!bf->bf_state.bfs_paprd) {
                if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
                        max_power = min(ah->tx_power_stbc[rateidx],
                                        fi->tx_power);
@@@ -1193,7 -1152,7 +1193,7 @@@ static void ath_buf_set_rate(struct ath
        info->rtscts_rate = fi->rtscts_rate;
  
        for (i = 0; i < ARRAY_SIZE(bf->rates); i++) {
 -              bool is_40, is_sgi, is_sp;
 +              bool is_40, is_sgi, is_sp, is_cck;
                int phy;
  
                if (!rates[i].count || (rates[i].idx < 0))
                        if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
                                info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
  
 -                      info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
 +                      info->txpower[i] = ath_get_rate_txpower(sc, bf, rix,
 +                                                              is_40, false);
                        continue;
                }
  
                info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
                        phy, rate->bitrate * 100, len, rix, is_sp);
  
 -              info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
 +              is_cck = IS_CCK_RATE(info->rates[i].Rate);
 +              info->txpower[i] = ath_get_rate_txpower(sc, bf, rix, false,
 +                                                      is_cck);
        }
  
        /* For AR5416 - RTS cannot be followed by a frame larger than 8K */
@@@ -2303,7 -2259,7 +2303,7 @@@ int ath_tx_start(struct ieee80211_hw *h
        struct ath_txq *txq = txctl->txq;
        struct ath_atx_tid *tid = NULL;
        struct ath_buf *bf;
-       bool queue, skip_uapsd = false;
+       bool queue, skip_uapsd = false, ps_resp;
        int q, ret;
  
        if (vif)
        if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
                txctl->force_channel = true;
  
+       ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE);
        ret = ath_tx_prepare(hw, skb, txctl);
        if (ret)
            return ret;
        if (txctl->an && queue)
                tid = ath_get_skb_tid(sc, txctl->an, skb);
  
-       if (!skip_uapsd && (info->flags & IEEE80211_TX_CTL_PS_RESPONSE)) {
+       if (!skip_uapsd && ps_resp) {
                ath_txq_unlock(sc, txq);
                txq = sc->tx.uapsdq;
                ath_txq_lock(sc, txq);
@@@ -142,14 -142,14 +142,14 @@@ int wil_cid_fill_sinfo(struct wil6210_p
  
        sinfo->generation = wil->sinfo_gen;
  
-       sinfo->filled = STATION_INFO_RX_BYTES |
-                       STATION_INFO_TX_BYTES |
-                       STATION_INFO_RX_PACKETS |
-                       STATION_INFO_TX_PACKETS |
-                       STATION_INFO_RX_BITRATE |
-                       STATION_INFO_TX_BITRATE |
-                       STATION_INFO_RX_DROP_MISC |
-                       STATION_INFO_TX_FAILED;
+       sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) |
+                       BIT(NL80211_STA_INFO_TX_BYTES) |
+                       BIT(NL80211_STA_INFO_RX_PACKETS) |
+                       BIT(NL80211_STA_INFO_TX_PACKETS) |
+                       BIT(NL80211_STA_INFO_RX_BITRATE) |
+                       BIT(NL80211_STA_INFO_TX_BITRATE) |
+                       BIT(NL80211_STA_INFO_RX_DROP_MISC) |
+                       BIT(NL80211_STA_INFO_TX_FAILED);
  
        sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
        sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
        sinfo->tx_packets = stats->tx_packets;
        sinfo->tx_failed = stats->tx_errors;
  
 -      if (test_bit(wil_status_fwconnected, &wil->status)) {
 +      if (test_bit(wil_status_fwconnected, wil->status)) {
-               sinfo->filled |= STATION_INFO_SIGNAL;
+               sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
                sinfo->signal = reply.evt.sqi;
        }
  
@@@ -282,7 -282,7 +282,7 @@@ static int wil_cfg80211_scan(struct wip
        }
  
        /* FW don't support scan after connection attempt */
 -      if (test_bit(wil_status_dontscan, &wil->status)) {
 +      if (test_bit(wil_status_dontscan, wil->status)) {
                wil_err(wil, "Can't scan now\n");
                return -EBUSY;
        }
@@@ -362,8 -362,8 +362,8 @@@ static int wil_cfg80211_connect(struct 
        int ch;
        int rc = 0;
  
 -      if (test_bit(wil_status_fwconnecting, &wil->status) ||
 -          test_bit(wil_status_fwconnected, &wil->status))
 +      if (test_bit(wil_status_fwconnecting, wil->status) ||
 +          test_bit(wil_status_fwconnected, wil->status))
                return -EALREADY;
  
        wil_print_connect_params(wil, sme);
        memcpy(conn.bssid, bss->bssid, ETH_ALEN);
        memcpy(conn.dst_mac, bss->bssid, ETH_ALEN);
  
 -      set_bit(wil_status_fwconnecting, &wil->status);
 +      set_bit(wil_status_fwconnecting, wil->status);
  
        rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
        if (rc == 0) {
                mod_timer(&wil->connect_timer,
                          jiffies + msecs_to_jiffies(2000));
        } else {
 -              clear_bit(wil_status_fwconnecting, &wil->status);
 +              clear_bit(wil_status_fwconnecting, wil->status);
        }
  
   out:
  #include "wmi.h"
  #include "trace.h"
  
 -static uint max_assoc_sta = 1;
 +static uint max_assoc_sta = WIL6210_MAX_CID;
  module_param(max_assoc_sta, uint, S_IRUGO | S_IWUSR);
  MODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP");
  
 +int agg_wsize; /* = 0; */
 +module_param(agg_wsize, int, S_IRUGO | S_IWUSR);
 +MODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;"
 +               " 0 - use default; < 0 - don't auto-establish");
 +
  /**
   * WMI event receiving - theory of operations
   *
@@@ -202,7 -197,7 +202,7 @@@ static int __wmi_send(struct wil6210_pr
  
        might_sleep();
  
 -      if (!test_bit(wil_status_fwready, &wil->status)) {
 +      if (!test_bit(wil_status_fwready, wil->status)) {
                wil_err(wil, "WMI: cannot send command while FW not ready\n");
                return -EAGAIN;
        }
@@@ -305,7 -300,7 +305,7 @@@ static void wmi_evt_fw_ready(struct wil
        wil_dbg_wmi(wil, "WMI: got FW ready event\n");
  
        wil_set_recovery_state(wil, fw_recovery_idle);
 -      set_bit(wil_status_fwready, &wil->status);
 +      set_bit(wil_status_fwready, wil->status);
        /* let the reset sequence continue */
        complete(&wil->wmi_ready);
  }
@@@ -443,7 -438,7 +443,7 @@@ static void wmi_evt_connect(struct wil6
  
        if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
            (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
 -              if (!test_bit(wil_status_fwconnecting, &wil->status)) {
 +              if (!test_bit(wil_status_fwconnecting, wil->status)) {
                        wil_err(wil, "Not in connecting state\n");
                        return;
                }
                if (assoc_req_ie) {
                        sinfo.assoc_req_ies = assoc_req_ie;
                        sinfo.assoc_req_ies_len = assoc_req_ielen;
-                       sinfo.filled |= STATION_INFO_ASSOC_REQ_IES;
                }
  
                cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
        }
 -      clear_bit(wil_status_fwconnecting, &wil->status);
 -      set_bit(wil_status_fwconnected, &wil->status);
 +      clear_bit(wil_status_fwconnecting, wil->status);
 +      set_bit(wil_status_fwconnected, wil->status);
  
        /* FIXME FW can transmit only ucast frames to peer */
        /* FIXME real ring_id instead of hard coded 0 */
        wil->sta[evt->cid].status = wil_sta_conn_pending;
  
        wil->pending_connect_cid = evt->cid;
 -      queue_work(wil->wmi_wq_conn, &wil->connect_worker);
 +      queue_work(wil->wq_service, &wil->connect_worker);
  }
  
  static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
@@@ -549,22 -543,6 +548,22 @@@ static void wmi_evt_eapol_rx(struct wil
        }
  }
  
 +static void wil_addba_tx_cid(struct wil6210_priv *wil, u8 cid, u16 wsize)
 +{
 +      struct vring_tx_data *t;
 +      int i;
 +
 +      for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
 +              if (cid != wil->vring2cid_tid[i][0])
 +                      continue;
 +              t = &wil->vring_tx_data[i];
 +              if (!t->enabled)
 +                      continue;
 +
 +              wil_addba_tx_request(wil, i, wsize);
 +      }
 +}
 +
  static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len)
  {
        struct net_device *ndev = wil_to_ndev(wil);
        }
  
        wil->sta[cid].data_port_open = true;
 +      if (agg_wsize >= 0)
 +              wil_addba_tx_cid(wil, cid, agg_wsize);
        netif_carrier_on(ndev);
  }
  
@@@ -606,89 -582,55 +605,89 @@@ static void wmi_evt_ba_status(struct wi
                              int len)
  {
        struct wmi_vring_ba_status_event *evt = d;
 -      struct wil_sta_info *sta;
 -      uint i, cid;
 +      struct vring_tx_data *txdata;
  
 -      /* TODO: use Rx BA status, not Tx one */
 -
 -      wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n",
 +      wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d AMSDU%s\n",
                    evt->ringid,
                    evt->status == WMI_BA_AGREED ? "OK" : "N/A",
 -                  evt->agg_wsize, __le16_to_cpu(evt->ba_timeout));
 +                  evt->agg_wsize, __le16_to_cpu(evt->ba_timeout),
 +                  evt->amsdu ? "+" : "-");
  
        if (evt->ringid >= WIL6210_MAX_TX_RINGS) {
                wil_err(wil, "invalid ring id %d\n", evt->ringid);
                return;
        }
  
 -      mutex_lock(&wil->mutex);
 -
 -      cid = wil->vring2cid_tid[evt->ringid][0];
 -      if (cid >= WIL6210_MAX_CID) {
 -              wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid);
 -              goto out;
 +      if (evt->status != WMI_BA_AGREED) {
 +              evt->ba_timeout = 0;
 +              evt->agg_wsize = 0;
 +              evt->amsdu = 0;
        }
  
 -      sta = &wil->sta[cid];
 -      if (sta->status == wil_sta_unused) {
 -              wil_err(wil, "CID %d unused\n", cid);
 -              goto out;
 -      }
 +      txdata = &wil->vring_tx_data[evt->ringid];
  
 -      wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr);
 -      for (i = 0; i < WIL_STA_TID_NUM; i++) {
 -              struct wil_tid_ampdu_rx *r;
 -              unsigned long flags;
 +      txdata->agg_timeout = le16_to_cpu(evt->ba_timeout);
 +      txdata->agg_wsize = evt->agg_wsize;
 +      txdata->agg_amsdu = evt->amsdu;
 +      txdata->addba_in_progress = false;
 +}
  
 -              spin_lock_irqsave(&sta->tid_rx_lock, flags);
 +static void wmi_evt_addba_rx_req(struct wil6210_priv *wil, int id, void *d,
 +                               int len)
 +{
 +      struct wmi_rcp_addba_req_event *evt = d;
  
 -              r = sta->tid_rx[i];
 -              sta->tid_rx[i] = NULL;
 -              wil_tid_ampdu_rx_free(wil, r);
 +      wil_addba_rx_request(wil, evt->cidxtid, evt->dialog_token,
 +                           evt->ba_param_set, evt->ba_timeout,
 +                           evt->ba_seq_ctrl);
 +}
  
 -              spin_unlock_irqrestore(&sta->tid_rx_lock, flags);
 +static void wmi_evt_delba(struct wil6210_priv *wil, int id, void *d, int len)
 +__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
 +{
 +      struct wmi_delba_event *evt = d;
 +      u8 cid, tid;
 +      u16 reason = __le16_to_cpu(evt->reason);
 +      struct wil_sta_info *sta;
 +      struct wil_tid_ampdu_rx *r;
  
 -              if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize)
 -                      sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil,
 -                                              evt->agg_wsize, 0);
 +      might_sleep();
 +      parse_cidxtid(evt->cidxtid, &cid, &tid);
 +      wil_dbg_wmi(wil, "DELBA CID %d TID %d from %s reason %d\n",
 +                  cid, tid,
 +                  evt->from_initiator ? "originator" : "recipient",
 +                  reason);
 +      if (!evt->from_initiator) {
 +              int i;
 +              /* find Tx vring it belongs to */
 +              for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
 +                      if ((wil->vring2cid_tid[i][0] == cid) &&
 +                          (wil->vring2cid_tid[i][1] == tid)) {
 +                              struct vring_tx_data *txdata =
 +                                      &wil->vring_tx_data[i];
 +
 +                              wil_dbg_wmi(wil, "DELBA Tx vring %d\n", i);
 +                              txdata->agg_timeout = 0;
 +                              txdata->agg_wsize = 0;
 +                              txdata->addba_in_progress = false;
 +
 +                              break; /* max. 1 matching ring */
 +                      }
 +              }
 +              if (i >= ARRAY_SIZE(wil->vring2cid_tid))
 +                      wil_err(wil, "DELBA: unable to find Tx vring\n");
 +              return;
        }
  
 -out:
 -      mutex_unlock(&wil->mutex);
 +      sta = &wil->sta[cid];
 +
 +      spin_lock_bh(&sta->tid_rx_lock);
 +
 +      r = sta->tid_rx[tid];
 +      sta->tid_rx[tid] = NULL;
 +      wil_tid_ampdu_rx_free(wil, r);
 +
 +      spin_unlock_bh(&sta->tid_rx_lock);
  }
  
  static const struct {
        {WMI_DATA_PORT_OPEN_EVENTID,    wmi_evt_linkup},
        {WMI_WBE_LINKDOWN_EVENTID,      wmi_evt_linkdown},
        {WMI_BA_STATUS_EVENTID,         wmi_evt_ba_status},
 +      {WMI_RCP_ADDBA_REQ_EVENTID,     wmi_evt_addba_rx_req},
 +      {WMI_DELBA_EVENTID,             wmi_evt_delba},
  };
  
  /*
@@@ -727,7 -667,7 +726,7 @@@ void wmi_recv_cmd(struct wil6210_priv *
        ulong flags;
        unsigned n;
  
 -      if (!test_bit(wil_status_reset_done, &wil->status)) {
 +      if (!test_bit(wil_status_reset_done, wil->status)) {
                wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
                return;
        }
@@@ -1084,14 -1024,13 +1083,14 @@@ int wmi_rx_chain_add(struct wil6210_pri
        struct wmi_cfg_rx_chain_cmd cmd = {
                .action = WMI_RX_CHAIN_ADD,
                .rx_sw_ring = {
 -                      .max_mpdu_size = cpu_to_le16(mtu_max + ETH_HLEN),
 +                      .max_mpdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)),
                        .ring_mem_base = cpu_to_le64(vring->pa),
                        .ring_size = cpu_to_le16(vring->size),
                },
                .mid = 0, /* TODO - what is it? */
                .decap_trans_type = WMI_DECAP_TYPE_802_3,
                .reorder_type = WMI_RX_SW_REORDER,
 +              .host_thrsh = cpu_to_le16(rx_ring_overflow_thrsh),
        };
        struct {
                struct wil6210_mbox_hdr_wmi wmi;
@@@ -1171,87 -1110,6 +1170,87 @@@ int wmi_disconnect_sta(struct wil6210_p
        return wmi_send(wil, WMI_DISCONNECT_STA_CMDID, &cmd, sizeof(cmd));
  }
  
 +int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout)
 +{
 +      struct wmi_vring_ba_en_cmd cmd = {
 +              .ringid = ringid,
 +              .agg_max_wsize = size,
 +              .ba_timeout = cpu_to_le16(timeout),
 +              .amsdu = 0,
 +      };
 +
 +      wil_dbg_wmi(wil, "%s(ring %d size %d timeout %d)\n", __func__,
 +                  ringid, size, timeout);
 +
 +      return wmi_send(wil, WMI_VRING_BA_EN_CMDID, &cmd, sizeof(cmd));
 +}
 +
 +int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason)
 +{
 +      struct wmi_vring_ba_dis_cmd cmd = {
 +              .ringid = ringid,
 +              .reason = cpu_to_le16(reason),
 +      };
 +
 +      wil_dbg_wmi(wil, "%s(ring %d reason %d)\n", __func__,
 +                  ringid, reason);
 +
 +      return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, &cmd, sizeof(cmd));
 +}
 +
 +int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason)
 +{
 +      struct wmi_rcp_delba_cmd cmd = {
 +              .cidxtid = cidxtid,
 +              .reason = cpu_to_le16(reason),
 +      };
 +
 +      wil_dbg_wmi(wil, "%s(CID %d TID %d reason %d)\n", __func__,
 +                  cidxtid & 0xf, (cidxtid >> 4) & 0xf, reason);
 +
 +      return wmi_send(wil, WMI_RCP_DELBA_CMDID, &cmd, sizeof(cmd));
 +}
 +
 +int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
 +                    u16 status, bool amsdu, u16 agg_wsize, u16 timeout)
 +{
 +      int rc;
 +      struct wmi_rcp_addba_resp_cmd cmd = {
 +              .cidxtid = mk_cidxtid(cid, tid),
 +              .dialog_token = token,
 +              .status_code = cpu_to_le16(status),
 +              /* bit 0: A-MSDU supported
 +               * bit 1: policy (should be 0 for us)
 +               * bits 2..5: TID
 +               * bits 6..15: buffer size
 +               */
 +              .ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) |
 +                                          (agg_wsize << 6)),
 +              .ba_timeout = cpu_to_le16(timeout),
 +      };
 +      struct {
 +              struct wil6210_mbox_hdr_wmi wmi;
 +              struct wmi_rcp_addba_resp_sent_event evt;
 +      } __packed reply;
 +
 +      wil_dbg_wmi(wil,
 +                  "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s\n",
 +                  cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-");
 +
 +      rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, &cmd, sizeof(cmd),
 +                    WMI_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), 100);
 +      if (rc)
 +              return rc;
 +
 +      if (reply.evt.status) {
 +              wil_err(wil, "ADDBA response failed with status %d\n",
 +                      le16_to_cpu(reply.evt.status));
 +              rc = -EINVAL;
 +      }
 +
 +      return rc;
 +}
 +
  void wmi_event_flush(struct wil6210_priv *wil)
  {
        struct pending_wmi_event *evt, *t;
@@@ -38,7 -38,6 +38,7 @@@
  #include "proto.h"
  #include "vendor.h"
  #include "bus.h"
 +#include "common.h"
  
  #define BRCMF_SCAN_IE_LEN_MAX         2048
  #define BRCMF_PNO_VERSION             2
@@@ -453,16 -452,16 +453,16 @@@ static void convert_key_from_CPU(struc
  }
  
  static int
 -send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
 +send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
  {
        int err;
        struct brcmf_wsec_key_le key_le;
  
        convert_key_from_CPU(key, &key_le);
  
 -      brcmf_netdev_wait_pend8021x(ndev);
 +      brcmf_netdev_wait_pend8021x(ifp);
  
 -      err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
 +      err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
                                        sizeof(key_le));
  
        if (err)
@@@ -1671,7 -1670,7 +1671,7 @@@ brcmf_set_sharedkey(struct net_device *
        brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
                  key.len, key.index, key.algo);
        brcmf_dbg(CONN, "key \"%s\"\n", key.data);
 -      err = send_key_to_dongle(ndev, &key);
 +      err = send_key_to_dongle(netdev_priv(ndev), &key);
        if (err)
                return err;
  
@@@ -2053,7 -2052,7 +2053,7 @@@ brcmf_add_keyext(struct wiphy *wiphy, s
        /* check for key index change */
        if (key.len == 0) {
                /* key delete */
 -              err = send_key_to_dongle(ndev, &key);
 +              err = send_key_to_dongle(ifp, &key);
                if (err)
                        brcmf_err("key delete error (%d)\n", err);
        } else {
                        brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
                        return -EINVAL;
                }
 -              err = send_key_to_dongle(ndev, &key);
 +              err = send_key_to_dongle(ifp, &key);
                if (err)
                        brcmf_err("wsec_key error (%d)\n", err);
        }
@@@ -2122,7 -2121,7 +2122,7 @@@ brcmf_cfg80211_add_key(struct wiphy *wi
                    struct key_params *params)
  {
        struct brcmf_if *ifp = netdev_priv(ndev);
 -      struct brcmf_wsec_key key;
 +      struct brcmf_wsec_key *key;
        s32 val;
        s32 wsec;
        s32 err = 0;
        if (!check_vif_up(ifp->vif))
                return -EIO;
  
 +      if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
 +              /* we ignore this key index in this case */
 +              brcmf_err("invalid key index (%d)\n", key_idx);
 +              return -EINVAL;
 +      }
 +
        if (mac_addr &&
                (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
                (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
                brcmf_dbg(TRACE, "Exit");
                return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
        }
 -      memset(&key, 0, sizeof(key));
  
 -      key.len = (u32) params->key_len;
 -      key.index = (u32) key_idx;
 +      key = &ifp->vif->profile.key[key_idx];
 +      memset(key, 0, sizeof(*key));
  
 -      if (key.len > sizeof(key.data)) {
 -              brcmf_err("Too long key length (%u)\n", key.len);
 +      if (params->key_len > sizeof(key->data)) {
 +              brcmf_err("Too long key length (%u)\n", params->key_len);
                err = -EINVAL;
                goto done;
        }
 -      memcpy(key.data, params->key, key.len);
 +      key->len = params->key_len;
 +      key->index = key_idx;
  
 -      key.flags = BRCMF_PRIMARY_KEY;
 +      memcpy(key->data, params->key, key->len);
 +
 +      key->flags = BRCMF_PRIMARY_KEY;
        switch (params->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
 -              key.algo = CRYPTO_ALGO_WEP1;
 +              key->algo = CRYPTO_ALGO_WEP1;
                val = WEP_ENABLED;
                brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
                break;
        case WLAN_CIPHER_SUITE_WEP104:
 -              key.algo = CRYPTO_ALGO_WEP128;
 +              key->algo = CRYPTO_ALGO_WEP128;
                val = WEP_ENABLED;
                brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
                break;
        case WLAN_CIPHER_SUITE_TKIP:
                if (!brcmf_is_apmode(ifp->vif)) {
                        brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
 -                      memcpy(keybuf, &key.data[24], sizeof(keybuf));
 -                      memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
 -                      memcpy(&key.data[16], keybuf, sizeof(keybuf));
 +                      memcpy(keybuf, &key->data[24], sizeof(keybuf));
 +                      memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
 +                      memcpy(&key->data[16], keybuf, sizeof(keybuf));
                }
 -              key.algo = CRYPTO_ALGO_TKIP;
 +              key->algo = CRYPTO_ALGO_TKIP;
                val = TKIP_ENABLED;
                brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
 -              key.algo = CRYPTO_ALGO_AES_CCM;
 +              key->algo = CRYPTO_ALGO_AES_CCM;
                val = AES_ENABLED;
                brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
                break;
        case WLAN_CIPHER_SUITE_CCMP:
 -              key.algo = CRYPTO_ALGO_AES_CCM;
 +              key->algo = CRYPTO_ALGO_AES_CCM;
                val = AES_ENABLED;
                brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
                break;
                goto done;
        }
  
 -      err = send_key_to_dongle(ndev, &key);
 +      err = send_key_to_dongle(ifp, key);
        if (err)
                goto done;
  
@@@ -2231,7 -2222,7 +2231,7 @@@ brcmf_cfg80211_del_key(struct wiphy *wi
        if (!check_vif_up(ifp->vif))
                return -EIO;
  
 -      if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
 +      if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
                /* we ignore this key index in this case */
                brcmf_err("invalid key index (%d)\n", key_idx);
                return -EINVAL;
        brcmf_dbg(CONN, "key index (%d)\n", key_idx);
  
        /* Set the new key/index */
 -      err = send_key_to_dongle(ndev, &key);
 +      err = send_key_to_dongle(ifp, &key);
  
        brcmf_dbg(TRACE, "Exit\n");
        return err;
@@@ -2314,39 -2305,6 +2314,39 @@@ brcmf_cfg80211_config_default_mgmt_key(
        return -EOPNOTSUPP;
  }
  
 +static void
 +brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
 +{
 +      s32 err;
 +      u8 key_idx;
 +      struct brcmf_wsec_key *key;
 +      s32 wsec;
 +
 +      for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
 +              key = &ifp->vif->profile.key[key_idx];
 +              if ((key->algo == CRYPTO_ALGO_WEP1) ||
 +                  (key->algo == CRYPTO_ALGO_WEP128))
 +                      break;
 +      }
 +      if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
 +              return;
 +
 +      err = send_key_to_dongle(ifp, key);
 +      if (err) {
 +              brcmf_err("Setting WEP key failed (%d)\n", err);
 +              return;
 +      }
 +      err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
 +      if (err) {
 +              brcmf_err("get wsec error (%d)\n", err);
 +              return;
 +      }
 +      wsec |= WEP_ENABLED;
 +      err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
 +      if (err)
 +              brcmf_err("set wsec error (%d)\n", err);
 +}
 +
  static s32
  brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
                           const u8 *mac, struct station_info *sinfo)
                        brcmf_err("GET STA INFO failed, %d\n", err);
                        goto done;
                }
-               sinfo->filled = STATION_INFO_INACTIVE_TIME;
+               sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
                sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
                if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
-                       sinfo->filled |= STATION_INFO_CONNECTED_TIME;
+                       sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
                        sinfo->connected_time = le32_to_cpu(sta_info_le.in);
                }
                brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
                        brcmf_err("Could not get rate (%d)\n", err);
                        goto done;
                } else {
-                       sinfo->filled |= STATION_INFO_TX_BITRATE;
+                       sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
                        sinfo->txrate.legacy = rate * 5;
                        brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
                }
                                goto done;
                        } else {
                                rssi = le32_to_cpu(scb_val.val);
-                               sinfo->filled |= STATION_INFO_SIGNAL;
+                               sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
                                sinfo->signal = rssi;
                                brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
                        }
                                brcmf_dbg(CONN, "DTIM peroid %d\n",
                                          dtim_period);
                        }
-                       sinfo->filled |= STATION_INFO_BSS_PARAM;
+                       sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
                }
        } else
                err = -EPERM;
@@@ -3966,7 -3924,6 +3966,7 @@@ brcmf_cfg80211_start_ap(struct wiphy *w
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct brcmf_if *ifp = netdev_priv(ndev);
        const struct brcmf_tlv *ssid_ie;
 +      const struct brcmf_tlv *country_ie;
        struct brcmf_ssid_le ssid_le;
        s32 err = -EPERM;
        const struct brcmf_tlv *rsn_ie;
        struct brcmf_fil_bss_enable_le bss_enable;
        u16 chanspec;
        bool mbss;
 +      int is_11d;
  
        brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
                  settings->chandef.chan->hw_value,
        brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
                  settings->ssid, settings->ssid_len, settings->auth_type,
                  settings->inactivity_timeout);
 -
        dev_role = ifp->vif->wdev.iftype;
        mbss = ifp->vif->mbss;
  
 +      /* store current 11d setting */
 +      brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
 +      country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
 +                                    settings->beacon.tail_len,
 +                                    WLAN_EID_COUNTRY);
 +      is_11d = country_ie ? 1 : 0;
 +
        memset(&ssid_le, 0, sizeof(ssid_le));
        if (settings->ssid == NULL || settings->ssid_len == 0) {
                ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
                        goto exit;
                }
  
 +              if (is_11d != ifp->vif->is_11d) {
 +                      err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
 +                                                  is_11d);
 +                      if (err < 0) {
 +                              brcmf_err("Regulatory Set Error, %d\n", err);
 +                              goto exit;
 +                      }
 +              }
                if (settings->beacon_interval) {
                        err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
                                                    settings->beacon_interval);
                        brcmf_err("SET INFRA error %d\n", err);
                        goto exit;
                }
 +      } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
 +              /* Multiple-BSS should use same 11d configuration */
 +              err = -EINVAL;
 +              goto exit;
        }
        if (dev_role == NL80211_IFTYPE_AP) {
                if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
                        brcmf_err("BRCMF_C_UP error (%d)\n", err);
                        goto exit;
                }
 +              /* On DOWN the firmware removes the WEP keys, reconfigure
 +               * them if they were set.
 +               */
 +              brcmf_cfg80211_reconfigure_wep(ifp);
  
                memset(&join_params, 0, sizeof(join_params));
                /* join parameters starts with ssid */
@@@ -4199,11 -4133,6 +4199,11 @@@ static int brcmf_cfg80211_stop_ap(struc
                        brcmf_err("setting INFRA mode failed %d\n", err);
                if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
                        brcmf_fil_iovar_int_set(ifp, "mbss", 0);
 +              err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
 +                                          ifp->vif->is_11d);
 +              if (err < 0)
 +                      brcmf_err("restoring REGULATORY setting failed %d\n",
 +                                err);
                /* Bring device back up so it can be used again */
                err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
                if (err < 0)
@@@ -4268,34 -4197,6 +4268,34 @@@ brcmf_cfg80211_del_station(struct wiph
        return err;
  }
  
 +static int
 +brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
 +                            const u8 *mac, struct station_parameters *params)
 +{
 +      struct brcmf_if *ifp = netdev_priv(ndev);
 +      s32 err;
 +
 +      brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
 +                params->sta_flags_mask, params->sta_flags_set);
 +
 +      /* Ignore all 00 MAC */
 +      if (is_zero_ether_addr(mac))
 +              return 0;
 +
 +      if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
 +              return 0;
 +
 +      if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
 +                                           (void *)mac, ETH_ALEN);
 +      else
 +              err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
 +                                           (void *)mac, ETH_ALEN);
 +      if (err < 0)
 +              brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
 +
 +      return err;
 +}
  
  static void
  brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
@@@ -4570,7 -4471,6 +4570,7 @@@ static struct cfg80211_ops wl_cfg80211_
        .stop_ap = brcmf_cfg80211_stop_ap,
        .change_beacon = brcmf_cfg80211_change_beacon,
        .del_station = brcmf_cfg80211_del_station,
 +      .change_station = brcmf_cfg80211_change_station,
        .sched_scan_start = brcmf_cfg80211_sched_scan_start,
        .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
        .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
@@@ -4878,7 -4778,6 +4878,6 @@@ brcmf_notify_connect_status_ap(struct b
        if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
            (reason == BRCMF_E_STATUS_SUCCESS)) {
                memset(&sinfo, 0, sizeof(sinfo));
-               sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
                if (!data) {
                        brcmf_err("No IEs present in ASSOC/REASSOC_IND");
                        return -EINVAL;
@@@ -5976,29 -5875,6 +5975,29 @@@ int brcmf_cfg80211_wait_vif_event_timeo
                                  vif_event_equals(event, action), timeout);
  }
  
 +static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
 +                                      struct regulatory_request *req)
 +{
 +      struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
 +      struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
 +      struct brcmf_fil_country_le ccreq;
 +      int i;
 +
 +      brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
 +                req->alpha2[0], req->alpha2[1]);
 +
 +      /* ignore non-ISO3166 country codes */
 +      for (i = 0; i < sizeof(req->alpha2); i++)
 +              if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
 +                      brcmf_err("not a ISO3166 code\n");
 +                      return;
 +              }
 +      memset(&ccreq, 0, sizeof(ccreq));
 +      ccreq.rev = cpu_to_le32(-1);
 +      memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
 +      brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
 +}
 +
  static void brcmf_free_wiphy(struct wiphy *wiphy)
  {
        kfree(wiphy->iface_combinations);
@@@ -6075,7 -5951,6 +6074,7 @@@ struct brcmf_cfg80211_info *brcmf_cfg80
                goto priv_out;
  
        brcmf_dbg(INFO, "Registering custom regulatory\n");
 +      wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
        wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
        wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
  
@@@ -282,7 -282,6 +282,6 @@@ static struct ieee80211_hw *cw1200_init
                    IEEE80211_HW_SUPPORTS_PS |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
                    IEEE80211_HW_REPORTS_TX_ACK_STATUS |
-                   IEEE80211_HW_SUPPORTS_UAPSD |
                    IEEE80211_HW_CONNECTION_MONITOR |
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
        INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
        INIT_WORK(&priv->set_beacon_wakeup_period_work,
                  cw1200_set_beacon_wakeup_period_work);
 -      init_timer(&priv->mcast_timeout);
 -      priv->mcast_timeout.data = (unsigned long)priv;
 -      priv->mcast_timeout.function = cw1200_mcast_timeout;
 +      setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout,
 +                  (unsigned long)priv);
  
        if (cw1200_queue_stats_init(&priv->tx_queue_stats,
                                    CW1200_LINK_ID_MAX,
@@@ -85,7 -85,6 +85,7 @@@
  #include "testmode.h"
  #include "iwl-fw-error-dump.h"
  #include "iwl-prph.h"
 +#include "iwl-csr.h"
  
  static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
  
  static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
        {
 -              .num_different_channels = 1,
 +              .num_different_channels = 2,
                .max_interfaces = 3,
                .limits = iwl_mvm_limits,
                .n_limits = ARRAY_SIZE(iwl_mvm_limits),
@@@ -327,6 -326,8 +327,8 @@@ int iwl_mvm_mac_setup_register(struct i
        hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
                IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
        hw->rate_control_algorithm = "iwl-mvm-rs";
+       hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
+       hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
  
        /*
         * Enable 11w if advertised by firmware and software crypto
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
  
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
-           !iwlwifi_mod_params.uapsd_disable) {
-               hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
-               hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
-               hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
-       }
        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
            mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
                hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
  
        hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
 +      /* we can compensate an offset of up to 3 channels = 15 MHz */
 +      hw->wiphy->max_adj_channel_rssi_comp = 3 * 5;
  
        /* Extract MAC address */
        memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
            device_can_wakeup(mvm->trans->dev)) {
                mvm->wowlan.flags = WIPHY_WOWLAN_ANY;
                hw->wiphy->wowlan = &mvm->wowlan;
 -      } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
 +      }
 +
 +      if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
 -              mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
 -                                  WIPHY_WOWLAN_DISCONNECT |
 -                                  WIPHY_WOWLAN_EAP_IDENTITY_REQ |
 -                                  WIPHY_WOWLAN_RFKILL_RELEASE |
 -                                  WIPHY_WOWLAN_NET_DETECT;
 +              mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
 +                                   WIPHY_WOWLAN_DISCONNECT |
 +                                   WIPHY_WOWLAN_EAP_IDENTITY_REQ |
 +                                   WIPHY_WOWLAN_RFKILL_RELEASE |
 +                                   WIPHY_WOWLAN_NET_DETECT;
                if (!iwlwifi_mod_params.sw_crypto)
                        mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                             WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@@ -771,37 -761,22 +766,37 @@@ void iwl_mvm_fw_error_dump(struct iwl_m
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        struct iwl_fw_error_dump_info *dump_info;
 +      struct iwl_fw_error_dump_mem *dump_mem;
        struct iwl_mvm_dump_ptrs *fw_error_dump;
 -      const struct fw_img *img;
        u32 sram_len, sram_ofs;
        u32 file_len, rxf_len;
        unsigned long flags;
        int reg_val;
 +      u32 smem_len = mvm->cfg->smem_len;
  
        lockdep_assert_held(&mvm->mutex);
  
 +      /* W/A for 8000 HW family A-step */
 +      if (mvm->cfg->smem_len &&
 +          mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
 +          CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP)
 +              smem_len = 0x38000;
 +
        fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
        if (!fw_error_dump)
                return;
  
 -      img = &mvm->fw->img[mvm->cur_ucode];
 -      sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
 -      sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
 +      /* SRAM - include stack CCM if driver knows the values for it */
 +      if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
 +              const struct fw_img *img;
 +
 +              img = &mvm->fw->img[mvm->cur_ucode];
 +              sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
 +              sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
 +      } else {
 +              sram_ofs = mvm->cfg->dccm_offset;
 +              sram_len = mvm->cfg->dccm_len;
 +      }
  
        /* reading buffer size */
        reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
  
        file_len = sizeof(*dump_file) +
                   sizeof(*dump_data) * 3 +
 -                 sram_len +
 +                 sram_len + sizeof(*dump_mem) +
                   rxf_len +
                   sizeof(*dump_info);
  
 +      /* Make room for the SMEM, if it exists */
 +      if (smem_len)
 +              file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
 +
        dump_file = vzalloc(file_len);
        if (!dump_file) {
                kfree(fw_error_dump);
                mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
 +      dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
        memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
               sizeof(dump_info->fw_human_readable));
        strncpy(dump_info->dev_human_readable, mvm->cfg->name,
        }
  
        dump_data = iwl_fw_error_next_data(dump_data);
 -      dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
 -      dump_data->len = cpu_to_le32(sram_len);
 -      iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
 +      dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 +      dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
 +      dump_mem = (void *)dump_data->data;
 +      dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
 +      dump_mem->offset = cpu_to_le32(sram_ofs);
 +      iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
                                 sram_len);
  
 +      if (smem_len) {
 +              dump_data = iwl_fw_error_next_data(dump_data);
 +              dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
 +              dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
 +              dump_mem = (void *)dump_data->data;
 +              dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
 +              dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
 +              iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
 +                                       dump_mem->data, smem_len);
 +      }
 +
        fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
        fw_error_dump->op_mode_len = file_len;
        if (fw_error_dump->trans_ptr)
@@@ -903,11 -859,6 +898,11 @@@ static void iwl_mvm_restart_cleanup(str
        if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
                iwl_mvm_fw_error_dump(mvm);
  
 +      /* cleanup all stale references (scan, roc), but keep the
 +       * ucode_down ref until reconfig is complete
 +       */
 +      iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
 +
        iwl_trans_stop_device(mvm->trans);
  
        mvm->scan_status = IWL_MVM_SCAN_NONE;
  
        ieee80211_wake_queues(mvm->hw);
  
 -      /* cleanup all stale references (scan, roc), but keep the
 -       * ucode_down ref until reconfig is complete */
 -      iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
 -
        /* clear any stale d0i3 state */
        clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
  
@@@ -973,19 -928,6 +968,19 @@@ static int iwl_mvm_mac_start(struct iee
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
  
 +      /* Some hw restart cleanups must not hold the mutex */
 +      if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
 +              /*
 +               * Make sure we are out of d0i3. This is needed
 +               * to make sure the reference accounting is correct
 +               * (and there is no stale d0i3_exit_work).
 +               */
 +              wait_event_timeout(mvm->d0i3_exit_waitq,
 +                                 !test_bit(IWL_MVM_STATUS_IN_D0I3,
 +                                           &mvm->status),
 +                                 HZ);
 +      }
 +
        mutex_lock(&mvm->mutex);
        ret = __iwl_mvm_mac_start(mvm);
        mutex_unlock(&mvm->mutex);
@@@ -1035,13 -977,6 +1030,13 @@@ static void iwl_mvm_resume_complete(str
                IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
                _iwl_mvm_exit_d0i3(mvm);
        }
 +
 +      if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND)
 +              if (!wait_event_timeout(mvm->d0i3_exit_waitq,
 +                                      !test_bit(IWL_MVM_STATUS_IN_D0I3,
 +                                                &mvm->status),
 +                                      HZ))
 +                      WARN_ONCE(1, "D0i3 exit on resume timed out\n");
  }
  
  static void
@@@ -1064,13 -999,8 +1059,13 @@@ void __iwl_mvm_mac_stop(struct iwl_mvm 
  {
        lockdep_assert_held(&mvm->mutex);
  
 -      /* disallow low power states when the FW is down */
 -      iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 +      /*
 +       * Disallow low power states when the FW is down by taking
 +       * the UCODE_DOWN ref. in case of ongoing hw restart the
 +       * ref is already taken, so don't take it again.
 +       */
 +      if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
 +              iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
  
        /* async_handlers_wk is now blocked */
  
        /* the fw is stopped, the aux sta is dead: clean up driver state */
        iwl_mvm_del_aux_sta(mvm);
  
 +      /*
 +       * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete()
 +       * won't be called in this case).
 +       */
 +      clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
 +
        mvm->ucode_loaded = false;
  }
  
@@@ -1218,6 -1142,10 +1213,10 @@@ static int iwl_mvm_mac_add_interface(st
                mvm->bf_allowed_vif = mvmvif;
                vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                                     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+               if (mvm->fw->ucode_capa.flags &
+                                       IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
+                   !iwlwifi_mod_params.uapsd_disable)
+                       vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
        }
  
        /*
@@@ -2148,7 -2076,7 +2147,7 @@@ static void iwl_mvm_sta_pre_rcu_remove(
                                       struct ieee80211_sta *sta)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 -      struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 +      struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
  
        /*
         * This is called before mac80211 does RCU synchronisation,
@@@ -3163,7 -3091,7 +3162,7 @@@ static int iwl_mvm_set_tim(struct ieee8
                           bool set)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 -      struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 +      struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
  
        if (!mvm_sta || !mvm_sta->vif) {
                IWL_ERR(mvm, "Station is not associated to a vif\n");
@@@ -395,6 -395,7 +395,6 @@@ static int hwsim_radio_idx
  static struct platform_driver mac80211_hwsim_driver = {
        .driver = {
                .name = "mac80211_hwsim",
 -              .owner = THIS_MODULE,
        },
  };
  
@@@ -625,22 -626,22 +625,22 @@@ static int hwsim_fops_ps_write(void *da
        old_ps = data->ps;
        data->ps = val;
  
+       local_bh_disable();
        if (val == PS_MANUAL_POLL) {
-               ieee80211_iterate_active_interfaces(data->hw,
-                                                   IEEE80211_IFACE_ITER_NORMAL,
-                                                   hwsim_send_ps_poll, data);
+               ieee80211_iterate_active_interfaces_atomic(
+                       data->hw, IEEE80211_IFACE_ITER_NORMAL,
+                       hwsim_send_ps_poll, data);
                data->ps_poll_pending = true;
        } else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
-               ieee80211_iterate_active_interfaces(data->hw,
-                                                   IEEE80211_IFACE_ITER_NORMAL,
-                                                   hwsim_send_nullfunc_ps,
-                                                   data);
+               ieee80211_iterate_active_interfaces_atomic(
+                       data->hw, IEEE80211_IFACE_ITER_NORMAL,
+                       hwsim_send_nullfunc_ps, data);
        } else if (old_ps != PS_DISABLED && val == PS_DISABLED) {
-               ieee80211_iterate_active_interfaces(data->hw,
-                                                   IEEE80211_IFACE_ITER_NORMAL,
-                                                   hwsim_send_nullfunc_no_ps,
-                                                   data);
+               ieee80211_iterate_active_interfaces_atomic(
+                       data->hw, IEEE80211_IFACE_ITER_NORMAL,
+                       hwsim_send_nullfunc_no_ps, data);
        }
+       local_bh_enable();
  
        return 0;
  }
@@@ -2149,14 -2150,14 +2149,14 @@@ static int append_radio_msg(struct sk_b
        if (param->regd) {
                int i;
  
-               for (i = 0; hwsim_world_regdom_custom[i] != param->regd &&
-                    i < ARRAY_SIZE(hwsim_world_regdom_custom); i++)
-                       ;
+               for (i = 0; i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) {
+                       if (hwsim_world_regdom_custom[i] != param->regd)
+                               continue;
  
-               if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
                        ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
                        if (ret < 0)
                                return ret;
+                       break;
                }
        }
  
@@@ -856,16 -856,16 +856,16 @@@ mwifiex_parse_htinfo(struct mwifiex_pri
                        /* HT or VHT */
                        switch (tx_htinfo & (BIT(3) | BIT(2))) {
                        case 0:
-                               /* This will be 20MHz */
+                               rate->bw = RATE_INFO_BW_20;
                                break;
                        case (BIT(2)):
-                               rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+                               rate->bw = RATE_INFO_BW_40;
                                break;
                        case (BIT(3)):
-                               rate->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+                               rate->bw = RATE_INFO_BW_80;
                                break;
                        case (BIT(3) | BIT(2)):
-                               rate->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+                               rate->bw = RATE_INFO_BW_160;
                                break;
                        }
  
                if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) {
                        rate->mcs = priv->tx_rate;
                        rate->flags |= RATE_INFO_FLAGS_MCS;
+                       rate->bw = RATE_INFO_BW_20;
                        if (tx_htinfo & BIT(1))
-                               rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+                               rate->bw = RATE_INFO_BW_40;
                        if (tx_htinfo & BIT(2))
                                rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
                }
@@@ -910,10 -911,10 +911,10 @@@ mwifiex_dump_station_info(struct mwifie
  {
        u32 rate;
  
-       sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES |
-                       STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS |
-                       STATION_INFO_TX_BITRATE |
-                       STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
+       sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) |
+                       BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) |
+                       BIT(NL80211_STA_INFO_TX_BITRATE) |
+                       BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG);
  
        /* Get signal information from the firmware */
        if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
        sinfo->txrate.legacy = rate * 5;
  
        if (priv->bss_mode == NL80211_IFTYPE_STATION) {
-               sinfo->filled |= STATION_INFO_BSS_PARAM;
+               sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
                sinfo->bss_param.flags = 0;
                if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
                                                WLAN_CAPABILITY_SHORT_PREAMBLE)
@@@ -1037,10 -1038,11 +1038,11 @@@ mwifiex_cfg80211_dump_survey(struct wip
        survey->channel = ieee80211_get_channel(wiphy,
            ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band));
        survey->filled = SURVEY_INFO_NOISE_DBM |
-               SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY;
+                        SURVEY_INFO_TIME |
+                        SURVEY_INFO_TIME_BUSY;
        survey->noise = pchan_stats[idx].noise;
-       survey->channel_time = pchan_stats[idx].cca_scan_dur;
-       survey->channel_time_busy = pchan_stats[idx].cca_busy_dur;
+       survey->time = pchan_stats[idx].cca_scan_dur;
+       survey->time_busy = pchan_stats[idx].cca_busy_dur;
  
        return 0;
  }
@@@ -2386,10 -2388,6 +2388,10 @@@ int mwifiex_del_virtual_intf(struct wip
  
        priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
  
 +      if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA ||
 +          GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
 +              kfree(priv->hist_data);
 +
        return 0;
  }
  EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
@@@ -2425,16 -2423,30 +2427,16 @@@ mwifiex_is_pattern_supported(struct cfg
  }
  
  #ifdef CONFIG_PM
 -static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
 -                                  struct cfg80211_wowlan *wowlan)
 +static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
 +                                struct cfg80211_wowlan *wowlan)
  {
 -      struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
 -      struct mwifiex_ds_mef_cfg mef_cfg;
 -      struct mwifiex_mef_entry *mef_entry;
 -      int i, filt_num = 0, ret;
 +      int i, filt_num = 0, ret = 0;
        bool first_pat = true;
        u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
        const u8 ipv4_mc_mac[] = {0x33, 0x33};
        const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
 -      struct mwifiex_private *priv =
 -                      mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
 -
 -      if (!wowlan) {
 -              dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n");
 -              return 0;
 -      }
 -
 -      if (!priv->media_connected) {
 -              dev_warn(adapter->dev,
 -                       "Can not configure WOWLAN in disconnected state\n");
 -              return 0;
 -      }
 +      struct mwifiex_ds_mef_cfg mef_cfg;
 +      struct mwifiex_mef_entry *mef_entry;
  
        mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL);
        if (!mef_entry)
        for (i = 0; i < wowlan->n_patterns; i++) {
                memset(byte_seq, 0, sizeof(byte_seq));
                if (!mwifiex_is_pattern_supported(&wowlan->patterns[i],
 -                                                byte_seq,
 -                                                MWIFIEX_MEF_MAX_BYTESEQ)) {
 -                      wiphy_err(wiphy, "Pattern not supported\n");
 +                                      byte_seq,
 +                                      MWIFIEX_MEF_MAX_BYTESEQ)) {
 +                      dev_err(priv->adapter->dev, "Pattern not supported\n");
                        kfree(mef_entry);
                        return -EOPNOTSUPP;
                }
  
                mef_entry->filter[filt_num].repeat = 1;
                mef_entry->filter[filt_num].offset =
 -                                              wowlan->patterns[i].pkt_offset;
 +                      wowlan->patterns[i].pkt_offset;
                memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq,
 -                     sizeof(byte_seq));
 +                              sizeof(byte_seq));
                mef_entry->filter[filt_num].filt_type = TYPE_EQ;
  
                if (first_pat)
                mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
                mef_entry->filter[filt_num].repeat = 16;
                memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
 -                     ETH_ALEN);
 +                              ETH_ALEN);
                mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
 -                                                              ETH_ALEN;
 +                      ETH_ALEN;
                mef_entry->filter[filt_num].offset = 28;
                mef_entry->filter[filt_num].filt_type = TYPE_EQ;
                if (filt_num)
                filt_num++;
                mef_entry->filter[filt_num].repeat = 16;
                memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
 -                     ETH_ALEN);
 +                              ETH_ALEN);
                mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
 -                                                              ETH_ALEN;
 +                      ETH_ALEN;
                mef_entry->filter[filt_num].offset = 56;
                mef_entry->filter[filt_num].filt_type = TYPE_EQ;
                mef_entry->filter[filt_num].filt_action = TYPE_OR;
  
        if (!mef_cfg.criteria)
                mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
 -                                 MWIFIEX_CRITERIA_UNICAST |
 -                                 MWIFIEX_CRITERIA_MULTICAST;
 +                      MWIFIEX_CRITERIA_UNICAST |
 +                      MWIFIEX_CRITERIA_MULTICAST;
  
        ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
 -                             HostCmd_ACT_GEN_SET, 0, &mef_cfg, true);
 +                      HostCmd_ACT_GEN_SET, 0, &mef_cfg, true);
  
        kfree(mef_entry);
        return ret;
  }
  
 +static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
 +                                  struct cfg80211_wowlan *wowlan)
 +{
 +      struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
 +      struct mwifiex_ds_hs_cfg hs_cfg;
 +      int ret = 0;
 +      struct mwifiex_private *priv =
 +                      mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
 +
 +      if (!wowlan) {
 +              dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n");
 +              return 0;
 +      }
 +
 +      if (!priv->media_connected) {
 +              dev_warn(adapter->dev,
 +                       "Can not configure WOWLAN in disconnected state\n");
 +              return 0;
 +      }
 +
 +      if (wowlan->n_patterns || wowlan->magic_pkt) {
 +              ret = mwifiex_set_mef_filter(priv, wowlan);
 +              if (ret) {
 +                      dev_err(adapter->dev, "Failed to set MEF filter\n");
 +                      return ret;
 +              }
 +      }
 +
 +      if (wowlan->disconnect) {
 +              memset(&hs_cfg, 0, sizeof(hs_cfg));
 +              hs_cfg.is_invoke_hostcmd = false;
 +              hs_cfg.conditions = HS_CFG_COND_MAC_EVENT;
 +              hs_cfg.gpio = HS_CFG_GPIO_DEF;
 +              hs_cfg.gap = HS_CFG_GAP_DEF;
 +              ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
 +                                          MWIFIEX_SYNC_CMD, &hs_cfg);
 +              if (ret) {
 +                      dev_err(adapter->dev, "Failed to set HS params\n");
 +                      return ret;
 +              }
 +      }
 +
 +      return ret;
 +}
 +
  static int mwifiex_cfg80211_resume(struct wiphy *wiphy)
  {
        return 0;
@@@ -2905,7 -2872,7 +2907,7 @@@ static struct cfg80211_ops mwifiex_cfg8
  
  #ifdef CONFIG_PM
  static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
 -      .flags = WIPHY_WOWLAN_MAGIC_PKT,
 +      .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
        .n_patterns = MWIFIEX_MEF_MAX_FILTERS,
        .pattern_min_len = 1,
        .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
@@@ -68,7 -68,6 +68,6 @@@ int mwifiex_process_uap_event(struct mw
                                len = ETH_ALEN;
  
                        if (len != -1) {
-                               sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
                                sinfo.assoc_req_ies = &event->data[len];
                                len = (u8 *)sinfo.assoc_req_ies -
                                      (u8 *)&event->frame_control;
                dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
                memcpy(priv->netdev->dev_addr, adapter->event_body + 2,
                       ETH_ALEN);
 +              if (priv->hist_data)
 +                      mwifiex_hist_data_reset(priv);
                break;
        case EVENT_UAP_MIC_COUNTERMEASURES:
                /* For future development */
@@@ -79,12 -79,22 +79,12 @@@ static int wl12xx_set_authorized(struc
  static void wl1271_reg_notify(struct wiphy *wiphy,
                              struct regulatory_request *request)
  {
 -      struct ieee80211_supported_band *band;
 -      struct ieee80211_channel *ch;
 -      int i;
        struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
        struct wl1271 *wl = hw->priv;
  
 -      band = wiphy->bands[IEEE80211_BAND_5GHZ];
 -      for (i = 0; i < band->n_channels; i++) {
 -              ch = &band->channels[i];
 -              if (ch->flags & IEEE80211_CHAN_DISABLED)
 -                      continue;
 -
 -              if (ch->flags & IEEE80211_CHAN_RADAR)
 -                      ch->flags |= IEEE80211_CHAN_NO_IR;
 -
 -      }
 +      /* copy the current dfs region */
 +      if (request)
 +              wl->dfs_region = request->dfs_region;
  
        wlcore_regdomain_config(wl);
  }
@@@ -216,29 -226,6 +216,29 @@@ void wl12xx_rearm_tx_watchdog_locked(st
                msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
  }
  
 +static void wlcore_rc_update_work(struct work_struct *work)
 +{
 +      int ret;
 +      struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
 +                                              rc_update_work);
 +      struct wl1271 *wl = wlvif->wl;
 +
 +      mutex_lock(&wl->mutex);
 +
 +      if (unlikely(wl->state != WLCORE_STATE_ON))
 +              goto out;
 +
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      wlcore_hw_sta_rc_update(wl, wlvif);
 +
 +      wl1271_ps_elp_sleep(wl);
 +out:
 +      mutex_unlock(&wl->mutex);
 +}
 +
  static void wl12xx_tx_watchdog_work(struct work_struct *work)
  {
        struct delayed_work *dwork;
@@@ -1675,15 -1662,19 +1675,15 @@@ static int wl1271_configure_suspend_sta
        if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
                goto out;
  
 -      ret = wl1271_ps_elp_wakeup(wl);
 -      if (ret < 0)
 -              goto out;
 -
        ret = wl1271_configure_wowlan(wl, wow);
        if (ret < 0)
 -              goto out_sleep;
 +              goto out;
  
        if ((wl->conf.conn.suspend_wake_up_event ==
             wl->conf.conn.wake_up_event) &&
            (wl->conf.conn.suspend_listen_interval ==
             wl->conf.conn.listen_interval))
 -              goto out_sleep;
 +              goto out;
  
        ret = wl1271_acx_wake_up_conditions(wl, wlvif,
                                    wl->conf.conn.suspend_wake_up_event,
  
        if (ret < 0)
                wl1271_error("suspend: set wake up conditions failed: %d", ret);
 -
 -out_sleep:
 -      wl1271_ps_elp_sleep(wl);
  out:
        return ret;
  
  }
  
  static int wl1271_configure_suspend_ap(struct wl1271 *wl,
 -                                     struct wl12xx_vif *wlvif)
 +                                      struct wl12xx_vif *wlvif,
 +                                      struct cfg80211_wowlan *wow)
  {
        int ret = 0;
  
        if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
                goto out;
  
 -      ret = wl1271_ps_elp_wakeup(wl);
 +      ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
        if (ret < 0)
                goto out;
  
 -      ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
 +      ret = wl1271_configure_wowlan(wl, wow);
 +      if (ret < 0)
 +              goto out;
  
 -      wl1271_ps_elp_sleep(wl);
  out:
        return ret;
  
@@@ -1725,7 -1717,7 +1725,7 @@@ static int wl1271_configure_suspend(str
        if (wlvif->bss_type == BSS_TYPE_STA_BSS)
                return wl1271_configure_suspend_sta(wl, wlvif, wow);
        if (wlvif->bss_type == BSS_TYPE_AP_BSS)
 -              return wl1271_configure_suspend_ap(wl, wlvif);
 +              return wl1271_configure_suspend_ap(wl, wlvif, wow);
        return 0;
  }
  
@@@ -1738,18 -1730,21 +1738,18 @@@ static void wl1271_configure_resume(str
        if ((!is_ap) && (!is_sta))
                return;
  
 -      if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
 +      if ((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) ||
 +          (is_ap && !test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)))
                return;
  
 -      ret = wl1271_ps_elp_wakeup(wl);
 -      if (ret < 0)
 -              return;
 +      wl1271_configure_wowlan(wl, NULL);
  
        if (is_sta) {
 -              wl1271_configure_wowlan(wl, NULL);
 -
                if ((wl->conf.conn.suspend_wake_up_event ==
                     wl->conf.conn.wake_up_event) &&
                    (wl->conf.conn.suspend_listen_interval ==
                     wl->conf.conn.listen_interval))
 -                      goto out_sleep;
 +                      return;
  
                ret = wl1271_acx_wake_up_conditions(wl, wlvif,
                                    wl->conf.conn.wake_up_event,
        } else if (is_ap) {
                ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
        }
 -
 -out_sleep:
 -      wl1271_ps_elp_sleep(wl);
  }
  
  static int wl1271_op_suspend(struct ieee80211_hw *hw,
        wl1271_tx_flush(wl);
  
        mutex_lock(&wl->mutex);
 +
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              return ret;
 +
        wl->wow_enabled = true;
        wl12xx_for_each_wlvif(wl, wlvif) {
                ret = wl1271_configure_suspend(wl, wlvif, wow);
                        return ret;
                }
        }
 +
 +      /* disable fast link flow control notifications from FW */
 +      ret = wlcore_hw_interrupt_notify(wl, false);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +      /* if filtering is enabled, configure the FW to drop all RX BA frames */
 +      ret = wlcore_hw_rx_ba_filter(wl,
 +                                   !!wl->conf.conn.suspend_rx_ba_activity);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +out_sleep:
 +      wl1271_ps_elp_sleep(wl);
        mutex_unlock(&wl->mutex);
 +
 +      if (ret < 0) {
 +              wl1271_warning("couldn't prepare device to suspend");
 +              return ret;
 +      }
 +
        /* flush any remaining work */
        wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
  
@@@ -1891,29 -1864,13 +1891,29 @@@ static int wl1271_op_resume(struct ieee
        if (pending_recovery) {
                wl1271_warning("queuing forgotten recovery on resume");
                ieee80211_queue_work(wl->hw, &wl->recovery_work);
 -              goto out;
 +              goto out_sleep;
        }
  
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
        wl12xx_for_each_wlvif(wl, wlvif) {
                wl1271_configure_resume(wl, wlvif);
        }
  
 +      ret = wlcore_hw_interrupt_notify(wl, true);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +      /* if filtering is enabled, configure the FW to drop all RX BA frames */
 +      ret = wlcore_hw_rx_ba_filter(wl, false);
 +      if (ret < 0)
 +              goto out_sleep;
 +
 +out_sleep:
 +      wl1271_ps_elp_sleep(wl);
 +
  out:
        wl->wow_enabled = false;
  
@@@ -2322,7 -2279,6 +2322,7 @@@ static int wl12xx_init_vif_data(struct 
                  wl1271_rx_streaming_enable_work);
        INIT_WORK(&wlvif->rx_streaming_disable_work,
                  wl1271_rx_streaming_disable_work);
 +      INIT_WORK(&wlvif->rc_update_work, wlcore_rc_update_work);
        INIT_DELAYED_WORK(&wlvif->channel_switch_work,
                          wlcore_channel_switch_work);
        INIT_DELAYED_WORK(&wlvif->connection_loss_work,
@@@ -2552,6 -2508,7 +2552,7 @@@ static int wl1271_op_add_interface(stru
        }
  
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+                            IEEE80211_VIF_SUPPORTS_UAPSD |
                             IEEE80211_VIF_SUPPORTS_CQM_RSSI;
  
        wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
@@@ -2767,7 -2724,6 +2768,7 @@@ unlock
        del_timer_sync(&wlvif->rx_streaming_timer);
        cancel_work_sync(&wlvif->rx_streaming_enable_work);
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
 +      cancel_work_sync(&wlvif->rc_update_work);
        cancel_delayed_work_sync(&wlvif->connection_loss_work);
        cancel_delayed_work_sync(&wlvif->channel_switch_work);
        cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
@@@ -4117,14 -4073,8 +4118,14 @@@ static int wl1271_bss_beacon_info_chang
                ret = wlcore_set_beacon_template(wl, vif, is_ap);
                if (ret < 0)
                        goto out;
 -      }
  
 +              if (test_and_clear_bit(WLVIF_FLAG_BEACON_DISABLED,
 +                                     &wlvif->flags)) {
 +                      ret = wlcore_hw_dfs_master_restart(wl, wlvif);
 +                      if (ret < 0)
 +                              goto out;
 +              }
 +      }
  out:
        if (ret != 0)
                wl1271_error("beacon info change failed: %d", ret);
@@@ -4625,46 -4575,10 +4626,46 @@@ static void wlcore_op_change_chanctx(st
                                     struct ieee80211_chanctx_conf *ctx,
                                     u32 changed)
  {
 +      struct wl1271 *wl = hw->priv;
 +      struct wl12xx_vif *wlvif;
 +      int ret;
 +      int channel = ieee80211_frequency_to_channel(
 +              ctx->def.chan->center_freq);
 +
        wl1271_debug(DEBUG_MAC80211,
                     "mac80211 change chanctx %d (type %d) changed 0x%x",
 -                   ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
 -                   cfg80211_get_chandef_type(&ctx->def), changed);
 +                   channel, cfg80211_get_chandef_type(&ctx->def), changed);
 +
 +      mutex_lock(&wl->mutex);
 +
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      wl12xx_for_each_wlvif(wl, wlvif) {
 +              struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 +
 +              rcu_read_lock();
 +              if (rcu_access_pointer(vif->chanctx_conf) != ctx) {
 +                      rcu_read_unlock();
 +                      continue;
 +              }
 +              rcu_read_unlock();
 +
 +              /* start radar if needed */
 +              if (changed & IEEE80211_CHANCTX_CHANGE_RADAR &&
 +                  wlvif->bss_type == BSS_TYPE_AP_BSS &&
 +                  ctx->radar_enabled && !wlvif->radar_enabled &&
 +                  ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
 +                      wl1271_debug(DEBUG_MAC80211, "Start radar detection");
 +                      wlcore_hw_set_cac(wl, wlvif, true);
 +                      wlvif->radar_enabled = true;
 +              }
 +      }
 +
 +      wl1271_ps_elp_sleep(wl);
 +out:
 +      mutex_unlock(&wl->mutex);
  }
  
  static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int channel = ieee80211_frequency_to_channel(
                ctx->def.chan->center_freq);
 +      int ret = -EINVAL;
  
        wl1271_debug(DEBUG_MAC80211,
 -                   "mac80211 assign chanctx (role %d) %d (type %d)",
 -                   wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
 +                   "mac80211 assign chanctx (role %d) %d (type %d) (radar %d dfs_state %d)",
 +                   wlvif->role_id, channel,
 +                   cfg80211_get_chandef_type(&ctx->def),
 +                   ctx->radar_enabled, ctx->def.chan->dfs_state);
  
        mutex_lock(&wl->mutex);
  
 +      if (unlikely(wl->state != WLCORE_STATE_ON))
 +              goto out;
 +
 +      if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
 +              goto out;
 +
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
        wlvif->band = ctx->def.chan->band;
        wlvif->channel = channel;
        wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
        /* update default rates according to the band */
        wl1271_set_band_rate(wl, wlvif);
  
 +      if (ctx->radar_enabled &&
 +          ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
 +              wl1271_debug(DEBUG_MAC80211, "Start radar detection");
 +              wlcore_hw_set_cac(wl, wlvif, true);
 +              wlvif->radar_enabled = true;
 +      }
 +
 +      wl1271_ps_elp_sleep(wl);
 +out:
        mutex_unlock(&wl->mutex);
  
        return 0;
@@@ -4722,7 -4614,6 +4723,7 @@@ static void wlcore_op_unassign_vif_chan
  {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 +      int ret;
  
        wl1271_debug(DEBUG_MAC80211,
                     "mac80211 unassign chanctx (role %d) %d (type %d)",
                     cfg80211_get_chandef_type(&ctx->def));
  
        wl1271_tx_flush(wl);
 +
 +      mutex_lock(&wl->mutex);
 +
 +      if (unlikely(wl->state != WLCORE_STATE_ON))
 +              goto out;
 +
 +      if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
 +              goto out;
 +
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      if (wlvif->radar_enabled) {
 +              wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
 +              wlcore_hw_set_cac(wl, wlvif, false);
 +              wlvif->radar_enabled = false;
 +      }
 +
 +      wl1271_ps_elp_sleep(wl);
 +out:
 +      mutex_unlock(&wl->mutex);
 +}
 +
 +static int __wlcore_switch_vif_chan(struct wl1271 *wl,
 +                                  struct wl12xx_vif *wlvif,
 +                                  struct ieee80211_chanctx_conf *new_ctx)
 +{
 +      int channel = ieee80211_frequency_to_channel(
 +              new_ctx->def.chan->center_freq);
 +
 +      wl1271_debug(DEBUG_MAC80211,
 +                   "switch vif (role %d) %d -> %d chan_type: %d",
 +                   wlvif->role_id, wlvif->channel, channel,
 +                   cfg80211_get_chandef_type(&new_ctx->def));
 +
 +      if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
 +              return 0;
 +
 +      WARN_ON(!test_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags));
 +
 +      if (wlvif->radar_enabled) {
 +              wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
 +              wlcore_hw_set_cac(wl, wlvif, false);
 +              wlvif->radar_enabled = false;
 +      }
 +
 +      wlvif->band = new_ctx->def.chan->band;
 +      wlvif->channel = channel;
 +      wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def);
 +
 +      /* start radar if needed */
 +      if (new_ctx->radar_enabled) {
 +              wl1271_debug(DEBUG_MAC80211, "Start radar detection");
 +              wlcore_hw_set_cac(wl, wlvif, true);
 +              wlvif->radar_enabled = true;
 +      }
 +
 +      return 0;
 +}
 +
 +static int
 +wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw,
 +                           struct ieee80211_vif_chanctx_switch *vifs,
 +                           int n_vifs,
 +                           enum ieee80211_chanctx_switch_mode mode)
 +{
 +      struct wl1271 *wl = hw->priv;
 +      int i, ret;
 +
 +      wl1271_debug(DEBUG_MAC80211,
 +                   "mac80211 switch chanctx n_vifs %d mode %d",
 +                   n_vifs, mode);
 +
 +      mutex_lock(&wl->mutex);
 +
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      for (i = 0; i < n_vifs; i++) {
 +              struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif);
 +
 +              ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx);
 +              if (ret)
 +                      goto out_sleep;
 +      }
 +out_sleep:
 +      wl1271_ps_elp_sleep(wl);
 +out:
 +      mutex_unlock(&wl->mutex);
 +
 +      return 0;
  }
  
  static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
  }
  
 +static const void *wlcore_get_beacon_ie(struct wl1271 *wl,
 +                                      struct wl12xx_vif *wlvif,
 +                                      u8 eid)
 +{
 +      int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
 +      struct sk_buff *beacon =
 +              ieee80211_beacon_get(wl->hw, wl12xx_wlvif_to_vif(wlvif));
 +
 +      if (!beacon)
 +              return NULL;
 +
 +      return cfg80211_find_ie(eid,
 +                              beacon->data + ieoffset,
 +                              beacon->len - ieoffset);
 +}
 +
 +static int wlcore_get_csa_count(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 +                              u8 *csa_count)
 +{
 +      const u8 *ie;
 +      const struct ieee80211_channel_sw_ie *ie_csa;
 +
 +      ie = wlcore_get_beacon_ie(wl, wlvif, WLAN_EID_CHANNEL_SWITCH);
 +      if (!ie)
 +              return -EINVAL;
 +
 +      ie_csa = (struct ieee80211_channel_sw_ie *)&ie[2];
 +      *csa_count = ie_csa->count;
 +
 +      return 0;
 +}
 +
 +static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw,
 +                                          struct ieee80211_vif *vif,
 +                                          struct cfg80211_chan_def *chandef)
 +{
 +      struct wl1271 *wl = hw->priv;
 +      struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 +      struct ieee80211_channel_switch ch_switch = {
 +              .block_tx = true,
 +              .chandef = *chandef,
 +      };
 +      int ret;
 +
 +      wl1271_debug(DEBUG_MAC80211,
 +                   "mac80211 channel switch beacon (role %d)",
 +                   wlvif->role_id);
 +
 +      ret = wlcore_get_csa_count(wl, wlvif, &ch_switch.count);
 +      if (ret < 0) {
 +              wl1271_error("error getting beacon (for CSA counter)");
 +              return;
 +      }
 +
 +      mutex_lock(&wl->mutex);
 +
 +      if (unlikely(wl->state != WLCORE_STATE_ON)) {
 +              ret = -EBUSY;
 +              goto out;
 +      }
 +
 +      ret = wl1271_ps_elp_wakeup(wl);
 +      if (ret < 0)
 +              goto out;
 +
 +      ret = wl->ops->channel_switch(wl, wlvif, &ch_switch);
 +      if (ret)
 +              goto out_sleep;
 +
 +      set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
 +
 +out_sleep:
 +      wl1271_ps_elp_sleep(wl);
 +out:
 +      mutex_unlock(&wl->mutex);
 +}
 +
  static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                            u32 queues, bool drop)
  {
@@@ -5650,25 -5371,20 +5651,26 @@@ static void wlcore_op_sta_rc_update(str
                                    u32 changed)
  {
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
 -      struct wl1271 *wl = hw->priv;
  
 -      wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
 +      wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update");
 +
 +      if (!(changed & IEEE80211_RC_BW_CHANGED))
 +              return;
 +
 +      /* this callback is atomic, so schedule a new work */
 +      wlvif->rc_update_bw = sta->bandwidth;
 +      ieee80211_queue_work(hw, &wlvif->rc_update_work);
  }
  
- static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
-                              struct ieee80211_vif *vif,
-                              struct ieee80211_sta *sta,
-                              s8 *rssi_dbm)
+ static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_sta *sta,
+                                    struct station_info *sinfo)
  {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       int ret = 0;
+       s8 rssi_dbm;
+       int ret;
  
        wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
  
        if (ret < 0)
                goto out_sleep;
  
-       ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm);
+       ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm);
        if (ret < 0)
                goto out_sleep;
  
+       sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+       sinfo->signal = rssi_dbm;
  out_sleep:
        wl1271_ps_elp_sleep(wl);
  
  out:
        mutex_unlock(&wl->mutex);
-       return ret;
  }
  
  static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
@@@ -5882,7 -5599,6 +5885,7 @@@ static const struct ieee80211_ops wl127
        .set_bitrate_mask = wl12xx_set_bitrate_mask,
        .set_default_unicast_key = wl1271_op_set_default_key_idx,
        .channel_switch = wl12xx_op_channel_switch,
 +      .channel_switch_beacon = wlcore_op_channel_switch_beacon,
        .flush = wlcore_op_flush,
        .remain_on_channel = wlcore_op_remain_on_channel,
        .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
        .change_chanctx = wlcore_op_change_chanctx,
        .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
        .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
 +      .switch_vif_chanctx = wlcore_op_switch_vif_chanctx,
        .sta_rc_update = wlcore_op_sta_rc_update,
-       .get_rssi = wlcore_op_get_rssi,
+       .sta_statistics = wlcore_op_sta_statistics,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
  };
  
@@@ -6064,7 -5779,6 +6067,6 @@@ static int wl1271_init_ieee80211(struc
        wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
-               IEEE80211_HW_SUPPORTS_UAPSD |
                IEEE80211_HW_HAS_RATE_CONTROL |
                IEEE80211_HW_CONNECTION_MONITOR |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
  
        wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
                                WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
 -                              WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
 +                              WIPHY_FLAG_SUPPORTS_SCHED_SCAN |
 +                              WIPHY_FLAG_HAS_CHANNEL_SWITCH;
  
        /* make sure all our channels fit in the scanned_ch bitmask */
        BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
@@@ -275,8 -275,7 +275,8 @@@ static int rtw_cfg80211_inform_bss(stru
                            &pnetwork->network)) {
                notify_signal = 100 * translate_percentage_to_dbm(padapter->recvpriv.signal_strength);  /* dbm */
        } else {
 -              notify_signal = 100 * translate_percentage_to_dbm(pnetwork->network.PhyInfo.SignalStrength);    /* dbm */
 +              notify_signal = 100 * translate_percentage_to_dbm(
 +                      pnetwork->network.SignalStrength);      /* dbm */
        }
  
        bss = cfg80211_inform_bss(wiphy, notify_channel,
@@@ -472,6 -471,7 +472,6 @@@ static int rtw_cfg80211_ap_set_encrypti
                                          int set_tx, const u8 *sta_addr,
                                          struct key_params *keyparms)
  {
 -      int ret = 0;
        int key_len;
        struct sta_info *psta = NULL, *pbcmc_sta = NULL;
        struct rtw_adapter *padapter = netdev_priv(dev);
  
  exit:
  
 -      return ret;
 +      return 0;
  }
  #endif
  
@@@ -850,6 -850,7 +850,6 @@@ static int rtw_cfg80211_set_encryption(
                                            dot11PrivacyAlgrthm;
                                }
                        }
 -              } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {        /* adhoc mode */
                }
        }
  
@@@ -1091,17 -1092,17 +1091,17 @@@ static int cfg80211_rtw_get_station(str
                        goto exit;
                }
  
-               sinfo->filled |= STATION_INFO_SIGNAL;
+               sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
                sinfo->signal = translate_percentage_to_dbm(padapter->recvpriv.
                                                            signal_strength);
  
-               sinfo->filled |= STATION_INFO_TX_BITRATE;
+               sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
                sinfo->txrate.legacy = rtw_get_cur_max_rate(padapter);
  
-               sinfo->filled |= STATION_INFO_RX_PACKETS;
+               sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
                sinfo->rx_packets = sta_rx_data_pkts(psta);
  
-               sinfo->filled |= STATION_INFO_TX_PACKETS;
+               sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
                sinfo->tx_packets = psta->sta_stats.tx_pkts;
        }
  
@@@ -2363,7 -2364,7 +2363,7 @@@ void rtw_cfg80211_indicate_sta_assoc(st
                        ie_offset = offsetof(struct ieee80211_mgmt,
                                             u.reassoc_req.variable);
  
-               sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
+               sinfo.filled = 0;
                sinfo.assoc_req_ies = pmgmt_frame + ie_offset;
                sinfo.assoc_req_ies_len = frame_len - ie_offset;
                cfg80211_new_sta(ndev, hdr->addr2, &sinfo, GFP_ATOMIC);
@@@ -2430,16 -2431,20 +2430,16 @@@ void rtw_cfg80211_indicate_sta_disassoc
  
  static int rtw_cfg80211_monitor_if_open(struct net_device *ndev)
  {
 -      int ret = 0;
 -
        DBG_8723A("%s\n", __func__);
  
 -      return ret;
 +      return 0;
  }
  
  static int rtw_cfg80211_monitor_if_close(struct net_device *ndev)
  {
 -      int ret = 0;
 -
        DBG_8723A("%s\n", __func__);
  
 -      return ret;
 +      return 0;
  }
  
  static int rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb,
@@@ -2568,9 -2573,11 +2568,9 @@@ fail
  static int
  rtw_cfg80211_monitor_if_set_mac_address(struct net_device *ndev, void *addr)
  {
 -      int ret = 0;
 -
        DBG_8723A("%s\n", __func__);
  
 -      return ret;
 +      return 0;
  }
  
  static const struct net_device_ops rtw_cfg80211_monitor_if_ops = {