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

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
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -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 */
+188 −0
Original line number Diff line number Diff line
@@ -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 */