ath10k: add bitrate parse for peer stats info
authorWen Gong <wgong@codeaurora.org>
Mon, 27 Apr 2020 08:04:15 +0000 (16:04 +0800)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 28 Apr 2020 09:12:34 +0000 (12:12 +0300)
The rate code and rate kbps report by WMI_TLV_PEER_STATS_INFO_EVENTID
from firmware contains all the bitrate info which include OFDM, CCK,
HT/VHT, and mac80211 need the struct rate_info which include below
parameters:

flags: bitflag of flags from &enum rate_info_flags
mcs: mcs index if struct describes an HT/VHT/HE rate
legacy: bitrate in 100kbit/s for 802.11abg
nss: number of streams (VHT & HE only)
bw: bandwidth (from &enum rate_info_bw)

For OFDM/CCK, its rate kbps indicate the bitrate, for HT/VHT, mac80211
need the above 5 parameters to cacluate the bitrate and show by iw.

After parse the bitrate info, iw show the correct rx bitrate:

localhost ~ # iw wlan0 link
rx bitrate: 234.0 MBit/s VHT-MCS 3 80MHz VHT-NSS 2
rx bitrate: 40.5 MBit/s MCS 2 40MHz
rx bitrate: 72.2 MBit/s MCS 7 short GI
rx bitrate: 54.0 MBit/s
rx bitrate: 48.0 MBit/s

Tested with QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00042.

Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200427080416.8265-4-wgong@codeaurora.org
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/wmi-tlv.h

index 11d9132be4fd777d8cdda5e9f57a7bfa7c1a7761..1700bf59e8fa557d7edba6a53f9ecead64bd66fd 100644 (file)
@@ -149,6 +149,26 @@ static inline u32 host_interest_item_address(u32 item_offset)
        return QCA988X_HOST_INTEREST_ADDRESS + item_offset;
 }
 
+enum ath10k_phy_mode {
+       ATH10K_PHY_MODE_LEGACY = 0,
+       ATH10K_PHY_MODE_HT = 1,
+       ATH10K_PHY_MODE_VHT = 2,
+};
+
+/* Data rate 100KBPS based on IE Index */
+struct ath10k_index_ht_data_rate_type {
+       u8   beacon_rate_index;
+       u16  supported_rate[4];
+};
+
+/* Data rate 100KBPS based on IE Index */
+struct ath10k_index_vht_data_rate_type {
+       u8   beacon_rate_index;
+       u16  supported_VHT80_rate[2];
+       u16  supported_VHT40_rate[2];
+       u16  supported_VHT20_rate[2];
+};
+
 struct ath10k_bmi {
        bool done_sent;
 };
index d0401d3adde4a44b6304ac8e25e7f9d498268aae..38fc8cb3aac9f6452f07eadf0f4690276580acf6 100644 (file)
@@ -8305,6 +8305,157 @@ static void ath10k_mac_op_sta_pre_rcu_remove(struct ieee80211_hw *hw,
                        peer->removed = true;
 }
 
+/* HT MCS parameters with Nss = 1 */
+static const struct ath10k_index_ht_data_rate_type supported_ht_mcs_rate_nss1[] = {
+       /* MCS  L20   L40   S20  S40 */
+       {0,  { 65,  135,  72,  150} },
+       {1,  { 130, 270,  144, 300} },
+       {2,  { 195, 405,  217, 450} },
+       {3,  { 260, 540,  289, 600} },
+       {4,  { 390, 810,  433, 900} },
+       {5,  { 520, 1080, 578, 1200} },
+       {6,  { 585, 1215, 650, 1350} },
+       {7,  { 650, 1350, 722, 1500} }
+};
+
+/* HT MCS parameters with Nss = 2 */
+static const struct ath10k_index_ht_data_rate_type supported_ht_mcs_rate_nss2[] = {
+       /* MCS  L20    L40   S20   S40 */
+       {0,  {130,  270,  144,  300} },
+       {1,  {260,  540,  289,  600} },
+       {2,  {390,  810,  433,  900} },
+       {3,  {520,  1080, 578,  1200} },
+       {4,  {780,  1620, 867,  1800} },
+       {5,  {1040, 2160, 1156, 2400} },
+       {6,  {1170, 2430, 1300, 2700} },
+       {7,  {1300, 2700, 1444, 3000} }
+};
+
+/* MCS parameters with Nss = 1 */
+static const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = {
+       /* MCS  L80    S80     L40   S40    L20   S20 */
+       {0,  {293,  325},  {135,  150},  {65,   72} },
+       {1,  {585,  650},  {270,  300},  {130,  144} },
+       {2,  {878,  975},  {405,  450},  {195,  217} },
+       {3,  {1170, 1300}, {540,  600},  {260,  289} },
+       {4,  {1755, 1950}, {810,  900},  {390,  433} },
+       {5,  {2340, 2600}, {1080, 1200}, {520,  578} },
+       {6,  {2633, 2925}, {1215, 1350}, {585,  650} },
+       {7,  {2925, 3250}, {1350, 1500}, {650,  722} },
+       {8,  {3510, 3900}, {1620, 1800}, {780,  867} },
+       {9,  {3900, 4333}, {1800, 2000}, {780,  867} }
+};
+
+/*MCS parameters with Nss = 2 */
+static const struct ath10k_index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = {
+       /* MCS  L80    S80     L40   S40    L20   S20 */
+       {0,  {585,  650},  {270,  300},  {130,  144} },
+       {1,  {1170, 1300}, {540,  600},  {260,  289} },
+       {2,  {1755, 1950}, {810,  900},  {390,  433} },
+       {3,  {2340, 2600}, {1080, 1200}, {520,  578} },
+       {4,  {3510, 3900}, {1620, 1800}, {780,  867} },
+       {5,  {4680, 5200}, {2160, 2400}, {1040, 1156} },
+       {6,  {5265, 5850}, {2430, 2700}, {1170, 1300} },
+       {7,  {5850, 6500}, {2700, 3000}, {1300, 1444} },
+       {8,  {7020, 7800}, {3240, 3600}, {1560, 1733} },
+       {9,  {7800, 8667}, {3600, 4000}, {1560, 1733} }
+};
+
+static void ath10k_mac_get_rate_flags_ht(struct ath10k *ar, u32 rate, u8 nss, u8 mcs,
+                                        u8 *flags, u8 *bw)
+{
+       struct ath10k_index_ht_data_rate_type *mcs_rate;
+
+       mcs_rate = (struct ath10k_index_ht_data_rate_type *)
+                  ((nss == 1) ? &supported_ht_mcs_rate_nss1 :
+                  &supported_ht_mcs_rate_nss2);
+
+       if (rate == mcs_rate[mcs].supported_rate[0]) {
+               *bw = RATE_INFO_BW_20;
+       } else if (rate == mcs_rate[mcs].supported_rate[1]) {
+               *bw |= RATE_INFO_BW_40;
+       } else if (rate == mcs_rate[mcs].supported_rate[2]) {
+               *bw |= RATE_INFO_BW_20;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else if (rate == mcs_rate[mcs].supported_rate[3]) {
+               *bw |= RATE_INFO_BW_40;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else {
+               ath10k_warn(ar, "invalid ht params rate %d 100kbps nss %d mcs %d",
+                           rate, nss, mcs);
+       }
+}
+
+static void ath10k_mac_get_rate_flags_vht(struct ath10k *ar, u32 rate, u8 nss, u8 mcs,
+                                         u8 *flags, u8 *bw)
+{
+       struct ath10k_index_vht_data_rate_type *mcs_rate;
+
+       mcs_rate = (struct ath10k_index_vht_data_rate_type *)
+                  ((nss == 1) ? &supported_vht_mcs_rate_nss1 :
+                  &supported_vht_mcs_rate_nss2);
+
+       if (rate == mcs_rate[mcs].supported_VHT80_rate[0]) {
+               *bw = RATE_INFO_BW_80;
+       } else if (rate == mcs_rate[mcs].supported_VHT80_rate[1]) {
+               *bw = RATE_INFO_BW_80;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else if (rate == mcs_rate[mcs].supported_VHT40_rate[0]) {
+               *bw = RATE_INFO_BW_40;
+       } else if (rate == mcs_rate[mcs].supported_VHT40_rate[1]) {
+               *bw = RATE_INFO_BW_40;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else if (rate == mcs_rate[mcs].supported_VHT20_rate[0]) {
+               *bw = RATE_INFO_BW_20;
+       } else if (rate == mcs_rate[mcs].supported_VHT20_rate[1]) {
+               *bw = RATE_INFO_BW_20;
+               *flags |= RATE_INFO_FLAGS_SHORT_GI;
+       } else {
+               ath10k_warn(ar, "invalid vht params rate %d 100kbps nss %d mcs %d",
+                           rate, nss, mcs);
+       }
+}
+
+static void ath10k_mac_get_rate_flags(struct ath10k *ar, u32 rate,
+                                     enum ath10k_phy_mode mode, u8 nss, u8 mcs,
+                                     u8 *flags, u8 *bw)
+{
+       if (mode == ATH10K_PHY_MODE_HT) {
+               *flags = RATE_INFO_FLAGS_MCS;
+               ath10k_mac_get_rate_flags_ht(ar, rate, nss, mcs, flags, bw);
+       } else if (mode == ATH10K_PHY_MODE_VHT) {
+               *flags = RATE_INFO_FLAGS_VHT_MCS;
+               ath10k_mac_get_rate_flags_vht(ar, rate, nss, mcs, flags, bw);
+       }
+}
+
+static void ath10k_mac_parse_bitrate(struct ath10k *ar, u32 rate_code,
+                                    u32 bitrate_kbps, struct rate_info *rate)
+{
+       enum ath10k_phy_mode mode = ATH10K_PHY_MODE_LEGACY;
+       enum wmi_rate_preamble preamble = WMI_TLV_GET_HW_RC_PREAM_V1(rate_code);
+       u8 nss = WMI_TLV_GET_HW_RC_NSS_V1(rate_code) + 1;
+       u8 mcs = WMI_TLV_GET_HW_RC_RATE_V1(rate_code);
+       u8 flags = 0, bw = 0;
+
+       if (preamble == WMI_RATE_PREAMBLE_HT)
+               mode = ATH10K_PHY_MODE_HT;
+       else if (preamble == WMI_RATE_PREAMBLE_VHT)
+               mode = ATH10K_PHY_MODE_VHT;
+
+       ath10k_mac_get_rate_flags(ar, bitrate_kbps / 100, mode, nss, mcs, &flags, &bw);
+
+       ath10k_dbg(ar, ATH10K_DBG_MAC,
+                  "mac parse bitrate preamble %d mode %d nss %d mcs %d flags %x bw %d\n",
+                  preamble, mode, nss, mcs, flags, bw);
+
+       rate->flags = flags;
+       rate->bw = bw;
+       rate->legacy = bitrate_kbps / 100;
+       rate->nss = nss;
+       rate->mcs = mcs;
+}
+
 static void ath10k_mac_sta_get_peer_stats_info(struct ath10k *ar,
                                               struct ieee80211_sta *sta,
                                               struct station_info *sinfo)
@@ -8341,6 +8492,16 @@ static void ath10k_mac_sta_get_peer_stats_info(struct ath10k *ar,
                ath10k_warn(ar, "timed out waiting peer stats info\n");
                return;
        }
+
+       if (arsta->rx_rate_code != 0 && arsta->rx_bitrate_kbps != 0) {
+               ath10k_mac_parse_bitrate(ar, arsta->rx_rate_code,
+                                        arsta->rx_bitrate_kbps,
+                                        &sinfo->rxrate);
+
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+               arsta->rx_rate_code = 0;
+               arsta->rx_bitrate_kbps = 0;
+       }
 }
 
 static void ath10k_sta_statistics(struct ieee80211_hw *hw,
index 2153e2d9a95525a62d7b61a05d44d7e4f1639843..6e0537dabd1df3530c1433c82f369b5df032fd08 100644 (file)
@@ -2174,6 +2174,15 @@ struct wmi_tlv_peer_stats_info {
        __le32 peer_rssi_per_chain[WMI_TLV_MAX_CHAINS];
 } __packed;
 
+#define HW_RATECODE_PREAM_V1_MASK GENMASK(10, 8)
+#define WMI_TLV_GET_HW_RC_PREAM_V1(rc) FIELD_GET(HW_RATECODE_PREAM_V1_MASK, rc)
+
+#define HW_RATECODE_NSS_V1_MASK GENMASK(7, 5)
+#define WMI_TLV_GET_HW_RC_NSS_V1(rc) FIELD_GET(HW_RATECODE_NSS_V1_MASK, rc)
+
+#define HW_RATECODE_RATE_V1_MASK GENMASK(4, 0)
+#define WMI_TLV_GET_HW_RC_RATE_V1(rc) FIELD_GET(HW_RATECODE_RATE_V1_MASK, rc)
+
 struct wmi_tlv_p2p_noa_ev {
        __le32 vdev_id;
 } __packed;