On 2/1/2025 1:07 AM, Rameshkumar Sundaram wrote: > From: Aditya Kumar Singh <aditya.kumar.singh@xxxxxxxxxxxxxxxx> > > 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 ath12k 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. > > While at it, there is a possibility that repeated stats request WMI > commands are queued to FW if mac80211/userspace does get tx power back > to back(in Multiple BSS cases). This could potentially consume the WMI > queue completely. Hence limit this by fetching the power only for every > 5 seconds and reusing the value until the refresh timeout or when there > is a change in channel. > > Also remove init_completion(&ar->fw_stats_complete) in > ath12k_mac_hw_register() as ath12k_fw_stats_init() takes care of > it for each ar. > > Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 > Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 > > Signed-off-by: Aditya Kumar Singh <aditya.kumar.singh@xxxxxxxxxxxxxxxx> > Signed-off-by: Rameshkumar Sundaram <rameshkumar.sundaram@xxxxxxxxxxxxxxxx> > --- > drivers/net/wireless/ath/ath12k/core.h | 1 + > drivers/net/wireless/ath/ath12k/mac.c | 208 +++++++++++++++++-------- > drivers/net/wireless/ath/ath12k/mac.h | 3 + > 3 files changed, 148 insertions(+), 64 deletions(-) > > diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h > index 373e7baf379b..d278e4a8bb08 100644 > --- a/drivers/net/wireless/ath/ath12k/core.h > +++ b/drivers/net/wireless/ath/ath12k/core.h > @@ -731,6 +731,7 @@ struct ath12k { > u32 mlo_setup_status; > u8 ftm_msgref; > struct ath12k_fw_stats fw_stats; > + unsigned long last_tx_power_update; > }; > > struct ath12k_hw { > diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c > index 8cd33ea75590..efab885bdbb1 100644 > --- a/drivers/net/wireless/ath/ath12k/mac.c > +++ b/drivers/net/wireless/ath/ath12k/mac.c > @@ -4280,6 +4280,145 @@ static int ath12k_start_scan(struct ath12k *ar, > return 0; > } > > +int ath12k_mac_get_fw_stats(struct ath12k *ar, > + struct ath12k_fw_stats_req_params *param) > +{ > + struct ath12k_base *ab = ar->ab; > + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); > + unsigned long timeout, time_left; > + int ret; > + > + guard(mutex)(&ah->hw_mutex); > + > + if (ah->state != ATH12K_HW_STATE_ON) > + return -ENETDOWN; > + > + /* FW stats can get split when exceeding the stats data buffer limit. > + * In that case, since there is no end marking for the back-to-back > + * received 'update stats' event, we keep a 3 seconds timeout in case, > + * fw_stats_done is not marked yet > + */ > + timeout = jiffies + msecs_to_jiffies(3 * 1000); > + ath12k_fw_stats_reset(ar); > + > + reinit_completion(&ar->fw_stats_complete); > + > + ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id, > + param->vdev_id, param->pdev_id); > + > + if (ret) { > + ath12k_warn(ab, "failed to request fw stats: %d\n", ret); > + return ret; > + } > + > + ath12k_dbg(ab, ATH12K_DBG_WMI, > + "get fw stat pdev id %d vdev id %d stats id 0x%x\n", > + param->pdev_id, param->vdev_id, param->stats_id); > + > + time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); > + > + if (!time_left) { > + ath12k_warn(ab, "time out while waiting for get fw stats\n"); > + return -ETIMEDOUT; > + } > + > + /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back > + * when stats data buffer limit is reached. fw_stats_complete > + * is completed once host receives first event from firmware, but > + * still end might not be marked in the TLV. > + * Below loop is to confirm that firmware completed sending all the event > + * and fw_stats_done is marked true when end is marked in the TLV. > + */ > + for (;;) { > + if (time_after(jiffies, timeout)) > + break; > + spin_lock_bh(&ar->data_lock); > + if (ar->fw_stats.fw_stats_done) { > + spin_unlock_bh(&ar->data_lock); > + break; > + } > + spin_unlock_bh(&ar->data_lock); > + } > + return 0; > +} > + > +static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw, > + struct ieee80211_vif *vif, > + unsigned int link_id, > + int *dbm) > +{ > + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); > + struct ath12k_fw_stats_req_params params = {}; > + struct ath12k_fw_stats_pdev *pdev; > + struct ath12k_hw *ah = hw->priv; > + struct ath12k_link_vif *arvif; > + struct ath12k_base *ab; > + struct ath12k *ar; > + 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. > + */ > + lockdep_assert_wiphy(hw->wiphy); > + > + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); > + if (!arvif || !arvif->ar) > + return -EINVAL; > + > + ar = arvif->ar; > + ab = ar->ab; > + if (ah->state != ATH12K_HW_STATE_ON) > + goto err_fallback; > + > + if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) > + return -EAGAIN; > + > + /* Limit the requests to Firmware for fetching the tx power */ > + if (ar->chan_tx_pwr != ATH12K_PDEV_TX_POWER_INVALID && > + time_before(jiffies, > + msecs_to_jiffies(ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS) + > + ar->last_tx_power_update)) > + goto send_tx_power; > + > + params.pdev_id = ar->pdev->pdev_id; > + params.vdev_id = arvif->vdev_id; > + params.stats_id = WMI_REQUEST_PDEV_STAT; > + ret = ath12k_mac_get_fw_stats(ar, ¶ms); > + if (ret) { > + ath12k_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 ath12k_fw_stats_pdev, list); > + if (!pdev) { > + spin_unlock_bh(&ar->data_lock); > + goto err_fallback; > + } > + > + /* tx power reported by firmware is in units of 0.5 dBm */ > + ar->chan_tx_pwr = pdev->chan_tx_power / 2; > + spin_unlock_bh(&ar->data_lock); > + ar->last_tx_power_update = jiffies; > + > +send_tx_power: > + *dbm = ar->chan_tx_pwr; > + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower fetched from firmware %d dBm\n", > + *dbm); > + return 0; > + > +err_fallback: > + /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */ > + *dbm = vif->bss_conf.txpower; > + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n", > + *dbm); > + return 0; > +} > + > static u8 > ath12k_mac_find_link_id_by_ar(struct ath12k_vif *ahvif, struct ath12k *ar) > { > @@ -7433,6 +7572,7 @@ static int ath12k_mac_start(struct ath12k *ar) > ar->num_created_vdevs = 0; > ar->num_peers = 0; > ar->allocated_vdev_map = 0; > + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; > > /* Configure monitor status ring with default rx_filter to get rx status > * such as rssi, rx_duration. > @@ -8638,6 +8778,7 @@ static int ath12k_mac_op_add_chanctx(struct ieee80211_hw *hw, > */ > ar->rx_channel = ctx->def.chan; > spin_unlock_bh(&ar->data_lock); > + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; > > return 0; > } > @@ -8666,6 +8807,7 @@ static void ath12k_mac_op_remove_chanctx(struct ieee80211_hw *hw, > */ > ar->rx_channel = NULL; > spin_unlock_bh(&ar->data_lock); > + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; > } > > static enum wmi_phy_mode > @@ -10109,68 +10251,6 @@ static int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx, > return 0; > } > > -int ath12k_mac_get_fw_stats(struct ath12k *ar, > - struct ath12k_fw_stats_req_params *param) > -{ > - struct ath12k_base *ab = ar->ab; > - struct ath12k_hw *ah = ath12k_ar_to_ah(ar); > - unsigned long timeout, time_left; > - int ret; > - > - guard(mutex)(&ah->hw_mutex); > - > - if (ah->state != ATH12K_HW_STATE_ON) > - return -ENETDOWN; > - > - /* FW stats can get split when exceeding the stats data buffer limit. > - * In that case, since there is no end marking for the back-to-back > - * received 'update stats' event, we keep a 3 seconds timeout in case, > - * fw_stats_done is not marked yet > - */ > - timeout = jiffies + msecs_to_jiffies(3 * 1000); > - ath12k_fw_stats_reset(ar); > - > - reinit_completion(&ar->fw_stats_complete); > - > - ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id, > - param->vdev_id, param->pdev_id); > - > - if (ret) { > - ath12k_warn(ab, "failed to request fw stats: %d\n", ret); > - return ret; > - } > - > - ath12k_dbg(ab, ATH12K_DBG_WMI, > - "get fw stat pdev id %d vdev id %d stats id 0x%x\n", > - param->pdev_id, param->vdev_id, param->stats_id); > - > - time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); > - > - if (!time_left) { > - ath12k_warn(ab, "time out while waiting for get fw stats\n"); > - return -ETIMEDOUT; > - } > - > - /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back > - * when stats data buffer limit is reached. fw_stats_complete > - * is completed once host receives first event from firmware, but > - * still end might not be marked in the TLV. > - * Below loop is to confirm that firmware completed sending all the event > - * and fw_stats_done is marked true when end is marked in the TLV. > - */ > - for (;;) { > - if (time_after(jiffies, timeout)) > - break; > - spin_lock_bh(&ar->data_lock); > - if (ar->fw_stats.fw_stats_done) { > - spin_unlock_bh(&ar->data_lock); > - break; > - } > - spin_unlock_bh(&ar->data_lock); > - } > - return 0; > -} > - > static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, > struct ieee80211_vif *vif, > struct ieee80211_sta *sta, > @@ -10463,6 +10543,7 @@ static const struct ieee80211_ops ath12k_ops = { > .assign_vif_chanctx = ath12k_mac_op_assign_vif_chanctx, > .unassign_vif_chanctx = ath12k_mac_op_unassign_vif_chanctx, > .switch_vif_chanctx = ath12k_mac_op_switch_vif_chanctx, > + .get_txpower = ath12k_mac_op_get_txpower, > .set_rts_threshold = ath12k_mac_op_set_rts_threshold, > .set_frag_threshold = ath12k_mac_op_set_frag_threshold, > .set_bitrate_mask = ath12k_mac_op_set_bitrate_mask, > @@ -11210,11 +11291,10 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) > goto err_unregister_hw; > } > > + ath12k_fw_stats_init(ar); > ath12k_debugfs_register(ar); > } > > - init_completion(&ar->fw_stats_complete); > - > return 0; > > err_unregister_hw: > diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h > index a0de0ddf175e..8435bdea904f 100644 > --- a/drivers/net/wireless/ath/ath12k/mac.h > +++ b/drivers/net/wireless/ath/ath12k/mac.h > @@ -33,6 +33,9 @@ struct ath12k_generic_iter { > #define ATH12K_KEEPALIVE_MAX_IDLE 3895 > #define ATH12K_KEEPALIVE_MAX_UNRESPONSIVE 3900 > > +#define ATH12K_PDEV_TX_POWER_INVALID ((u32)-1) > +#define ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS 5000 /* msecs */ > + > /* FIXME: should these be in ieee80211.h? */ > #define IEEE80211_VHT_MCS_SUPPORT_0_11_MASK GENMASK(23, 16) > #define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24) Series LGTM