diff options
author | Anilkumar Kolli <akolli@qti.qualcomm.com> | 2016-11-23 09:58:10 -0500 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2016-11-23 14:40:02 -0500 |
commit | cec17c382140a17ad54853a4b57a84588f090806 (patch) | |
tree | e55f453a49817441976d84c080f0eea453c0d62e /drivers/net/wireless/ath/ath10k | |
parent | 3fea18d079e2f1f50bc2a37db826c9e6e8a4003e (diff) |
ath10k: add per peer htt tx stats support for 10.4
Per peer tx stats are part of 'HTT_10_4_T2H_MSG_TYPE_PEER_STATS'
event, Firmware sends one HTT event for every four PPDUs.
HTT payload has success pkts/bytes, failed pkts/bytes, retry
pkts/bytes and rate info per ppdu.
Peer stats are enabled through 'WMI_SERVICE_PEER_STATS',
which are nowadays enabled by default.
Parse peer stats and update the tx rate information per STA.
tx rate, Peer stats are tested on QCA4019 with Firmware version
10.4-3.2.1-00028.
Signed-off-by: Anilkumar Kolli <akolli@qti.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath10k')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 17 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt.h | 25 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_rx.c | 125 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.h | 10 |
5 files changed, 178 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 018fc22d6056..6f2608a501e2 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h | |||
@@ -337,6 +337,7 @@ struct ath10k_sta { | |||
337 | u32 nss; | 337 | u32 nss; |
338 | u32 smps; | 338 | u32 smps; |
339 | u16 peer_id; | 339 | u16 peer_id; |
340 | struct rate_info txrate; | ||
340 | 341 | ||
341 | struct work_struct update_wk; | 342 | struct work_struct update_wk; |
342 | 343 | ||
@@ -692,6 +693,21 @@ struct ath10k_fw_components { | |||
692 | struct ath10k_fw_file fw_file; | 693 | struct ath10k_fw_file fw_file; |
693 | }; | 694 | }; |
694 | 695 | ||
696 | struct ath10k_per_peer_tx_stats { | ||
697 | u32 succ_bytes; | ||
698 | u32 retry_bytes; | ||
699 | u32 failed_bytes; | ||
700 | u8 ratecode; | ||
701 | u8 flags; | ||
702 | u16 peer_id; | ||
703 | u16 succ_pkts; | ||
704 | u16 retry_pkts; | ||
705 | u16 failed_pkts; | ||
706 | u16 duration; | ||
707 | u32 reserved1; | ||
708 | u32 reserved2; | ||
709 | }; | ||
710 | |||
695 | struct ath10k { | 711 | struct ath10k { |
696 | struct ath_common ath_common; | 712 | struct ath_common ath_common; |
697 | struct ieee80211_hw *hw; | 713 | struct ieee80211_hw *hw; |
@@ -905,6 +921,7 @@ struct ath10k { | |||
905 | 921 | ||
906 | struct ath10k_thermal thermal; | 922 | struct ath10k_thermal thermal; |
907 | struct ath10k_wow wow; | 923 | struct ath10k_wow wow; |
924 | struct ath10k_per_peer_tx_stats peer_tx_stats; | ||
908 | 925 | ||
909 | /* NAPI */ | 926 | /* NAPI */ |
910 | struct net_device napi_dev; | 927 | struct net_device napi_dev; |
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 130cd9502021..cd160b16db1e 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c | |||
@@ -137,6 +137,8 @@ static const enum htt_t2h_msg_type htt_10_4_t2h_msg_types[] = { | |||
137 | HTT_T2H_MSG_TYPE_STATS_NOUPLOAD, | 137 | HTT_T2H_MSG_TYPE_STATS_NOUPLOAD, |
138 | [HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND] = | 138 | [HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND] = |
139 | HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, | 139 | HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, |
140 | [HTT_10_4_T2H_MSG_TYPE_PEER_STATS] = | ||
141 | HTT_T2H_MSG_TYPE_PEER_STATS, | ||
140 | }; | 142 | }; |
141 | 143 | ||
142 | int ath10k_htt_connect(struct ath10k_htt *htt) | 144 | int ath10k_htt_connect(struct ath10k_htt *htt) |
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 0d2ed09f202b..164eb3a10566 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h | |||
@@ -419,6 +419,7 @@ enum htt_10_4_t2h_msg_type { | |||
419 | HTT_10_4_T2H_MSG_TYPE_STATS_NOUPLOAD = 0x18, | 419 | HTT_10_4_T2H_MSG_TYPE_STATS_NOUPLOAD = 0x18, |
420 | /* 0x19 to 0x2f are reserved */ | 420 | /* 0x19 to 0x2f are reserved */ |
421 | HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND = 0x30, | 421 | HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND = 0x30, |
422 | HTT_10_4_T2H_MSG_TYPE_PEER_STATS = 0x31, | ||
422 | /* keep this last */ | 423 | /* keep this last */ |
423 | HTT_10_4_T2H_NUM_MSGS | 424 | HTT_10_4_T2H_NUM_MSGS |
424 | }; | 425 | }; |
@@ -453,6 +454,7 @@ enum htt_t2h_msg_type { | |||
453 | HTT_T2H_MSG_TYPE_TX_FETCH_IND, | 454 | HTT_T2H_MSG_TYPE_TX_FETCH_IND, |
454 | HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM, | 455 | HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM, |
455 | HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, | 456 | HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, |
457 | HTT_T2H_MSG_TYPE_PEER_STATS, | ||
456 | /* keep this last */ | 458 | /* keep this last */ |
457 | HTT_T2H_NUM_MSGS | 459 | HTT_T2H_NUM_MSGS |
458 | }; | 460 | }; |
@@ -1470,6 +1472,28 @@ struct htt_channel_change { | |||
1470 | __le32 phymode; | 1472 | __le32 phymode; |
1471 | } __packed; | 1473 | } __packed; |
1472 | 1474 | ||
1475 | struct htt_per_peer_tx_stats_ind { | ||
1476 | __le32 succ_bytes; | ||
1477 | __le32 retry_bytes; | ||
1478 | __le32 failed_bytes; | ||
1479 | u8 ratecode; | ||
1480 | u8 flags; | ||
1481 | __le16 peer_id; | ||
1482 | __le16 succ_pkts; | ||
1483 | __le16 retry_pkts; | ||
1484 | __le16 failed_pkts; | ||
1485 | __le16 tx_duration; | ||
1486 | __le32 reserved1; | ||
1487 | __le32 reserved2; | ||
1488 | } __packed; | ||
1489 | |||
1490 | struct htt_peer_tx_stats { | ||
1491 | u8 num_ppdu; | ||
1492 | u8 ppdu_len; | ||
1493 | u8 version; | ||
1494 | u8 payload[0]; | ||
1495 | } __packed; | ||
1496 | |||
1473 | union htt_rx_pn_t { | 1497 | union htt_rx_pn_t { |
1474 | /* WEP: 24-bit PN */ | 1498 | /* WEP: 24-bit PN */ |
1475 | u32 pn24; | 1499 | u32 pn24; |
@@ -1521,6 +1545,7 @@ struct htt_resp { | |||
1521 | struct htt_tx_fetch_confirm tx_fetch_confirm; | 1545 | struct htt_tx_fetch_confirm tx_fetch_confirm; |
1522 | struct htt_tx_mode_switch_ind tx_mode_switch_ind; | 1546 | struct htt_tx_mode_switch_ind tx_mode_switch_ind; |
1523 | struct htt_channel_change chan_change; | 1547 | struct htt_channel_change chan_change; |
1548 | struct htt_peer_tx_stats peer_tx_stats; | ||
1524 | }; | 1549 | }; |
1525 | } __packed; | 1550 | } __packed; |
1526 | 1551 | ||
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 285b235268d7..86d082cf4eef 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c | |||
@@ -2194,6 +2194,128 @@ void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) | |||
2194 | dev_kfree_skb_any(skb); | 2194 | dev_kfree_skb_any(skb); |
2195 | } | 2195 | } |
2196 | 2196 | ||
2197 | static inline bool is_valid_legacy_rate(u8 rate) | ||
2198 | { | ||
2199 | static const u8 legacy_rates[] = {1, 2, 5, 11, 6, 9, 12, | ||
2200 | 18, 24, 36, 48, 54}; | ||
2201 | int i; | ||
2202 | |||
2203 | for (i = 0; i < ARRAY_SIZE(legacy_rates); i++) { | ||
2204 | if (rate == legacy_rates[i]) | ||
2205 | return true; | ||
2206 | } | ||
2207 | |||
2208 | return false; | ||
2209 | } | ||
2210 | |||
2211 | static void | ||
2212 | ath10k_update_per_peer_tx_stats(struct ath10k *ar, | ||
2213 | struct ieee80211_sta *sta, | ||
2214 | struct ath10k_per_peer_tx_stats *peer_stats) | ||
2215 | { | ||
2216 | struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; | ||
2217 | u8 rate = 0, sgi; | ||
2218 | struct rate_info txrate; | ||
2219 | |||
2220 | lockdep_assert_held(&ar->data_lock); | ||
2221 | |||
2222 | txrate.flags = ATH10K_HW_PREAMBLE(peer_stats->ratecode); | ||
2223 | txrate.bw = ATH10K_HW_BW(peer_stats->flags); | ||
2224 | txrate.nss = ATH10K_HW_NSS(peer_stats->ratecode); | ||
2225 | txrate.mcs = ATH10K_HW_MCS_RATE(peer_stats->ratecode); | ||
2226 | sgi = ATH10K_HW_GI(peer_stats->flags); | ||
2227 | |||
2228 | if (((txrate.flags == WMI_RATE_PREAMBLE_HT) || | ||
2229 | (txrate.flags == WMI_RATE_PREAMBLE_VHT)) && txrate.mcs > 9) { | ||
2230 | ath10k_warn(ar, "Invalid mcs %hhd peer stats", txrate.mcs); | ||
2231 | return; | ||
2232 | } | ||
2233 | |||
2234 | if (txrate.flags == WMI_RATE_PREAMBLE_CCK || | ||
2235 | txrate.flags == WMI_RATE_PREAMBLE_OFDM) { | ||
2236 | rate = ATH10K_HW_LEGACY_RATE(peer_stats->ratecode); | ||
2237 | |||
2238 | if (!is_valid_legacy_rate(rate)) { | ||
2239 | ath10k_warn(ar, "Invalid legacy rate %hhd peer stats", | ||
2240 | rate); | ||
2241 | return; | ||
2242 | } | ||
2243 | |||
2244 | /* This is hacky, FW sends CCK rate 5.5Mbps as 6 */ | ||
2245 | rate *= 10; | ||
2246 | if (rate == 60 && txrate.flags == WMI_RATE_PREAMBLE_CCK) | ||
2247 | rate = rate - 5; | ||
2248 | arsta->txrate.legacy = rate * 10; | ||
2249 | } else if (txrate.flags == WMI_RATE_PREAMBLE_HT) { | ||
2250 | arsta->txrate.flags = RATE_INFO_FLAGS_MCS; | ||
2251 | arsta->txrate.mcs = txrate.mcs; | ||
2252 | } else { | ||
2253 | arsta->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; | ||
2254 | arsta->txrate.mcs = txrate.mcs; | ||
2255 | } | ||
2256 | |||
2257 | if (sgi) | ||
2258 | arsta->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; | ||
2259 | |||
2260 | arsta->txrate.nss = txrate.nss; | ||
2261 | arsta->txrate.bw = txrate.bw + RATE_INFO_BW_20; | ||
2262 | } | ||
2263 | |||
2264 | static void ath10k_htt_fetch_peer_stats(struct ath10k *ar, | ||
2265 | struct sk_buff *skb) | ||
2266 | { | ||
2267 | struct htt_resp *resp = (struct htt_resp *)skb->data; | ||
2268 | struct ath10k_per_peer_tx_stats *p_tx_stats = &ar->peer_tx_stats; | ||
2269 | struct htt_per_peer_tx_stats_ind *tx_stats; | ||
2270 | struct ieee80211_sta *sta; | ||
2271 | struct ath10k_peer *peer; | ||
2272 | int peer_id, i; | ||
2273 | u8 ppdu_len, num_ppdu; | ||
2274 | |||
2275 | num_ppdu = resp->peer_tx_stats.num_ppdu; | ||
2276 | ppdu_len = resp->peer_tx_stats.ppdu_len * sizeof(__le32); | ||
2277 | |||
2278 | if (skb->len < sizeof(struct htt_resp_hdr) + num_ppdu * ppdu_len) { | ||
2279 | ath10k_warn(ar, "Invalid peer stats buf length %d\n", skb->len); | ||
2280 | return; | ||
2281 | } | ||
2282 | |||
2283 | tx_stats = (struct htt_per_peer_tx_stats_ind *) | ||
2284 | (resp->peer_tx_stats.payload); | ||
2285 | peer_id = __le16_to_cpu(tx_stats->peer_id); | ||
2286 | |||
2287 | rcu_read_lock(); | ||
2288 | spin_lock_bh(&ar->data_lock); | ||
2289 | peer = ath10k_peer_find_by_id(ar, peer_id); | ||
2290 | if (!peer) { | ||
2291 | ath10k_warn(ar, "Invalid peer id %d peer stats buffer\n", | ||
2292 | peer_id); | ||
2293 | goto out; | ||
2294 | } | ||
2295 | |||
2296 | sta = peer->sta; | ||
2297 | for (i = 0; i < num_ppdu; i++) { | ||
2298 | tx_stats = (struct htt_per_peer_tx_stats_ind *) | ||
2299 | (resp->peer_tx_stats.payload + i * ppdu_len); | ||
2300 | |||
2301 | p_tx_stats->succ_bytes = __le32_to_cpu(tx_stats->succ_bytes); | ||
2302 | p_tx_stats->retry_bytes = __le32_to_cpu(tx_stats->retry_bytes); | ||
2303 | p_tx_stats->failed_bytes = | ||
2304 | __le32_to_cpu(tx_stats->failed_bytes); | ||
2305 | p_tx_stats->ratecode = tx_stats->ratecode; | ||
2306 | p_tx_stats->flags = tx_stats->flags; | ||
2307 | p_tx_stats->succ_pkts = __le16_to_cpu(tx_stats->succ_pkts); | ||
2308 | p_tx_stats->retry_pkts = __le16_to_cpu(tx_stats->retry_pkts); | ||
2309 | p_tx_stats->failed_pkts = __le16_to_cpu(tx_stats->failed_pkts); | ||
2310 | |||
2311 | ath10k_update_per_peer_tx_stats(ar, sta, p_tx_stats); | ||
2312 | } | ||
2313 | |||
2314 | out: | ||
2315 | spin_unlock_bh(&ar->data_lock); | ||
2316 | rcu_read_unlock(); | ||
2317 | } | ||
2318 | |||
2197 | bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) | 2319 | bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) |
2198 | { | 2320 | { |
2199 | struct ath10k_htt *htt = &ar->htt; | 2321 | struct ath10k_htt *htt = &ar->htt; |
@@ -2354,6 +2476,9 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) | |||
2354 | case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND: | 2476 | case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND: |
2355 | ath10k_htt_rx_tx_mode_switch_ind(ar, skb); | 2477 | ath10k_htt_rx_tx_mode_switch_ind(ar, skb); |
2356 | break; | 2478 | break; |
2479 | case HTT_T2H_MSG_TYPE_PEER_STATS: | ||
2480 | ath10k_htt_fetch_peer_stats(ar, skb); | ||
2481 | break; | ||
2357 | case HTT_T2H_MSG_TYPE_EN_STATS: | 2482 | case HTT_T2H_MSG_TYPE_EN_STATS: |
2358 | default: | 2483 | default: |
2359 | ath10k_warn(ar, "htt event (%d) not handled\n", | 2484 | ath10k_warn(ar, "htt event (%d) not handled\n", |
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 3a73b5cc1551..5d3dff95b2e5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h | |||
@@ -4603,9 +4603,17 @@ enum wmi_rate_preamble { | |||
4603 | 4603 | ||
4604 | #define ATH10K_HW_NSS(rate) (1 + (((rate) >> 4) & 0x3)) | 4604 | #define ATH10K_HW_NSS(rate) (1 + (((rate) >> 4) & 0x3)) |
4605 | #define ATH10K_HW_PREAMBLE(rate) (((rate) >> 6) & 0x3) | 4605 | #define ATH10K_HW_PREAMBLE(rate) (((rate) >> 6) & 0x3) |
4606 | #define ATH10K_HW_RATECODE(rate, nss, preamble) \ | 4606 | #define ATH10K_HW_MCS_RATE(rate) ((rate) & 0xf) |
4607 | #define ATH10K_HW_LEGACY_RATE(rate) ((rate) & 0x3f) | ||
4608 | #define ATH10K_HW_BW(flags) (((flags) >> 3) & 0x3) | ||
4609 | #define ATH10K_HW_GI(flags) (((flags) >> 5) & 0x1) | ||
4610 | #define ATH10K_HW_RATECODE(rate, nss, preamble) \ | ||
4607 | (((preamble) << 6) | ((nss) << 4) | (rate)) | 4611 | (((preamble) << 6) | ((nss) << 4) | (rate)) |
4608 | 4612 | ||
4613 | #define VHT_MCS_NUM 10 | ||
4614 | #define VHT_BW_NUM 4 | ||
4615 | #define VHT_NSS_NUM 4 | ||
4616 | |||
4609 | /* Value to disable fixed rate setting */ | 4617 | /* Value to disable fixed rate setting */ |
4610 | #define WMI_FIXED_RATE_NONE (0xff) | 4618 | #define WMI_FIXED_RATE_NONE (0xff) |
4611 | 4619 | ||