lkml.org 
[lkml]   [2022]   [Mar]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH v2 2/2] wcn36xx: Implement tx_rate reporting
Date
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_tx_rate_update:

root@linaro-developer:~# iw wlan0 link
Connected to 6c:f3:7f:eb:98:21 (on wlan0)
SSID: SQ-DEVICETEST
freq: 2412
RX: 440785 bytes (573 packets)
TX: 60526 bytes (571 packets)
signal: -64 dBm
rx bitrate: 72.2 MBit/s MCS 7 short GI
tx bitrate: 52.0 MBit/s MCS 5

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

Tested on MSM8939 with WCN3680B running CNSS-PR-2-0-1-2-c1-00083 with
5.17, and verified by sniffing frames over the air with Wireshark to
ensure the MCS indices match.

Signed-off-by: Edmond Gagnon <egagnon@squareup.com>
Reviewed-by: Benjamin Li <benl@squareup.com>
---
drivers/net/wireless/ath/wcn36xx/hal.h | 7 ++-
drivers/net/wireless/ath/wcn36xx/main.c | 25 +++++++++
drivers/net/wireless/ath/wcn36xx/smd.c | 58 +++++++++++++++++++++
drivers/net/wireless/ath/wcn36xx/smd.h | 1 +
drivers/net/wireless/ath/wcn36xx/txrx.c | 59 ++++++++++++++++++++++
drivers/net/wireless/ath/wcn36xx/txrx.h | 2 +
drivers/net/wireless/ath/wcn36xx/wcn36xx.h | 4 ++
7 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h
index 2a1db9756fd5..46a49f0a51b3 100644
--- a/drivers/net/wireless/ath/wcn36xx/hal.h
+++ b/drivers/net/wireless/ath/wcn36xx/hal.h
@@ -2626,7 +2626,12 @@ enum tx_rate_info {
HAL_TX_RATE_SGI = 0x8,

/* 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 {
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
index 70531f62777e..75453a3744a6 100644
--- a/drivers/net/wireless/ath/wcn36xx/main.c
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -25,6 +25,7 @@
#include <linux/rpmsg.h>
#include <linux/soc/qcom/smem_state.h>
#include <linux/soc/qcom/wcnss_ctrl.h>
+#include <linux/workqueue.h>
#include <net/ipv6.h>
#include "wcn36xx.h"
#include "testmode.h"
@@ -960,6 +961,10 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
if (vif->type == NL80211_IFTYPE_STATION)
wcn36xx_smd_add_beacon_filter(wcn, vif);
wcn36xx_enable_keep_alive_null_packet(wcn, vif);
+
+ wcn->get_stats_sta = sta;
+ wcn->get_stats_vif = vif;
+ schedule_delayed_work(&wcn->get_stats_work, msecs_to_jiffies(3000));
} else {
wcn36xx_dbg(WCN36XX_DBG_MAC,
"disassociated bss %pM vif %pM AID=%d\n",
@@ -967,6 +972,9 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
vif->addr,
bss_conf->aid);
vif_priv->sta_assoc = false;
+ cancel_delayed_work_sync(&wcn->get_stats_work);
+ wcn->get_stats_vif = NULL;
+ wcn->get_stats_sta = NULL;
wcn36xx_smd_set_link_st(wcn,
bss_conf->bssid,
vif->addr,
@@ -1598,6 +1606,17 @@ static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
return ret;
}

+void wcn36xx_get_stats_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work = container_of(work, struct delayed_work, work);
+ struct wcn36xx *wcn = container_of(delayed_work, struct wcn36xx, get_stats_work);
+ int stats_status;
+
+ stats_status = wcn36xx_smd_get_stats(wcn, HAL_GLOBAL_CLASS_A_STATS_INFO);
+
+ schedule_delayed_work(&wcn->get_stats_work, msecs_to_jiffies(WCN36XX_HAL_STATS_INTERVAL));
+}
+
static int wcn36xx_probe(struct platform_device *pdev)
{
struct ieee80211_hw *hw;
@@ -1640,6 +1659,8 @@ static int wcn36xx_probe(struct platform_device *pdev)
goto out_wq;
}

+ INIT_DELAYED_WORK(&wcn->get_stats_work, wcn36xx_get_stats_work);
+
ret = dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32));
if (ret < 0) {
wcn36xx_err("failed to set DMA mask: %d\n", ret);
@@ -1713,6 +1734,10 @@ static int wcn36xx_remove(struct platform_device *pdev)
__skb_queue_purge(&wcn->amsdu);

mutex_destroy(&wcn->hal_mutex);
+
+ cancel_delayed_work_sync(&wcn->get_stats_work);
+ wcn->get_stats_vif = NULL;
+ wcn->get_stats_sta = NULL;
ieee80211_free_hw(hw);

return 0;
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index caeb68901326..ecb083d5b43a 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -2627,6 +2627,63 @@ int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 direction, u8 sta_index)
return ret;
}

+int wcn36xx_smd_get_stats(struct wcn36xx *wcn, u32 stats_mask)
+{
+ struct wcn36xx_hal_stats_req_msg msg_body;
+ struct wcn36xx_hal_stats_rsp_msg *rsp;
+ void *rsp_body;
+ int ret = 0;
+
+ 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 = get_sta_index(wcn->get_stats_vif,
+ wcn36xx_sta_to_priv(wcn->get_stats_sta));
+ 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("stat_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) {
+ ret = wcn36xx_report_tx_rate(wcn, (struct ani_global_class_a_stats_info *)rsp_body);
+ if (ret) {
+ wcn36xx_err("failed to report TX rate\n");
+ goto out;
+ }
+ 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)
{
struct wcn36xx_hal_trigger_ba_rsp_candidate *candidate;
@@ -3316,6 +3373,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_ADD_BA_SESSION_RSP:
case WCN36XX_HAL_ADD_BA_RSP:
case WCN36XX_HAL_DEL_BA_RSP:
+ case WCN36XX_HAL_GET_STATS_RSP:
case WCN36XX_HAL_TRIGGER_BA_RSP:
case WCN36XX_HAL_UPDATE_CFG_RSP:
case WCN36XX_HAL_JOIN_RSP:
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h
index 957cfa87fbde..a30f28f4130d 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.h
+++ b/drivers/net/wireless/ath/wcn36xx/smd.h
@@ -138,6 +138,7 @@ int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
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_trigger_ba(struct wcn36xx *wcn, u8 sta_index, u16 tid, u16 *ssn);
+int wcn36xx_smd_get_stats(struct wcn36xx *wcn, u32 stats_mask);

int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value);

diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c
index df749b114568..ac55f8d62440 100644
--- a/drivers/net/wireless/ath/wcn36xx/txrx.c
+++ b/drivers/net/wireless/ath/wcn36xx/txrx.c
@@ -699,3 +699,62 @@ int wcn36xx_start_tx(struct wcn36xx *wcn,

return ret;
}
+
+int wcn36xx_report_tx_rate(struct wcn36xx *wcn, struct ani_global_class_a_stats_info *stats)
+{
+ struct ieee80211_tx_info info;
+ struct ieee80211_tx_rate *tx_rate;
+
+ memset(&info, 0, sizeof(struct ieee80211_tx_info));
+ tx_rate = &info.status.rates[0];
+
+ tx_rate->idx = stats->mcs_index;
+ tx_rate->count = 0;
+ tx_rate->flags = 0;
+
+ if (stats->tx_rate_flags & HAL_TX_RATE_LEGACY) {
+ // tx_rate is in units of 500kbps, while wcn36xx_rate_table's rates
+ // are in units of 100kbps.
+ unsigned int reported_rate = stats->tx_rate * 5;
+ int i;
+
+ // Iterate over the legacy rates to convert bitrate to rate index.
+ for (i = 0; i < ARRAY_SIZE(wcn36xx_rate_table); i++) {
+ const struct wcn36xx_rate *rate = &wcn36xx_rate_table[i];
+
+ if (rate->encoding != RX_ENC_LEGACY) {
+ wcn36xx_warn("legacy rate %u not present in rate table\n",
+ reported_rate);
+ break;
+ }
+ if (rate->bitrate == reported_rate) {
+ tx_rate->idx = rate->mcs_or_legacy_index;
+ break;
+ }
+ }
+ }
+
+ // HT?
+ if (stats->tx_rate_flags & (HAL_TX_RATE_HT20 | HAL_TX_RATE_HT40))
+ tx_rate->flags |= IEEE80211_TX_RC_MCS;
+
+ // VHT?
+ if (stats->tx_rate_flags & (HAL_TX_RATE_VHT20 | HAL_TX_RATE_VHT40 | HAL_TX_RATE_VHT80))
+ tx_rate->flags |= IEEE80211_TX_RC_VHT_MCS;
+
+ // SGI / LGI?
+ if (stats->tx_rate_flags & HAL_TX_RATE_SGI)
+ tx_rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+
+ // 40MHz?
+ if (stats->tx_rate_flags & (HAL_TX_RATE_HT40 | HAL_TX_RATE_VHT40))
+ tx_rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+
+ // 80MHz?
+ if (stats->tx_rate_flags & HAL_TX_RATE_VHT80)
+ tx_rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+
+ ieee80211_tx_rate_update(wcn->hw, wcn->get_stats_sta, &info);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.h b/drivers/net/wireless/ath/wcn36xx/txrx.h
index b54311ffde9c..28cf45ce6c89 100644
--- a/drivers/net/wireless/ath/wcn36xx/txrx.h
+++ b/drivers/net/wireless/ath/wcn36xx/txrx.h
@@ -18,6 +18,7 @@
#define _TXRX_H_

#include <linux/etherdevice.h>
+#include <linux/string.h>
#include "wcn36xx.h"

/* TODO describe all properties */
@@ -164,5 +165,6 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb);
int wcn36xx_start_tx(struct wcn36xx *wcn,
struct wcn36xx_sta *sta_priv,
struct sk_buff *skb);
+int wcn36xx_report_tx_rate(struct wcn36xx *wcn, struct ani_global_class_a_stats_info *stats);

#endif /* _TXRX_H_ */
diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
index 80a4e7aea419..121195991ee2 100644
--- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
+++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h
@@ -32,6 +32,7 @@

#define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
#define WCN36XX_AGGR_BUFFER_SIZE 64
+#define WCN36XX_HAL_STATS_INTERVAL 3000

extern unsigned int wcn36xx_dbg_mask;

@@ -295,6 +296,9 @@ struct wcn36xx {

spinlock_t survey_lock; /* protects chan_survey */
struct wcn36xx_chan_survey *chan_survey;
+ struct delayed_work get_stats_work;
+ struct ieee80211_sta *get_stats_sta;
+ struct ieee80211_vif *get_stats_vif;
};

static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
--
2.25.1
\
 
 \ /
  Last update: 2022-03-18 20:59    [W:0.145 / U:0.172 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site