Currently, command “iw wlan0 station dump” does not show per-chain signal strength. This is because ath12k does not handle the num_per_chain_rssi and rssi_avg_beacon reported by firmware to ath12k. To address this, update ath12k to send WMI_REQUEST_STATS_CMDID with the flag WMI_REQUEST_RSSI_PER_CHAIN_STAT to the firmware. Then, add logic to handle num_per_chain_rssi and rssi_avg_beacon in the ath12k_wmi_tlv_fw_stats_parse(), and assign the resulting per-chain signal strength to the chain_signal of struct station_info. After that, "iw dev xxx station dump" shows the correct per-chain signal strength. Such as: Station AA:BB:CC:DD:EE:FF (on wlan0) inactive time: 212 ms rx bytes: 10398 rx packets: 64 tx bytes: 4362 tx packets: 33 tx retries: 49 tx failed: 0 beacon loss: 0 beacon rx: 14 rx drop misc: 16 signal: -45 [-51, -46] dBm beacon signal avg: -44 dBm Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219751 Signed-off-by: Lingbo Kong <quic_lingbok@xxxxxxxxxxx> --- drivers/net/wireless/ath/ath12k/core.h | 5 ++ drivers/net/wireless/ath/ath12k/mac.c | 39 +++++++++++-- drivers/net/wireless/ath/ath12k/mac.h | 2 + drivers/net/wireless/ath/ath12k/wmi.c | 81 ++++++++++++++++++++++++++ drivers/net/wireless/ath/ath12k/wmi.h | 18 +++++- 5 files changed, 138 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 57e71ccbbb67..967e3cad3bca 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -508,6 +508,7 @@ struct ath12k_link_sta { u32 bw_prev; u32 peer_nss; s8 rssi_beacon; + s8 chain_signal[IEEE80211_MAX_CHAINS]; /* For now the assoc link will be considered primary */ bool is_assoc_link; @@ -530,6 +531,9 @@ struct ath12k_sta { enum ieee80211_sta_state state; }; +#define ATH12K_INVALID_RSSI_FULL -1 +#define ATH12K_INVALID_RSSI_EMPTY -128 + #define ATH12K_MIN_5G_FREQ 4150 #define ATH12K_MIN_6G_FREQ 5925 #define ATH12K_MAX_6G_FREQ 7115 @@ -738,6 +742,7 @@ struct ath12k { u32 mlo_setup_status; u8 ftm_msgref; struct ath12k_fw_stats fw_stats; + unsigned long last_signal_update; }; struct ath12k_hw { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 16e6f2fae943..2c72a83e6aaf 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10140,6 +10140,27 @@ static int ath12k_mac_get_fw_stats(struct ath12k *ar, u32 pdev_id, return ret; } +static void ath12k_mac_put_chain_rssi(struct station_info *sinfo, + struct ath12k_link_sta *arsta) +{ + s8 rssi; + int i; + + for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { + sinfo->chains &= ~BIT(i); + rssi = arsta->chain_signal[i]; + + if (rssi != ATH12K_DEFAULT_NOISE_FLOOR && + rssi != ATH12K_INVALID_RSSI_FULL && + rssi != ATH12K_INVALID_RSSI_EMPTY && + rssi != 0) { + sinfo->chain_signal[i] = rssi; + sinfo->chains |= BIT(i); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); + } + } +} + static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -10187,12 +10208,22 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, /* TODO: Use real NF instead of default one. */ signal = arsta->rssi_comb; - if (!signal && - ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA && - !(ath12k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0, - WMI_REQUEST_VDEV_STAT))) + /* Limit the requests to Firmware for fetching the signal strength */ + if (time_after(jiffies, msecs_to_jiffies(ATH12K_PDEV_SIGNAL_UPDATE_TIME_MSECS) + + ar->last_signal_update)) { + ath12k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0, WMI_REQUEST_VDEV_STAT); + ath12k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0, + WMI_REQUEST_RSSI_PER_CHAIN_STAT); + ar->last_signal_update = jiffies; + } + + if (!signal && ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA) signal = arsta->rssi_beacon; + if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) && + ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA) + ath12k_mac_put_chain_rssi(sinfo, arsta); + if (signal) { sinfo->signal = db2dbm ? signal : signal + ATH12K_DEFAULT_NOISE_FLOOR; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 5a6e3c3316be..df7e35886668 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -51,6 +51,8 @@ struct ath12k_generic_iter { #define ATH12K_DEFAULT_SCAN_LINK IEEE80211_MLD_MAX_NUM_LINKS #define ATH12K_NUM_MAX_LINKS (IEEE80211_MLD_MAX_NUM_LINKS + 1) +#define ATH12K_PDEV_SIGNAL_UPDATE_TIME_MSECS 2000 + enum ath12k_supported_bw { ATH12K_BW_20 = 0, ATH12K_BW_40 = 1, diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index f934d49acee6..b6df2bb3b88e 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -29,6 +29,9 @@ struct ath12k_wmi_svc_ready_parse { struct wmi_tlv_fw_stats_parse { const struct wmi_stats_event *ev; + const struct wmi_per_chain_rssi_stat_params *rssi; + int rssi_num; + bool chain_rssi_done; }; struct ath12k_wmi_dma_ring_caps_parse { @@ -177,6 +180,8 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { .min_len = sizeof(struct ath12k_wmi_p2p_noa_info) }, [WMI_TAG_P2P_NOA_EVENT] = { .min_len = sizeof(struct wmi_p2p_noa_event) }, + [WMI_TAG_PER_CHAIN_RSSI_STATS] = { + .min_len = sizeof(struct wmi_per_chain_rssi_stat_params) }, }; __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) @@ -7452,6 +7457,67 @@ static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, return ret; } +static int ath12k_wmi_tlv_rssi_chain_parse(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + const struct wmi_rssi_stat_params *stats_rssi = ptr; + struct wmi_tlv_fw_stats_parse *parse = data; + const struct wmi_stats_event *ev = parse->ev; + struct ath12k_link_vif *arvif; + struct ath12k_link_sta *arsta; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k *ar; + int pdev_id; + int vdev_id; + int j; + + if (tag != WMI_TAG_RSSI_STATS) + return -EPROTO; + + pdev_id = le32_to_cpu(ev->pdev_id); + vdev_id = le32_to_cpu(stats_rssi->vdev_id); + guard(rcu)(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id); + if (!ar) { + ath12k_warn(ab, "invalid pdev id %d in rssi chain parse\n", + pdev_id); + return -EPROTO; + } + + arvif = ath12k_mac_get_arvif(ar, vdev_id); + if (!arvif) { + ath12k_warn(ab, "not found vif for vdev id %d\n", vdev_id); + return -EPROTO; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "stats bssid %pM vif %p\n", + arvif->bssid, arvif->ahvif->vif); + + sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar), + arvif->bssid, + NULL); + if (!sta) { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "not found station of bssid %pM for rssi chain\n", + arvif->bssid); + return -EPROTO; + } + + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; + + BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > + ARRAY_SIZE(stats_rssi->rssi_avg_beacon)); + + for (j = 0; j < ARRAY_SIZE(arsta->chain_signal); j++) + arsta->chain_signal[j] = le32_to_cpu(stats_rssi->rssi_avg_beacon[j]); + + return 0; +} + static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *ptr, void *data) @@ -7466,6 +7532,21 @@ static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_base *ab, case WMI_TAG_ARRAY_BYTE: ret = ath12k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); break; + case WMI_TAG_PER_CHAIN_RSSI_STATS: + parse->rssi = ptr; + if (le32_to_cpu(parse->ev->stats_id) & WMI_REQUEST_RSSI_PER_CHAIN_STAT) + parse->rssi_num = le32_to_cpu(parse->rssi->num_per_chain_rssi); + break; + case WMI_TAG_ARRAY_STRUCT: + if (parse->rssi_num && !parse->chain_rssi_done) { + ret = ath12k_wmi_tlv_iter(ab, ptr, len, + ath12k_wmi_tlv_rssi_chain_parse, + parse); + if (ret) + return ret; + + parse->chain_rssi_done = true; + } default: break; } diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 1ba33e30ddd2..be03616e7c88 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -5680,9 +5680,10 @@ struct wmi_stats_event { } __packed; enum wmi_stats_id { - WMI_REQUEST_PDEV_STAT = BIT(2), - WMI_REQUEST_VDEV_STAT = BIT(3), - WMI_REQUEST_BCN_STAT = BIT(11), + WMI_REQUEST_PDEV_STAT = BIT(2), + WMI_REQUEST_VDEV_STAT = BIT(3), + WMI_REQUEST_RSSI_PER_CHAIN_STAT = BIT(8), + WMI_REQUEST_BCN_STAT = BIT(11), }; struct wmi_request_stats_cmd { @@ -5693,6 +5694,17 @@ struct wmi_request_stats_cmd { __le32 pdev_id; } __packed; +struct wmi_rssi_stat_params { + __le32 vdev_id; + __le32 rssi_avg_beacon[WMI_MAX_CHAINS]; + __le32 rssi_avg_data[WMI_MAX_CHAINS]; + struct ath12k_wmi_mac_addr_params peer_macaddr; +} __packed; + +struct wmi_per_chain_rssi_stat_params { + __le32 num_per_chain_rssi; +} __packed; + #define WLAN_MAX_AC 4 #define MAX_TX_RATE_VALUES 10 base-commit: d132a72d768bae74d10e6539fcf6522dfdbd1115 -- 2.34.1