Commit 9a2aa68a authored by Aditya Kumar Singh's avatar Aditya Kumar Singh Committed by Kalle Valo

wifi: ath11k: add get_txpower mac ops

Driver does not support get_txpower mac ops because of which
cfg80211 returns vif->bss_conf.txpower to user space. bss_conf.txpower
gets its value from ieee80211_channel->max_reg_power. However, the final
txpower is dependent on few other parameters apart from max regulatory
supported power. It is the firmware which knows about all these
parameters and considers the minimum for each packet transmission.

All ath11k firmware reports the final tx power in firmware pdev stats
which falls under fw_stats.

Add get_txpower mac ops to get the tx power from firmware leveraging
fw_stats and return it accordingly.

Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
Signed-off-by: default avatarAditya Kumar Singh <quic_adisi@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220603082814.31466-3-quic_adisi@quicinc.com
parent ec8918f9
......@@ -8431,6 +8431,95 @@ static int ath11k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
return ret;
}
static int ath11k_fw_stats_request(struct ath11k *ar,
struct stats_request_params *req_param)
{
struct ath11k_base *ab = ar->ab;
unsigned long time_left;
int ret;
lockdep_assert_held(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
ar->fw_stats_done = false;
ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
spin_unlock_bh(&ar->data_lock);
reinit_completion(&ar->fw_stats_complete);
ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
if (ret) {
ath11k_warn(ab, "could not request fw stats (%d)\n",
ret);
return ret;
}
time_left = wait_for_completion_timeout(&ar->fw_stats_complete,
1 * HZ);
if (!time_left)
return -ETIMEDOUT;
return 0;
}
static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
int *dbm)
{
struct ath11k *ar = hw->priv;
struct ath11k_base *ab = ar->ab;
struct stats_request_params req_param = {0};
struct ath11k_fw_stats_pdev *pdev;
int ret;
/* Final Tx power is minimum of Target Power, CTL power, Regulatory
* Power, PSD EIRP Power. We just know the Regulatory power from the
* regulatory rules obtained. FW knows all these power and sets the min
* of these. Hence, we request the FW pdev stats in which FW reports
* the minimum of all vdev's channel Tx power.
*/
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH11K_STATE_ON)
goto err_fallback;
req_param.pdev_id = ar->pdev->pdev_id;
req_param.stats_id = WMI_REQUEST_PDEV_STAT;
ret = ath11k_fw_stats_request(ar, &req_param);
if (ret) {
ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
goto err_fallback;
}
spin_lock_bh(&ar->data_lock);
pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
struct ath11k_fw_stats_pdev, list);
if (!pdev) {
spin_unlock_bh(&ar->data_lock);
goto err_fallback;
}
/* tx power is set as 2 units per dBm in FW. */
*dbm = pdev->chan_tx_power / 2;
spin_unlock_bh(&ar->data_lock);
mutex_unlock(&ar->conf_mutex);
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware %d, reported %d dBm\n",
pdev->chan_tx_power, *dbm);
return 0;
err_fallback:
mutex_unlock(&ar->conf_mutex);
/* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
*dbm = vif->bss_conf.txpower;
ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
*dbm);
return 0;
}
static const struct ieee80211_ops ath11k_ops = {
.tx = ath11k_mac_op_tx,
.start = ath11k_mac_op_start,
......@@ -8481,6 +8570,7 @@ static const struct ieee80211_ops ath11k_ops = {
#if IS_ENABLED(CONFIG_IPV6)
.ipv6_addr_change = ath11k_mac_op_ipv6_changed,
#endif
.get_txpower = ath11k_mac_op_get_txpower,
.set_sar_specs = ath11k_mac_op_set_bios_sar_specs,
.remain_on_channel = ath11k_mac_op_remain_on_channel,
......@@ -9094,6 +9184,8 @@ int ath11k_mac_allocate(struct ath11k_base *ab)
clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags);
ar->vdev_id_11d_scan = ATH11K_11D_INVALID_VDEV_ID;
init_completion(&ar->completed_11d_scan);
ath11k_fw_stats_init(ar);
}
return 0;
......
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