Commit 6f4e235b authored by Wen Gong's avatar Wen Gong Committed by Kalle Valo

wifi: ath11k: add parse of transmit power envelope element

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: default avatarWen Gong <quic_wgong@quicinc.com>
Acked-by: default avatarJeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: default avatarBaochen Qiang <quic_bqiang@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20231218085844.2658-8-quic_bqiang@quicinc.com
parent 28f64d36
......@@ -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 */
......
......@@ -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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment