diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/wmi.c')
-rw-r--r-- | drivers/net/wireless/ath/ath11k/wmi.c | 179 |
1 files changed, 146 insertions, 33 deletions
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 2b4d27d807ab..b7bdc37f24fd 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -73,6 +73,14 @@ struct wmi_tlv_dma_buf_release_parse { bool meta_data_done; }; +struct wmi_tlv_fw_stats_parse { + const struct wmi_stats_event *ev; + const struct wmi_per_chain_rssi_stats *rssi; + struct ath11k_fw_stats *stats; + int rssi_num; + bool chain_rssi_done; +}; + static const struct wmi_tlv_policy wmi_tlv_policies[] = { [WMI_TAG_ARRAY_BYTE] = { .min_len = 0 }, @@ -132,6 +140,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { .min_len = sizeof(struct wmi_obss_color_collision_event) }, [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { .min_len = sizeof(struct wmi_11d_new_cc_ev) }, + [WMI_TAG_PER_CHAIN_RSSI_STATS] = { + .min_len = sizeof(struct wmi_per_chain_rssi_stats) }, }; #define PRIMAP(_hw_mode_) \ @@ -5554,37 +5564,89 @@ ath11k_wmi_pull_bcn_stats(const struct wmi_bcn_stats *src, dst->tx_bcn_outage_cnt = src->tx_bcn_outage_cnt; } -int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, - struct ath11k_fw_stats *stats) +static int ath11k_wmi_tlv_rssi_chain_parse(struct ath11k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) { - const void **tb; - const struct wmi_stats_event *ev; - const void *data; - int i, ret; - u32 len = skb->len; + struct wmi_tlv_fw_stats_parse *parse = data; + const struct wmi_stats_event *ev = parse->ev; + struct ath11k_fw_stats *stats = parse->stats; + struct ath11k *ar; + struct ath11k_vif *arvif; + struct ieee80211_sta *sta; + struct ath11k_sta *arsta; + const struct wmi_rssi_stats *stats_rssi = (const struct wmi_rssi_stats *)ptr; + int j, ret = 0; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC); - if (IS_ERR(tb)) { - ret = PTR_ERR(tb); - ath11k_warn(ab, "failed to parse tlv: %d\n", ret); - return ret; + if (tag != WMI_TAG_RSSI_STATS) + return -EPROTO; + + rcu_read_lock(); + + ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); + stats->stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats vdev id %d mac %pM\n", + stats_rssi->vdev_id, stats_rssi->peer_macaddr.addr); + + arvif = ath11k_mac_get_arvif(ar, stats_rssi->vdev_id); + if (!arvif) { + ath11k_warn(ab, "not found vif for vdev id %d\n", + stats_rssi->vdev_id); + ret = -EPROTO; + goto exit; + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats bssid %pM vif %pK\n", + arvif->bssid, arvif->vif); + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, + arvif->bssid, + NULL); + if (!sta) { + ath11k_warn(ab, "not found station for bssid %pM\n", + arvif->bssid); + ret = -EPROTO; + goto exit; + } + + arsta = (struct ath11k_sta *)sta->drv_priv; + + 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] = stats_rssi->rssi_avg_beacon[j]; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats beacon rssi[%d] %d data rssi[%d] %d\n", + j, + stats_rssi->rssi_avg_beacon[j], + j, + stats_rssi->rssi_avg_data[j]); } - ev = tb[WMI_TAG_STATS_EVENT]; - data = tb[WMI_TAG_ARRAY_BYTE]; - if (!ev || !data) { +exit: + rcu_read_unlock(); + return ret; +} + +static int ath11k_wmi_tlv_fw_stats_data_parse(struct ath11k_base *ab, + struct wmi_tlv_fw_stats_parse *parse, + const void *ptr, + u16 len) +{ + struct ath11k_fw_stats *stats = parse->stats; + const struct wmi_stats_event *ev = parse->ev; + int i; + const void *data = ptr; + + if (!ev) { ath11k_warn(ab, "failed to fetch update stats ev"); - kfree(tb); return -EPROTO; } - ath11k_dbg(ab, ATH11K_DBG_WMI, - "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n", - ev->pdev_id, - ev->num_pdev_stats, ev->num_vdev_stats, - ev->num_bcn_stats); - - stats->pdev_id = ev->pdev_id; stats->stats_id = 0; for (i = 0; i < ev->num_pdev_stats; i++) { @@ -5592,10 +5654,8 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, struct ath11k_fw_stats_pdev *dst; src = data; - if (len < sizeof(*src)) { - kfree(tb); + if (len < sizeof(*src)) return -EPROTO; - } stats->stats_id = WMI_REQUEST_PDEV_STAT; @@ -5617,10 +5677,8 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, struct ath11k_fw_stats_vdev *dst; src = data; - if (len < sizeof(*src)) { - kfree(tb); + if (len < sizeof(*src)) return -EPROTO; - } stats->stats_id = WMI_REQUEST_VDEV_STAT; @@ -5640,10 +5698,8 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, struct ath11k_fw_stats_bcn *dst; src = data; - if (len < sizeof(*src)) { - kfree(tb); + if (len < sizeof(*src)) return -EPROTO; - } stats->stats_id = WMI_REQUEST_BCN_STAT; @@ -5658,10 +5714,67 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, list_add_tail(&dst->list, &stats->bcn); } - kfree(tb); return 0; } +static int ath11k_wmi_tlv_fw_stats_parse(struct ath11k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_fw_stats_parse *parse = data; + int ret = 0; + + switch (tag) { + case WMI_TAG_STATS_EVENT: + parse->ev = (struct wmi_stats_event *)ptr; + parse->stats->pdev_id = parse->ev->pdev_id; + break; + case WMI_TAG_ARRAY_BYTE: + ret = ath11k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); + break; + case WMI_TAG_PER_CHAIN_RSSI_STATS: + parse->rssi = (struct wmi_per_chain_rssi_stats *)ptr; + + if (parse->ev->stats_id & WMI_REQUEST_RSSI_PER_CHAIN_STAT) + parse->rssi_num = parse->rssi->num_per_chain_rssi_stats; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi stats id 0x%x num chain %d\n", + parse->ev->stats_id, + parse->rssi_num); + break; + case WMI_TAG_ARRAY_STRUCT: + if (parse->rssi_num && !parse->chain_rssi_done) { + ret = ath11k_wmi_tlv_iter(ab, ptr, len, + ath11k_wmi_tlv_rssi_chain_parse, + parse); + if (ret) { + ath11k_warn(ab, "failed to parse rssi chain %d\n", + ret); + return ret; + } + parse->chain_rssi_done = true; + } + break; + default: + break; + } + return ret; +} + +int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, + struct ath11k_fw_stats *stats) +{ + struct wmi_tlv_fw_stats_parse parse = { }; + + stats->stats_id = 0; + parse.stats = stats; + + return ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_fw_stats_parse, + &parse); +} + size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head) { struct ath11k_fw_stats_vdev *i; |