]> Git Repo - J-linux.git/commitdiff
Merge tag 'ath-next-20240130' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath
authorKalle Valo <[email protected]>
Wed, 31 Jan 2024 16:45:31 +0000 (18:45 +0200)
committerKalle Valo <[email protected]>
Wed, 31 Jan 2024 16:45:31 +0000 (18:45 +0200)
ath.git patches for v6.9

We have new features for both ath11k and ath12k. ath12k is now under
heavy refactoring in preparation for MLO support.

Major changes:

ath12k

* refactoring in preparation for Multi-Link Operation (MLO) support

* 1024 Block Ack window size support

* provide firmware wmi logs via a trace event

ath11k

* 36 bit DMA mask support

* support 6 GHz station power modes: Low Power Indoor (LPI), Standard
  Power) SP and Very Low Power (VLP)

1  2 
drivers/net/wireless/ath/ath11k/core.h
drivers/net/wireless/ath/ath11k/mac.c

index 02e160d831bed13f3358034048ce5b03d36dc090,12fa5b28520f05f2cfd1bc3f67bbaaa20c16851e..0b2d4a93d706e129f8eae67168a665daf63880eb
@@@ -314,6 -314,43 +314,43 @@@ struct ath11k_rekey_data 
        bool enable_offload;
  };
  
+ /**
+  * struct ath11k_chan_power_info - TPE containing power info per channel chunk
+  * @chan_cfreq: channel center freq (MHz)
+  * e.g.
+  * channel 37/20 MHz,  it is 6135
+  * channel 37/40 MHz,  it is 6125
+  * channel 37/80 MHz,  it is 6145
+  * channel 37/160 MHz, it is 6185
+  * @tx_power: transmit power (dBm)
+  */
+ struct ath11k_chan_power_info {
+       u16 chan_cfreq;
+       s8 tx_power;
+ };
+ /**
+  * struct ath11k_reg_tpc_power_info - regulatory TPC power info
+  * @is_psd_power: is PSD power or not
+  * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD
+  * @ap_power_type: type of power (SP/LPI/VLP)
+  * @num_pwr_levels: number of power levels
+  * @reg_max: Array of maximum TX power (dBm) per PSD value
+  * @ap_constraint_power: AP constraint power (dBm)
+  * @tpe: TPE values processed from TPE IE
+  * @chan_power_info: power info to send to firmware
+  */
+ struct ath11k_reg_tpc_power_info {
+       bool is_psd_power;
+       u8 eirp_power;
+       enum wmi_reg_6ghz_ap_type ap_power_type;
+       u8 num_pwr_levels;
+       u8 reg_max[IEEE80211_MAX_NUM_PWR_LEVEL];
+       u8 ap_constraint_power;
+       s8 tpe[IEEE80211_MAX_NUM_PWR_LEVEL];
+       struct ath11k_chan_power_info chan_power_info[IEEE80211_MAX_NUM_PWR_LEVEL];
+ };
  struct ath11k_vif {
        u32 vdev_id;
        enum wmi_vdev_type vdev_type;
        struct ieee80211_chanctx_conf chanctx;
        struct ath11k_arp_ns_offload arp_ns_offload;
        struct ath11k_rekey_data rekey_data;
 -
 -#ifdef CONFIG_ATH11K_DEBUGFS
 -      struct dentry *debugfs_twt;
 -#endif /* CONFIG_ATH11K_DEBUGFS */
+       struct ath11k_reg_tpc_power_info reg_tpc_info;
  };
  
  struct ath11k_vif_iter {
@@@ -735,6 -778,7 +774,7 @@@ struct ath11k 
        /* protected by conf_mutex */
        bool ps_state_enable;
        bool ps_timekeeper_enable;
+       s8 max_allowed_tx_power;
  };
  
  struct ath11k_band_cap {
@@@ -918,6 -962,7 +958,7 @@@ struct ath11k_base 
         * This may or may not be used during the runtime
         */
        struct ieee80211_regdomain *new_regd[MAX_RADIOS];
+       struct cur_regulatory_info *reg_info_store;
  
        /* Current DFS Regulatory */
        enum ath11k_dfs_region dfs_region;
index b13525bbbb8087acbdc15247a0a428a74fd5f8b9,4a6ab45e9386ae7041ab83cd2ad377ff41c4bb1c..bbf4d1f4d310c1f9b1a1355faeb6274499ca031c
@@@ -1,7 -1,7 +1,7 @@@
  // SPDX-License-Identifier: BSD-3-Clause-Clear
  /*
   * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
-  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+  * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
   */
  
  #include <net/mac80211.h>
@@@ -255,9 -255,6 +255,6 @@@ static const u32 ath11k_smps_map[] = 
        [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
  };
  
- static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif);
  enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy)
  {
        enum nl80211_he_ru_alloc ret;
@@@ -3397,6 -3394,18 +3394,18 @@@ static int ath11k_mac_config_obss_pd(st
        return 0;
  }
  
+ static bool ath11k_mac_supports_station_tpc(struct ath11k *ar,
+                                           struct ath11k_vif *arvif,
+                                           const struct cfg80211_chan_def *chandef)
+ {
+       return ath11k_wmi_supports_6ghz_cc_ext(ar) &&
+               test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map) &&
+               arvif->vdev_type == WMI_VDEV_TYPE_STA &&
+               arvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE &&
+               chandef->chan &&
+               chandef->chan->band == NL80211_BAND_6GHZ;
+ }
  static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw,
                                           struct ieee80211_vif *vif,
                                           struct ieee80211_bss_conf *info,
        if (changed & BSS_CHANGED_TXPOWER) {
                ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev_id %i txpower %d\n",
                           arvif->vdev_id, info->txpower);
                arvif->txpower = info->txpower;
                ath11k_mac_txpower_recalc(ar);
        }
@@@ -4906,100 -4914,6 +4914,6 @@@ static void ath11k_mac_dec_num_stations
        ar->num_stations--;
  }
  
- static int ath11k_mac_station_add(struct ath11k *ar,
-                                 struct ieee80211_vif *vif,
-                                 struct ieee80211_sta *sta)
- {
-       struct ath11k_base *ab = ar->ab;
-       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
-       struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
-       struct peer_create_params peer_param;
-       int ret;
-       lockdep_assert_held(&ar->conf_mutex);
-       ret = ath11k_mac_inc_num_stations(arvif, sta);
-       if (ret) {
-               ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n",
-                           ar->max_num_stations);
-               goto exit;
-       }
-       arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL);
-       if (!arsta->rx_stats) {
-               ret = -ENOMEM;
-               goto dec_num_station;
-       }
-       peer_param.vdev_id = arvif->vdev_id;
-       peer_param.peer_addr = sta->addr;
-       peer_param.peer_type = WMI_PEER_TYPE_DEFAULT;
-       ret = ath11k_peer_create(ar, arvif, sta, &peer_param);
-       if (ret) {
-               ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n",
-                           sta->addr, arvif->vdev_id);
-               goto free_rx_stats;
-       }
-       ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n",
-                  sta->addr, arvif->vdev_id);
-       if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) {
-               arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL);
-               if (!arsta->tx_stats) {
-                       ret = -ENOMEM;
-                       goto free_peer;
-               }
-       }
-       if (ieee80211_vif_is_mesh(vif)) {
-               ath11k_dbg(ab, ATH11K_DBG_MAC,
-                          "setting USE_4ADDR for mesh STA %pM\n", sta->addr);
-               ret = ath11k_wmi_set_peer_param(ar, sta->addr,
-                                               arvif->vdev_id,
-                                               WMI_PEER_USE_4ADDR, 1);
-               if (ret) {
-                       ath11k_warn(ab, "failed to set mesh STA %pM 4addr capability: %d\n",
-                                   sta->addr, ret);
-                       goto free_tx_stats;
-               }
-       }
-       ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr);
-       if (ret) {
-               ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n",
-                           sta->addr, arvif->vdev_id, ret);
-               goto free_tx_stats;
-       }
-       if (ab->hw_params.vdev_start_delay &&
-           !arvif->is_started &&
-           arvif->vdev_type != WMI_VDEV_TYPE_AP) {
-               ret = ath11k_start_vdev_delay(ar->hw, vif);
-               if (ret) {
-                       ath11k_warn(ab, "failed to delay vdev start: %d\n", ret);
-                       goto free_tx_stats;
-               }
-       }
-       ewma_avg_rssi_init(&arsta->avg_rssi);
-       return 0;
- free_tx_stats:
-       kfree(arsta->tx_stats);
-       arsta->tx_stats = NULL;
- free_peer:
-       ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
- free_rx_stats:
-       kfree(arsta->rx_stats);
-       arsta->rx_stats = NULL;
- dec_num_station:
-       ath11k_mac_dec_num_stations(arvif, sta);
- exit:
-       return ret;
- }
  static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar,
                                              struct ieee80211_sta *sta)
  {
        return bw;
  }
  
- static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif,
-                                  struct ieee80211_sta *sta,
-                                  enum ieee80211_sta_state old_state,
-                                  enum ieee80211_sta_state new_state)
- {
-       struct ath11k *ar = hw->priv;
-       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
-       struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
-       struct ath11k_peer *peer;
-       int ret = 0;
-       /* cancel must be done outside the mutex to avoid deadlock */
-       if ((old_state == IEEE80211_STA_NONE &&
-            new_state == IEEE80211_STA_NOTEXIST)) {
-               cancel_work_sync(&arsta->update_wk);
-               cancel_work_sync(&arsta->set_4addr_wk);
-       }
-       mutex_lock(&ar->conf_mutex);
-       if (old_state == IEEE80211_STA_NOTEXIST &&
-           new_state == IEEE80211_STA_NONE) {
-               memset(arsta, 0, sizeof(*arsta));
-               arsta->arvif = arvif;
-               arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
-               INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
-               INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk);
-               ret = ath11k_mac_station_add(ar, vif, sta);
-               if (ret)
-                       ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n",
-                                   sta->addr, arvif->vdev_id);
-       } else if ((old_state == IEEE80211_STA_NONE &&
-                   new_state == IEEE80211_STA_NOTEXIST)) {
-               bool skip_peer_delete = ar->ab->hw_params.vdev_start_delay &&
-                       vif->type == NL80211_IFTYPE_STATION;
-               ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr);
-               if (!skip_peer_delete) {
-                       ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
-                       if (ret)
-                               ath11k_warn(ar->ab,
-                                           "Failed to delete peer: %pM for VDEV: %d\n",
-                                           sta->addr, arvif->vdev_id);
-                       else
-                               ath11k_dbg(ar->ab,
-                                          ATH11K_DBG_MAC,
-                                          "Removed peer: %pM for VDEV: %d\n",
-                                          sta->addr, arvif->vdev_id);
-               }
-               ath11k_mac_dec_num_stations(arvif, sta);
-               mutex_lock(&ar->ab->tbl_mtx_lock);
-               spin_lock_bh(&ar->ab->base_lock);
-               peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
-               if (skip_peer_delete && peer) {
-                       peer->sta = NULL;
-               } else if (peer && peer->sta == sta) {
-                       ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
-                                   vif->addr, arvif->vdev_id);
-                       ath11k_peer_rhash_delete(ar->ab, peer);
-                       peer->sta = NULL;
-                       list_del(&peer->list);
-                       kfree(peer);
-                       ar->num_peers--;
-               }
-               spin_unlock_bh(&ar->ab->base_lock);
-               mutex_unlock(&ar->ab->tbl_mtx_lock);
-               kfree(arsta->tx_stats);
-               arsta->tx_stats = NULL;
-               kfree(arsta->rx_stats);
-               arsta->rx_stats = NULL;
-       } else if (old_state == IEEE80211_STA_AUTH &&
-                  new_state == IEEE80211_STA_ASSOC &&
-                  (vif->type == NL80211_IFTYPE_AP ||
-                   vif->type == NL80211_IFTYPE_MESH_POINT ||
-                   vif->type == NL80211_IFTYPE_ADHOC)) {
-               ret = ath11k_station_assoc(ar, vif, sta, false);
-               if (ret)
-                       ath11k_warn(ar->ab, "Failed to associate station: %pM\n",
-                                   sta->addr);
-               spin_lock_bh(&ar->data_lock);
-               /* Set arsta bw and prev bw */
-               arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta);
-               arsta->bw_prev = arsta->bw;
-               spin_unlock_bh(&ar->data_lock);
-       } else if (old_state == IEEE80211_STA_ASSOC &&
-                  new_state == IEEE80211_STA_AUTHORIZED) {
-               spin_lock_bh(&ar->ab->base_lock);
-               peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
-               if (peer)
-                       peer->is_authorized = true;
-               spin_unlock_bh(&ar->ab->base_lock);
-               if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) {
-                       ret = ath11k_wmi_set_peer_param(ar, sta->addr,
-                                                       arvif->vdev_id,
-                                                       WMI_PEER_AUTHORIZE,
-                                                       1);
-                       if (ret)
-                               ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n",
-                                           sta->addr, arvif->vdev_id, ret);
-               }
-       } else if (old_state == IEEE80211_STA_AUTHORIZED &&
-                  new_state == IEEE80211_STA_ASSOC) {
-               spin_lock_bh(&ar->ab->base_lock);
-               peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
-               if (peer)
-                       peer->is_authorized = false;
-               spin_unlock_bh(&ar->ab->base_lock);
-       } else if (old_state == IEEE80211_STA_ASSOC &&
-                  new_state == IEEE80211_STA_AUTH &&
-                  (vif->type == NL80211_IFTYPE_AP ||
-                   vif->type == NL80211_IFTYPE_MESH_POINT ||
-                   vif->type == NL80211_IFTYPE_ADHOC)) {
-               ret = ath11k_station_disassoc(ar, vif, sta);
-               if (ret)
-                       ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n",
-                                   sta->addr);
-       }
-       mutex_unlock(&ar->conf_mutex);
-       return ret;
- }
  static int ath11k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       struct ieee80211_sta *sta)
@@@ -6756,6 -6536,13 +6536,6 @@@ static int ath11k_mac_op_add_interface(
                goto err;
        }
  
 -      /* In the case of hardware recovery, debugfs files are
 -       * not deleted since ieee80211_ops.remove_interface() is
 -       * not invoked. In such cases, try to delete the files.
 -       * These will be re-created later.
 -       */
 -      ath11k_debugfs_remove_interface(arvif);
 -
        memset(arvif, 0, sizeof(*arvif));
  
        arvif->ar = ar;
  
        ath11k_dp_vdev_tx_attach(ar, arvif);
  
 -      ath11k_debugfs_add_interface(arvif);
 -
        if (vif->type != NL80211_IFTYPE_MONITOR &&
            test_bit(ATH11K_FLAG_MONITOR_CONF_ENABLED, &ar->monitor_flags)) {
                ret = ath11k_mac_monitor_vdev_create(ar);
                                    ret);
        }
  
+       if (ath11k_wmi_supports_6ghz_cc_ext(ar)) {
+               struct cur_regulatory_info *reg_info;
+               reg_info = &ab->reg_info_store[ar->pdev_idx];
+               ath11k_dbg(ab, ATH11K_DBG_MAC, "interface added to change reg rules\n");
+               ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_LPI_AP);
+       }
        mutex_unlock(&ar->conf_mutex);
  
        return 0;
@@@ -7047,6 -6844,8 +6835,6 @@@ err_vdev_del
        /* Recalc txpower for remaining vdev */
        ath11k_mac_txpower_recalc(ar);
  
 -      ath11k_debugfs_remove_interface(arvif);
 -
        /* TODO: recal traffic pause state based on the available vdevs */
  
        mutex_unlock(&ar->conf_mutex);
@@@ -7266,6 -7065,15 +7054,15 @@@ ath11k_mac_vdev_start_restart(struct at
                return ret;
        }
  
+       /* TODO: For now we only set TPC power here. However when
+        * channel changes, say CSA, it should be updated again.
+        */
+       if (ath11k_mac_supports_station_tpc(ar, arvif, chandef)) {
+               ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx);
+               ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id,
+                                                  &arvif->reg_tpc_info);
+       }
        if (!restart)
                ar->num_started_vdevs++;
  
@@@ -7542,8 -7350,8 +7339,8 @@@ unlock
        mutex_unlock(&ar->conf_mutex);
  }
  
- static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif)
+ static int ath11k_mac_start_vdev_delay(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
  {
        struct ath11k *ar = hw->priv;
        struct ath11k_base *ab = ar->ab;
        return 0;
  }
  
- static int
- ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
-                                struct ieee80211_vif *vif,
-                                struct ieee80211_bss_conf *link_conf,
-                                struct ieee80211_chanctx_conf *ctx)
+ static int ath11k_mac_stop_vdev_early(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif)
  {
        struct ath11k *ar = hw->priv;
        struct ath11k_base *ab = ar->ab;
        struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
        int ret;
-       struct peer_create_params param;
-       mutex_lock(&ar->conf_mutex);
-       ath11k_dbg(ab, ATH11K_DBG_MAC,
-                  "chanctx assign ptr %p vdev_id %i\n",
-                  ctx, arvif->vdev_id);
  
-       /* for QCA6390 bss peer must be created before vdev_start */
-       if (ab->hw_params.vdev_start_delay &&
-           arvif->vdev_type != WMI_VDEV_TYPE_AP &&
-           arvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
-           !ath11k_peer_find_by_vdev_id(ab, arvif->vdev_id)) {
-               memcpy(&arvif->chanctx, ctx, sizeof(*ctx));
-               ret = 0;
-               goto out;
-       }
+       if (WARN_ON(!arvif->is_started))
+               return -EBUSY;
  
-       if (WARN_ON(arvif->is_started)) {
-               ret = -EBUSY;
-               goto out;
+       ret = ath11k_mac_vdev_stop(arvif);
+       if (ret) {
+               ath11k_warn(ab, "failed to stop vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
        }
  
-       if (ab->hw_params.vdev_start_delay &&
-           arvif->vdev_type != WMI_VDEV_TYPE_AP &&
-           arvif->vdev_type != WMI_VDEV_TYPE_MONITOR) {
-               param.vdev_id = arvif->vdev_id;
-               param.peer_type = WMI_PEER_TYPE_DEFAULT;
-               param.peer_addr = ar->mac_addr;
+       arvif->is_started = false;
  
-               ret = ath11k_peer_create(ar, arvif, NULL, &param);
-               if (ret) {
-                       ath11k_warn(ab, "failed to create peer after vdev start delay: %d",
-                                   ret);
+       /* TODO: Setup ps and cts/rts protection */
+       return 0;
+ }
+ static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt)
+ {
+       switch (txpwr_intrprt) {
+       /* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield
+        * if the Maximum Transmit Power Interpretation subfield is 0 or 2" of
+        * "IEEE Std 802.11ax 2021".
+        */
+       case IEEE80211_TPE_LOCAL_EIRP:
+       case IEEE80211_TPE_REG_CLIENT_EIRP:
+               txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3;
+               txpwr_cnt = txpwr_cnt + 1;
+               break;
+       /* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield
+        * if Maximum Transmit Power Interpretation subfield is 1 or 3" of
+        * "IEEE Std 802.11ax 2021".
+        */
+       case IEEE80211_TPE_LOCAL_EIRP_PSD:
+       case IEEE80211_TPE_REG_CLIENT_EIRP_PSD:
+               txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4;
+               txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1;
+               break;
+       }
+       return txpwr_cnt;
+ }
+ static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def)
+ {
+       if (chan_def->chan->flags & IEEE80211_CHAN_PSD) {
+               switch (chan_def->width) {
+               case NL80211_CHAN_WIDTH_20:
+                       return 1;
+               case NL80211_CHAN_WIDTH_40:
+                       return 2;
+               case NL80211_CHAN_WIDTH_80:
+                       return 4;
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       return 8;
+               default:
+                       return 1;
+               }
+       } else {
+               switch (chan_def->width) {
+               case NL80211_CHAN_WIDTH_20:
+                       return 1;
+               case NL80211_CHAN_WIDTH_40:
+                       return 2;
+               case NL80211_CHAN_WIDTH_80:
+                       return 3;
+               case NL80211_CHAN_WIDTH_80P80:
+               case NL80211_CHAN_WIDTH_160:
+                       return 4;
+               default:
+                       return 1;
+               }
+       }
+ }
+ static u16 ath11k_mac_get_6ghz_start_frequency(struct cfg80211_chan_def *chan_def)
+ {
+       u16 diff_seq;
+       /* It is to get the lowest channel number's center frequency of the chan.
+        * For example,
+        * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1
+        * with center frequency 5955, its diff is 5965 - 5955 = 10.
+        * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1
+        * with center frequency 5955, its diff is 5985 - 5955 = 30.
+        * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1
+        * with center frequency 5955, its diff is 6025 - 5955 = 70.
+        */
+       switch (chan_def->width) {
+       case NL80211_CHAN_WIDTH_160:
+               diff_seq = 70;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+       case NL80211_CHAN_WIDTH_80P80:
+               diff_seq = 30;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               diff_seq = 10;
+               break;
+       default:
+               diff_seq = 0;
+       }
+       return chan_def->center_freq1 - diff_seq;
+ }
+ static u16 ath11k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def,
+                                  u16 start_seq, u8 seq)
+ {
+       u16 seg_seq;
+       /* It is to get the center frequency of the specific bandwidth.
+        * start_seq means the lowest channel number's center frequency.
+        * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz&80P80.
+        * For example,
+        * lowest channel is 1, its center frequency 5955,
+        * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0.
+        * lowest channel is 1, its center frequency 5955,
+        * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10.
+        * lowest channel is 1, its center frequency 5955,
+        * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30.
+        * lowest channel is 1, its center frequency 5955,
+        * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70.
+        */
+       if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3)
+               return chan_def->center_freq2;
+       seg_seq = 10 * (BIT(seq) - 1);
+       return seg_seq + start_seq;
+ }
+ static void ath11k_mac_get_psd_channel(struct ath11k *ar,
+                                      u16 step_freq,
+                                      u16 *start_freq,
+                                      u16 *center_freq,
+                                      u8 i,
+                                      struct ieee80211_channel **temp_chan,
+                                      s8 *tx_power)
+ {
+       /* It is to get the center frequency for each 20 MHz.
+        * For example, if the chan is 160 MHz and center frequency is 6025,
+        * then it include 8 channels, they are 1/5/9/13/17/21/25/29,
+        * channel number 1's center frequency is 5955, it is parameter start_freq.
+        * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels.
+        * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7,
+        * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095,
+        * the gap is 20 for each channel, parameter step_freq means the gap.
+        * after get the center frequency of each channel, it is easy to find the
+        * struct ieee80211_channel of it and get the max_reg_power.
+        */
+       *center_freq = *start_freq + i * step_freq;
+       *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+       *tx_power = (*temp_chan)->max_reg_power;
+ }
+ static void ath11k_mac_get_eirp_power(struct ath11k *ar,
+                                     u16 *start_freq,
+                                     u16 *center_freq,
+                                     u8 i,
+                                     struct ieee80211_channel **temp_chan,
+                                     struct cfg80211_chan_def *def,
+                                     s8 *tx_power)
+ {
+       /* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/
+        * 160 MHz&80P80 bandwidth, and then plus 10 to the center frequency,
+        * it is the center frequency of a channel number.
+        * For example, when configured channel number is 1.
+        * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975,
+        * then it is channel number 5.
+        * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995,
+        * then it is channel number 9.
+        * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035,
+        * then it is channel number 17.
+        * after get the center frequency of each channel, it is easy to find the
+        * struct ieee80211_channel of it and get the max_reg_power.
+        */
+       *center_freq = ath11k_mac_get_seg_freq(def, *start_freq, i);
+       /* For the 20 MHz, its center frequency is same with same channel */
+       if (i != 0)
+               *center_freq += 10;
+       *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq);
+       *tx_power = (*temp_chan)->max_reg_power;
+ }
+ void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_chanctx_conf *ctx)
+ {
+       struct ath11k_base *ab = ar->ab;
+       struct ath11k_vif *arvif = (void *)vif->drv_priv;
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+       struct ath11k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info;
+       struct ieee80211_channel *chan, *temp_chan;
+       u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction;
+       bool is_psd_power = false, is_tpe_present = false;
+       s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL],
+               psd_power, tx_power, eirp_power;
+       u16 start_freq, center_freq;
+       chan = ctx->def.chan;
+       start_freq = ath11k_mac_get_6ghz_start_frequency(&ctx->def);
+       pwr_reduction = bss_conf->pwr_reduction;
+       if (arvif->reg_tpc_info.num_pwr_levels) {
+               is_tpe_present = true;
+               num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels;
+       } else {
+               num_pwr_levels = ath11k_mac_get_num_pwr_levels(&ctx->def);
+       }
+       for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) {
+               /* STA received TPE IE*/
+               if (is_tpe_present) {
+                       /* local power is PSD power*/
+                       if (chan->flags & IEEE80211_CHAN_PSD) {
+                               /* Connecting AP is psd power */
+                               if (reg_tpc_info->is_psd_power) {
+                                       is_psd_power = true;
+                                       ath11k_mac_get_psd_channel(ar, 20,
+                                                                  &start_freq,
+                                                                  &center_freq,
+                                                                  pwr_lvl_idx,
+                                                                  &temp_chan,
+                                                                  &tx_power);
+                                       psd_power = temp_chan->psd;
+                                       eirp_power = tx_power;
+                                       max_tx_power[pwr_lvl_idx] =
+                                               min_t(s8,
+                                                     psd_power,
+                                                     reg_tpc_info->tpe[pwr_lvl_idx]);
+                               /* Connecting AP is not psd power */
+                               } else {
+                                       ath11k_mac_get_eirp_power(ar,
+                                                                 &start_freq,
+                                                                 &center_freq,
+                                                                 pwr_lvl_idx,
+                                                                 &temp_chan,
+                                                                 &ctx->def,
+                                                                 &tx_power);
+                                       psd_power = temp_chan->psd;
+                                       /* convert psd power to EIRP power based
+                                        * on channel width
+                                        */
+                                       tx_power =
+                                               min_t(s8, tx_power,
+                                                     psd_power + 13 + pwr_lvl_idx * 3);
+                                       max_tx_power[pwr_lvl_idx] =
+                                               min_t(s8,
+                                                     tx_power,
+                                                     reg_tpc_info->tpe[pwr_lvl_idx]);
+                               }
+                       /* local power is not PSD power */
+                       } else {
+                               /* Connecting AP is psd power */
+                               if (reg_tpc_info->is_psd_power) {
+                                       is_psd_power = true;
+                                       ath11k_mac_get_psd_channel(ar, 20,
+                                                                  &start_freq,
+                                                                  &center_freq,
+                                                                  pwr_lvl_idx,
+                                                                  &temp_chan,
+                                                                  &tx_power);
+                                       eirp_power = tx_power;
+                                       max_tx_power[pwr_lvl_idx] =
+                                               reg_tpc_info->tpe[pwr_lvl_idx];
+                               /* Connecting AP is not psd power */
+                               } else {
+                                       ath11k_mac_get_eirp_power(ar,
+                                                                 &start_freq,
+                                                                 &center_freq,
+                                                                 pwr_lvl_idx,
+                                                                 &temp_chan,
+                                                                 &ctx->def,
+                                                                 &tx_power);
+                                       max_tx_power[pwr_lvl_idx] =
+                                               min_t(s8,
+                                                     tx_power,
+                                                     reg_tpc_info->tpe[pwr_lvl_idx]);
+                               }
+                       }
+               /* STA not received TPE IE */
+               } else {
+                       /* local power is PSD power*/
+                       if (chan->flags & IEEE80211_CHAN_PSD) {
+                               is_psd_power = true;
+                               ath11k_mac_get_psd_channel(ar, 20,
+                                                          &start_freq,
+                                                          &center_freq,
+                                                          pwr_lvl_idx,
+                                                          &temp_chan,
+                                                          &tx_power);
+                               psd_power = temp_chan->psd;
+                               eirp_power = tx_power;
+                               max_tx_power[pwr_lvl_idx] = psd_power;
+                       } else {
+                               ath11k_mac_get_eirp_power(ar,
+                                                         &start_freq,
+                                                         &center_freq,
+                                                         pwr_lvl_idx,
+                                                         &temp_chan,
+                                                         &ctx->def,
+                                                         &tx_power);
+                               max_tx_power[pwr_lvl_idx] = tx_power;
+                       }
+               }
+               if (is_psd_power) {
+                       /* If AP local power constraint is present */
+                       if (pwr_reduction)
+                               eirp_power = eirp_power - pwr_reduction;
+                       /* If firmware updated max tx power is non zero, then take
+                        * the min of firmware updated ap tx power
+                        * and max power derived from above mentioned parameters.
+                        */
+                       ath11k_dbg(ab, ATH11K_DBG_MAC,
+                                  "eirp power : %d firmware report power : %d\n",
+                                  eirp_power, ar->max_allowed_tx_power);
+                       /* Firmware reports lower max_allowed_tx_power during vdev
+                        * start response. In case of 6 GHz, firmware is not aware
+                        * of EIRP power unless driver sets EIRP power through WMI
+                        * TPC command. So radio which does not support idle power
+                        * save can set maximum calculated EIRP power directly to
+                        * firmware through TPC command without min comparison with
+                        * vdev start response's max_allowed_tx_power.
+                        */
+                       if (ar->max_allowed_tx_power && ab->hw_params.idle_ps)
+                               eirp_power = min_t(s8,
+                                                  eirp_power,
+                                                  ar->max_allowed_tx_power);
+               } else {
+                       /* If AP local power constraint is present */
+                       if (pwr_reduction)
+                               max_tx_power[pwr_lvl_idx] =
+                                       max_tx_power[pwr_lvl_idx] - pwr_reduction;
+                       /* If firmware updated max tx power is non zero, then take
+                        * the min of firmware updated ap tx power
+                        * and max power derived from above mentioned parameters.
+                        */
+                       if (ar->max_allowed_tx_power && ab->hw_params.idle_ps)
+                               max_tx_power[pwr_lvl_idx] =
+                                       min_t(s8,
+                                             max_tx_power[pwr_lvl_idx],
+                                             ar->max_allowed_tx_power);
+               }
+               reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq;
+               reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power =
+                       max_tx_power[pwr_lvl_idx];
+       }
+       reg_tpc_info->num_pwr_levels = num_pwr_levels;
+       reg_tpc_info->is_psd_power = is_psd_power;
+       reg_tpc_info->eirp_power = eirp_power;
+       reg_tpc_info->ap_power_type =
+               ath11k_reg_ap_pwr_convert(vif->bss_conf.power_type);
+ }
+ static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_chanctx_conf *ctx)
+ {
+       struct ath11k_base *ab = ar->ab;
+       struct ath11k_vif *arvif = (void *)vif->drv_priv;
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+       struct ieee80211_tx_pwr_env *single_tpe;
+       enum wmi_reg_6ghz_client_type client_type;
+       struct cur_regulatory_info *reg_info;
+       int i;
+       u8 pwr_count, pwr_interpret, pwr_category;
+       u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0;
+       bool use_local_tpe, non_psd_set = false, psd_set = false;
+       reg_info = &ab->reg_info_store[ar->pdev_idx];
+       client_type = reg_info->client_type;
+       for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
+               single_tpe = &bss_conf->tx_pwr_env[i];
+               pwr_category = u8_get_bits(single_tpe->tx_power_info,
+                                          IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
+               pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+                                           IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+               if (pwr_category == client_type) {
+                       if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP ||
+                           pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD)
+                               local_tpe_count++;
+                       else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP ||
+                                pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD)
+                               reg_tpe_count++;
+               }
+       }
+       if (!reg_tpe_count && !local_tpe_count) {
+               ath11k_warn(ab,
+                           "no transmit power envelope match client power type %d\n",
+                           client_type);
+               return;
+       } else if (!reg_tpe_count) {
+               use_local_tpe = true;
+       } else {
+               use_local_tpe = false;
+       }
+       for (i = 0; i < bss_conf->tx_pwr_env_num; i++) {
+               single_tpe = &bss_conf->tx_pwr_env[i];
+               pwr_category = u8_get_bits(single_tpe->tx_power_info,
+                                          IEEE80211_TX_PWR_ENV_INFO_CATEGORY);
+               pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+                                           IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+               if (pwr_category != client_type)
+                       continue;
+               /* get local transmit power envelope */
+               if (use_local_tpe) {
+                       if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) {
+                               non_psd_index = i;
+                               non_psd_set = true;
+                       } else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) {
+                               psd_index = i;
+                               psd_set = true;
+                       }
+               /* get regulatory transmit power envelope */
+               } else {
+                       if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) {
+                               non_psd_index = i;
+                               non_psd_set = true;
+                       } else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) {
+                               psd_index = i;
+                               psd_set = true;
+                       }
+               }
+       }
+       if (non_psd_set && !psd_set) {
+               single_tpe = &bss_conf->tx_pwr_env[non_psd_index];
+               pwr_count = u8_get_bits(single_tpe->tx_power_info,
+                                       IEEE80211_TX_PWR_ENV_INFO_COUNT);
+               pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+                                           IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+               arvif->reg_tpc_info.is_psd_power = false;
+               arvif->reg_tpc_info.eirp_power = 0;
+               arvif->reg_tpc_info.num_pwr_levels =
+                       ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
+               for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
+                       ath11k_dbg(ab, ATH11K_DBG_MAC,
+                                  "non PSD power[%d] : %d\n",
+                                  i, single_tpe->tx_power[i]);
+                       arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
+               }
+       }
+       if (psd_set) {
+               single_tpe = &bss_conf->tx_pwr_env[psd_index];
+               pwr_count = u8_get_bits(single_tpe->tx_power_info,
+                                       IEEE80211_TX_PWR_ENV_INFO_COUNT);
+               pwr_interpret = u8_get_bits(single_tpe->tx_power_info,
+                                           IEEE80211_TX_PWR_ENV_INFO_INTERPRET);
+               arvif->reg_tpc_info.is_psd_power = true;
+               if (pwr_count == 0) {
+                       ath11k_dbg(ab, ATH11K_DBG_MAC,
+                                  "TPE PSD power : %d\n", single_tpe->tx_power[0]);
+                       arvif->reg_tpc_info.num_pwr_levels =
+                               ath11k_mac_get_num_pwr_levels(&ctx->def);
+                       for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++)
+                               arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2;
+               } else {
+                       arvif->reg_tpc_info.num_pwr_levels =
+                               ath11k_mac_get_tpe_count(pwr_interpret, pwr_count);
+                       for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) {
+                               ath11k_dbg(ab, ATH11K_DBG_MAC,
+                                          "TPE PSD power[%d] : %d\n",
+                                          i, single_tpe->tx_power[i]);
+                               arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2;
+                       }
+               }
+       }
+ }
+ static int
+ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_bss_conf *link_conf,
+                                struct ieee80211_chanctx_conf *ctx)
+ {
+       struct ath11k *ar = hw->priv;
+       struct ath11k_base *ab = ar->ab;
+       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+       int ret;
+       struct cur_regulatory_info *reg_info;
+       enum ieee80211_ap_reg_power power_type;
+       mutex_lock(&ar->conf_mutex);
+       ath11k_dbg(ab, ATH11K_DBG_MAC,
+                  "chanctx assign ptr %p vdev_id %i\n",
+                  ctx, arvif->vdev_id);
+       if (ath11k_wmi_supports_6ghz_cc_ext(ar) &&
+           ctx->def.chan->band == NL80211_BAND_6GHZ &&
+           arvif->vdev_type == WMI_VDEV_TYPE_STA) {
+               reg_info = &ab->reg_info_store[ar->pdev_idx];
+               power_type = vif->bss_conf.power_type;
+               ath11k_dbg(ab, ATH11K_DBG_MAC, "chanctx power type %d\n", power_type);
+               if (power_type == IEEE80211_REG_UNSET_AP) {
+                       ret = -EINVAL;
                        goto out;
                }
+               ath11k_reg_handle_chan_list(ab, reg_info, power_type);
+               arvif->chanctx = *ctx;
+               ath11k_mac_parse_tx_pwr_env(ar, vif, ctx);
+       }
+       /* for QCA6390 bss peer must be created before vdev_start */
+       if (ab->hw_params.vdev_start_delay &&
+           arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+           arvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
+           !ath11k_peer_find_by_vdev_id(ab, arvif->vdev_id)) {
+               memcpy(&arvif->chanctx, ctx, sizeof(*ctx));
+               ret = 0;
+               goto out;
+       }
+       if (WARN_ON(arvif->is_started)) {
+               ret = -EBUSY;
+               goto out;
        }
  
        if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
                goto out;
        }
  
-       ret = ath11k_mac_vdev_start(arvif, ctx);
-       if (ret) {
-               ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n",
-                           arvif->vdev_id, vif->addr,
-                           ctx->def.chan->center_freq, ret);
-               goto out;
-       }
+       if (!arvif->is_started) {
+               ret = ath11k_mac_vdev_start(arvif, ctx);
+               if (ret) {
+                       ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n",
+                                   arvif->vdev_id, vif->addr,
+                                   ctx->def.chan->center_freq, ret);
+                       goto out;
+               }
  
-       arvif->is_started = true;
+               arvif->is_started = true;
+       }
  
        if (arvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
            test_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags)) {
@@@ -7697,8 -8005,6 +7994,6 @@@ ath11k_mac_op_unassign_vif_chanctx(stru
                   "chanctx unassign ptr %p vdev_id %i\n",
                   ctx, arvif->vdev_id);
  
-       WARN_ON(!arvif->is_started);
        if (ab->hw_params.vdev_start_delay &&
            arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
                spin_lock_bh(&ab->base_lock);
                return;
        }
  
-       ret = ath11k_mac_vdev_stop(arvif);
-       if (ret)
-               ath11k_warn(ab, "failed to stop vdev %i: %d\n",
-                           arvif->vdev_id, ret);
-       arvif->is_started = false;
-       if (ab->hw_params.vdev_start_delay &&
-           arvif->vdev_type == WMI_VDEV_TYPE_STA) {
-               ret = ath11k_peer_delete(ar, arvif->vdev_id, arvif->bssid);
+       if (arvif->is_started) {
+               ret = ath11k_mac_vdev_stop(arvif);
                if (ret)
-                       ath11k_warn(ar->ab,
-                                   "failed to delete peer %pM for vdev %d: %d\n",
-                                   arvif->bssid, arvif->vdev_id, ret);
-               else
-                       ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
-                                  "removed peer %pM  vdev %d after vdev stop\n",
-                                  arvif->bssid, arvif->vdev_id);
+                       ath11k_warn(ab, "failed to stop vdev %i: %d\n",
+                                   arvif->vdev_id, ret);
+               arvif->is_started = false;
        }
  
        if (ab->hw_params.vdev_start_delay &&
@@@ -9097,6 -9392,252 +9381,252 @@@ err_fallback
        return 0;
  }
  
+ static int ath11k_mac_station_add(struct ath11k *ar,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sta *sta)
+ {
+       struct ath11k_base *ab = ar->ab;
+       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+       struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
+       struct peer_create_params peer_param;
+       int ret;
+       lockdep_assert_held(&ar->conf_mutex);
+       ret = ath11k_mac_inc_num_stations(arvif, sta);
+       if (ret) {
+               ath11k_warn(ab, "refusing to associate station: too many connected already (%d)\n",
+                           ar->max_num_stations);
+               goto exit;
+       }
+       arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL);
+       if (!arsta->rx_stats) {
+               ret = -ENOMEM;
+               goto dec_num_station;
+       }
+       peer_param.vdev_id = arvif->vdev_id;
+       peer_param.peer_addr = sta->addr;
+       peer_param.peer_type = WMI_PEER_TYPE_DEFAULT;
+       ret = ath11k_peer_create(ar, arvif, sta, &peer_param);
+       if (ret) {
+               ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n",
+                           sta->addr, arvif->vdev_id);
+               goto free_rx_stats;
+       }
+       ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n",
+                  sta->addr, arvif->vdev_id);
+       if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) {
+               arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL);
+               if (!arsta->tx_stats) {
+                       ret = -ENOMEM;
+                       goto free_peer;
+               }
+       }
+       if (ieee80211_vif_is_mesh(vif)) {
+               ath11k_dbg(ab, ATH11K_DBG_MAC,
+                          "setting USE_4ADDR for mesh STA %pM\n", sta->addr);
+               ret = ath11k_wmi_set_peer_param(ar, sta->addr,
+                                               arvif->vdev_id,
+                                               WMI_PEER_USE_4ADDR, 1);
+               if (ret) {
+                       ath11k_warn(ab, "failed to set mesh STA %pM 4addr capability: %d\n",
+                                   sta->addr, ret);
+                       goto free_tx_stats;
+               }
+       }
+       ret = ath11k_dp_peer_setup(ar, arvif->vdev_id, sta->addr);
+       if (ret) {
+               ath11k_warn(ab, "failed to setup dp for peer %pM on vdev %i (%d)\n",
+                           sta->addr, arvif->vdev_id, ret);
+               goto free_tx_stats;
+       }
+       if (ab->hw_params.vdev_start_delay &&
+           !arvif->is_started &&
+           arvif->vdev_type != WMI_VDEV_TYPE_AP) {
+               ret = ath11k_mac_start_vdev_delay(ar->hw, vif);
+               if (ret) {
+                       ath11k_warn(ab, "failed to delay vdev start: %d\n", ret);
+                       goto free_tx_stats;
+               }
+       }
+       ewma_avg_rssi_init(&arsta->avg_rssi);
+       return 0;
+ free_tx_stats:
+       kfree(arsta->tx_stats);
+       arsta->tx_stats = NULL;
+ free_peer:
+       ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
+ free_rx_stats:
+       kfree(arsta->rx_stats);
+       arsta->rx_stats = NULL;
+ dec_num_station:
+       ath11k_mac_dec_num_stations(arvif, sta);
+ exit:
+       return ret;
+ }
+ static int ath11k_mac_station_remove(struct ath11k *ar,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_sta *sta)
+ {
+       struct ath11k_base *ab = ar->ab;
+       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+       struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
+       int ret;
+       if (ab->hw_params.vdev_start_delay &&
+           arvif->is_started &&
+           arvif->vdev_type != WMI_VDEV_TYPE_AP) {
+               ret = ath11k_mac_stop_vdev_early(ar->hw, vif);
+               if (ret) {
+                       ath11k_warn(ab, "failed to do early vdev stop: %d\n", ret);
+                       return ret;
+               }
+       }
+       ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr);
+       ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
+       if (ret)
+               ath11k_warn(ab, "Failed to delete peer: %pM for VDEV: %d\n",
+                           sta->addr, arvif->vdev_id);
+       else
+               ath11k_dbg(ab, ATH11K_DBG_MAC, "Removed peer: %pM for VDEV: %d\n",
+                          sta->addr, arvif->vdev_id);
+       ath11k_mac_dec_num_stations(arvif, sta);
+       kfree(arsta->tx_stats);
+       arsta->tx_stats = NULL;
+       kfree(arsta->rx_stats);
+       arsta->rx_stats = NULL;
+       return ret;
+ }
+ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_sta *sta,
+                                  enum ieee80211_sta_state old_state,
+                                  enum ieee80211_sta_state new_state)
+ {
+       struct ath11k *ar = hw->priv;
+       struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
+       struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
+       struct ath11k_peer *peer;
+       int ret = 0;
+       /* cancel must be done outside the mutex to avoid deadlock */
+       if ((old_state == IEEE80211_STA_NONE &&
+            new_state == IEEE80211_STA_NOTEXIST)) {
+               cancel_work_sync(&arsta->update_wk);
+               cancel_work_sync(&arsta->set_4addr_wk);
+       }
+       mutex_lock(&ar->conf_mutex);
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE) {
+               memset(arsta, 0, sizeof(*arsta));
+               arsta->arvif = arvif;
+               arsta->peer_ps_state = WMI_PEER_PS_STATE_DISABLED;
+               INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
+               INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk);
+               ret = ath11k_mac_station_add(ar, vif, sta);
+               if (ret)
+                       ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+       } else if ((old_state == IEEE80211_STA_NONE &&
+                   new_state == IEEE80211_STA_NOTEXIST)) {
+               ret = ath11k_mac_station_remove(ar, vif, sta);
+               if (ret)
+                       ath11k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n",
+                                   sta->addr, arvif->vdev_id);
+               mutex_lock(&ar->ab->tbl_mtx_lock);
+               spin_lock_bh(&ar->ab->base_lock);
+               peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
+               if (peer && peer->sta == sta) {
+                       ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
+                                   vif->addr, arvif->vdev_id);
+                       ath11k_peer_rhash_delete(ar->ab, peer);
+                       peer->sta = NULL;
+                       list_del(&peer->list);
+                       kfree(peer);
+                       ar->num_peers--;
+               }
+               spin_unlock_bh(&ar->ab->base_lock);
+               mutex_unlock(&ar->ab->tbl_mtx_lock);
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_MESH_POINT ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               ret = ath11k_station_assoc(ar, vif, sta, false);
+               if (ret)
+                       ath11k_warn(ar->ab, "Failed to associate station: %pM\n",
+                                   sta->addr);
+               spin_lock_bh(&ar->data_lock);
+               /* Set arsta bw and prev bw */
+               arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta);
+               arsta->bw_prev = arsta->bw;
+               spin_unlock_bh(&ar->data_lock);
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTHORIZED) {
+               spin_lock_bh(&ar->ab->base_lock);
+               peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
+               if (peer)
+                       peer->is_authorized = true;
+               spin_unlock_bh(&ar->ab->base_lock);
+               if (vif->type == NL80211_IFTYPE_STATION && arvif->is_up) {
+                       ret = ath11k_wmi_set_peer_param(ar, sta->addr,
+                                                       arvif->vdev_id,
+                                                       WMI_PEER_AUTHORIZE,
+                                                       1);
+                       if (ret)
+                               ath11k_warn(ar->ab, "Unable to authorize peer %pM vdev %d: %d\n",
+                                           sta->addr, arvif->vdev_id, ret);
+               }
+       } else if (old_state == IEEE80211_STA_AUTHORIZED &&
+                  new_state == IEEE80211_STA_ASSOC) {
+               spin_lock_bh(&ar->ab->base_lock);
+               peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
+               if (peer)
+                       peer->is_authorized = false;
+               spin_unlock_bh(&ar->ab->base_lock);
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH &&
+                  (vif->type == NL80211_IFTYPE_AP ||
+                   vif->type == NL80211_IFTYPE_MESH_POINT ||
+                   vif->type == NL80211_IFTYPE_ADHOC)) {
+               ret = ath11k_station_disassoc(ar, vif, sta);
+               if (ret)
+                       ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n",
+                                   sta->addr);
+       }
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+ }
  static const struct ieee80211_ops ath11k_ops = {
        .tx                             = ath11k_mac_op_tx,
        .wake_tx_queue                  = ieee80211_handle_wake_tx_queue,
  #endif
  
  #ifdef CONFIG_ATH11K_DEBUGFS
 +      .vif_add_debugfs                = ath11k_debugfs_op_vif_add,
        .sta_add_debugfs                = ath11k_debugfs_sta_op_add,
  #endif
  
This page took 0.118342 seconds and 4 git commands to generate.