Commit 1216c4d3 authored by Edmond Gagnon's avatar Edmond Gagnon Committed by Kalle Valo

wcn36xx: Implement tx_rate reporting

Currently, the driver reports a tx_rate of 6.0 MBit/s no matter the true
rate:

root@linaro-developer:~# iw wlan0 link
Connected to 6c:f3:7f:eb:9b:92 (on wlan0)
        SSID: SQ-DEVICETEST
        freq: 5200
        RX: 4141 bytes (32 packets)
        TX: 2082 bytes (15 packets)
        signal: -77 dBm
        rx bitrate: 135.0 MBit/s MCS 6 40MHz short GI
        tx bitrate: 6.0 MBit/s

        bss flags:      short-slot-time
        dtim period:    1
        beacon int:     100

This patch requests HAL_GLOBAL_CLASS_A_STATS_INFO via a hal_get_stats
firmware message and reports it via ieee80211_ops::sta_statistics.

root@linaro-developer:~# iw wlan0 link
Connected to 6c:f3:7f:eb:73:b2 (on wlan0)
        SSID: SQ-DEVICETEST
        freq: 5700
        RX: 26788094 bytes (19859 packets)
        TX: 1101376 bytes (12119 packets)
        signal: -75 dBm
        rx bitrate: 135.0 MBit/s MCS 6 40MHz short GI
        tx bitrate: 108.0 MBit/s VHT-MCS 5 40MHz VHT-NSS 1

        bss flags:      short-slot-time
        dtim period:    1
        beacon int:     100

Tested on MSM8939 with WCN3680B running firmware CNSS-PR-2-0-1-2-c1-00083,
and verified by sniffing frames over the air with Wireshark to ensure the
MCS indices match.
Signed-off-by: default avatarEdmond Gagnon <egagnon@squareup.com>
Reviewed-by: default avatarBenjamin Li <benl@squareup.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220325224212.159690-1-egagnon@squareup.com
parent b2beae32
...@@ -2626,7 +2626,12 @@ enum tx_rate_info { ...@@ -2626,7 +2626,12 @@ enum tx_rate_info {
HAL_TX_RATE_SGI = 0x8, HAL_TX_RATE_SGI = 0x8,
/* Rate with Long guard interval */ /* Rate with Long guard interval */
HAL_TX_RATE_LGI = 0x10 HAL_TX_RATE_LGI = 0x10,
/* VHT rates */
HAL_TX_RATE_VHT20 = 0x20,
HAL_TX_RATE_VHT40 = 0x40,
HAL_TX_RATE_VHT80 = 0x80,
}; };
struct ani_global_class_a_stats_info { struct ani_global_class_a_stats_info {
......
...@@ -1400,6 +1400,21 @@ static int wcn36xx_get_survey(struct ieee80211_hw *hw, int idx, ...@@ -1400,6 +1400,21 @@ static int wcn36xx_get_survey(struct ieee80211_hw *hw, int idx,
return 0; return 0;
} }
static void wcn36xx_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct station_info *sinfo)
{
struct wcn36xx *wcn;
u8 sta_index;
int status;
wcn = hw->priv;
sta_index = get_sta_index(vif, wcn36xx_sta_to_priv(sta));
status = wcn36xx_smd_get_stats(wcn, sta_index, HAL_GLOBAL_CLASS_A_STATS_INFO, sinfo);
if (status)
wcn36xx_err("wcn36xx_smd_get_stats failed\n");
}
static const struct ieee80211_ops wcn36xx_ops = { static const struct ieee80211_ops wcn36xx_ops = {
.start = wcn36xx_start, .start = wcn36xx_start,
.stop = wcn36xx_stop, .stop = wcn36xx_stop,
...@@ -1423,6 +1438,7 @@ static const struct ieee80211_ops wcn36xx_ops = { ...@@ -1423,6 +1438,7 @@ static const struct ieee80211_ops wcn36xx_ops = {
.set_rts_threshold = wcn36xx_set_rts_threshold, .set_rts_threshold = wcn36xx_set_rts_threshold,
.sta_add = wcn36xx_sta_add, .sta_add = wcn36xx_sta_add,
.sta_remove = wcn36xx_sta_remove, .sta_remove = wcn36xx_sta_remove,
.sta_statistics = wcn36xx_sta_statistics,
.ampdu_action = wcn36xx_ampdu_action, .ampdu_action = wcn36xx_ampdu_action,
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
.ipv6_addr_change = wcn36xx_ipv6_addr_change, .ipv6_addr_change = wcn36xx_ipv6_addr_change,
......
...@@ -2627,6 +2627,62 @@ int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 direction, u8 sta_index) ...@@ -2627,6 +2627,62 @@ int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 direction, u8 sta_index)
return ret; return ret;
} }
int wcn36xx_smd_get_stats(struct wcn36xx *wcn, u8 sta_index, u32 stats_mask,
struct station_info *sinfo)
{
struct wcn36xx_hal_stats_req_msg msg_body;
struct wcn36xx_hal_stats_rsp_msg *rsp;
void *rsp_body;
int ret;
if (stats_mask & ~HAL_GLOBAL_CLASS_A_STATS_INFO) {
wcn36xx_err("stats_mask 0x%x contains unimplemented types\n",
stats_mask);
return -EINVAL;
}
mutex_lock(&wcn->hal_mutex);
INIT_HAL_MSG(msg_body, WCN36XX_HAL_GET_STATS_REQ);
msg_body.sta_id = sta_index;
msg_body.stats_mask = stats_mask;
PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
if (ret) {
wcn36xx_err("sending hal_get_stats failed\n");
goto out;
}
ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
if (ret) {
wcn36xx_err("hal_get_stats response failed err=%d\n", ret);
goto out;
}
rsp = (struct wcn36xx_hal_stats_rsp_msg *)wcn->hal_buf;
rsp_body = (wcn->hal_buf + sizeof(struct wcn36xx_hal_stats_rsp_msg));
if (rsp->stats_mask != stats_mask) {
wcn36xx_err("stats_mask 0x%x differs from requested 0x%x\n",
rsp->stats_mask, stats_mask);
goto out;
}
if (rsp->stats_mask & HAL_GLOBAL_CLASS_A_STATS_INFO) {
struct ani_global_class_a_stats_info *stats_info = rsp_body;
wcn36xx_process_tx_rate(stats_info, &sinfo->txrate);
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
rsp_body += sizeof(struct ani_global_class_a_stats_info);
}
out:
mutex_unlock(&wcn->hal_mutex);
return ret;
}
static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len, struct add_ba_info *ba_info) static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len, struct add_ba_info *ba_info)
{ {
struct wcn36xx_hal_trigger_ba_rsp_candidate *candidate; struct wcn36xx_hal_trigger_ba_rsp_candidate *candidate;
...@@ -3316,6 +3372,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, ...@@ -3316,6 +3372,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_ADD_BA_SESSION_RSP: case WCN36XX_HAL_ADD_BA_SESSION_RSP:
case WCN36XX_HAL_ADD_BA_RSP: case WCN36XX_HAL_ADD_BA_RSP:
case WCN36XX_HAL_DEL_BA_RSP: case WCN36XX_HAL_DEL_BA_RSP:
case WCN36XX_HAL_GET_STATS_RSP:
case WCN36XX_HAL_TRIGGER_BA_RSP: case WCN36XX_HAL_TRIGGER_BA_RSP:
case WCN36XX_HAL_UPDATE_CFG_RSP: case WCN36XX_HAL_UPDATE_CFG_RSP:
case WCN36XX_HAL_JOIN_RSP: case WCN36XX_HAL_JOIN_RSP:
......
...@@ -138,6 +138,8 @@ int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, ...@@ -138,6 +138,8 @@ int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
int wcn36xx_smd_add_ba(struct wcn36xx *wcn, u8 session_id); int wcn36xx_smd_add_ba(struct wcn36xx *wcn, u8 session_id);
int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 direction, u8 sta_index); int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 direction, u8 sta_index);
int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index, u16 tid, u16 *ssn); int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index, u16 tid, u16 *ssn);
int wcn36xx_smd_get_stats(struct wcn36xx *wcn, u8 sta_index, u32 stats_mask,
struct station_info *sinfo);
int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value); int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value);
......
...@@ -699,3 +699,32 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, ...@@ -699,3 +699,32 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,
return ret; return ret;
} }
void wcn36xx_process_tx_rate(struct ani_global_class_a_stats_info *stats, struct rate_info *info)
{
/* tx_rate is in units of 500kbps; mac80211 wants them in 100kbps */
if (stats->tx_rate_flags & HAL_TX_RATE_LEGACY)
info->legacy = stats->tx_rate * 5;
info->flags = 0;
info->mcs = stats->mcs_index;
info->nss = 1;
if (stats->tx_rate_flags & (HAL_TX_RATE_HT20 | HAL_TX_RATE_HT40))
info->flags |= RATE_INFO_FLAGS_MCS;
if (stats->tx_rate_flags & (HAL_TX_RATE_VHT20 | HAL_TX_RATE_VHT40 | HAL_TX_RATE_VHT80))
info->flags |= RATE_INFO_FLAGS_VHT_MCS;
if (stats->tx_rate_flags & HAL_TX_RATE_SGI)
info->flags |= RATE_INFO_FLAGS_SHORT_GI;
if (stats->tx_rate_flags & (HAL_TX_RATE_HT20 | HAL_TX_RATE_VHT20))
info->bw = RATE_INFO_BW_20;
if (stats->tx_rate_flags & (HAL_TX_RATE_HT40 | HAL_TX_RATE_VHT40))
info->bw = RATE_INFO_BW_40;
if (stats->tx_rate_flags & HAL_TX_RATE_VHT80)
info->bw = RATE_INFO_BW_80;
}
...@@ -164,5 +164,6 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb); ...@@ -164,5 +164,6 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb);
int wcn36xx_start_tx(struct wcn36xx *wcn, int wcn36xx_start_tx(struct wcn36xx *wcn,
struct wcn36xx_sta *sta_priv, struct wcn36xx_sta *sta_priv,
struct sk_buff *skb); struct sk_buff *skb);
void wcn36xx_process_tx_rate(struct ani_global_class_a_stats_info *stats, struct rate_info *info);
#endif /* _TXRX_H_ */ #endif /* _TXRX_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment