]> Git Repo - linux.git/commitdiff
wifi: ath11k: add parse of transmit power envelope element
authorWen Gong <[email protected]>
Thu, 11 Jan 2024 13:56:58 +0000 (15:56 +0200)
committerKalle Valo <[email protected]>
Sun, 14 Jan 2024 14:59:09 +0000 (16:59 +0200)
The transmit power envelope element has some fields for power, ath11k
should parse it according to IEEE Std 802.11ax™‐2021.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23

Signed-off-by: Wen Gong <[email protected]>
Acked-by: Jeff Johnson <[email protected]>
Signed-off-by: Baochen Qiang <[email protected]>
Signed-off-by: Kalle Valo <[email protected]>
Link: https://msgid.link/[email protected]
drivers/net/wireless/ath/ath11k/core.h
drivers/net/wireless/ath/ath11k/mac.c

index cc91f7d3ca8e1725b60d64f7227a391a8f1dc3cd..9a398de5dc44fc202784e229851e7b19fe003a02 100644 (file)
@@ -314,6 +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;
@@ -369,6 +406,8 @@ struct ath11k_vif {
        struct ath11k_arp_ns_offload arp_ns_offload;
        struct ath11k_rekey_data rekey_data;
 
+       struct ath11k_reg_tpc_power_info reg_tpc_info;
+
 #ifdef CONFIG_ATH11K_DEBUGFS
        struct dentry *debugfs_twt;
 #endif /* CONFIG_ATH11K_DEBUGFS */
index 0b2b79ca5b1d11907f602120122d3af9f756706f..4be5b2a35ec99a44d84d9364288ca5ae724a3756 100644 (file)
@@ -7608,6 +7608,192 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw,
        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 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,
@@ -7642,6 +7828,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
                }
 
                ath11k_reg_handle_chan_list(ab, reg_info, power_type);
+
+               ath11k_mac_parse_tx_pwr_env(ar, vif, ctx);
        }
 
        /* for QCA6390 bss peer must be created before vdev_start */
This page took 0.115036 seconds and 4 git commands to generate.