wil6210: add support for link statistics
Driver can request FW to report link statistics using WMI_LINK_STATS_CMDID. FW will report statistics with WMI_LINK_STATS_EVENTID. Two categories of statistics defined: basic and global. New "link_stats" debugfs is used for requesting basic statistics report (write) and for reading the basic statistics (read). "link_stats_global" debugfs is used for requesting and reading the global statistics. Signed-off-by: Dedy Lansky <dlansky@codeaurora.org> Signed-off-by: Maya Erez <merez@codeaurora.org> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
@@ -464,6 +464,8 @@ static const char *cmdid2name(u16 cmdid)
|
||||
return "WMI_BCAST_DESC_RING_ADD_CMD";
|
||||
case WMI_CFG_DEF_RX_OFFLOAD_CMDID:
|
||||
return "WMI_CFG_DEF_RX_OFFLOAD_CMD";
|
||||
case WMI_LINK_STATS_CMDID:
|
||||
return "WMI_LINK_STATS_CMD";
|
||||
default:
|
||||
return "Untracked CMD";
|
||||
}
|
||||
@@ -598,6 +600,10 @@ static const char *eventid2name(u16 eventid)
|
||||
return "WMI_RX_DESC_RING_CFG_DONE_EVENT";
|
||||
case WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENTID:
|
||||
return "WMI_CFG_DEF_RX_OFFLOAD_DONE_EVENT";
|
||||
case WMI_LINK_STATS_CONFIG_DONE_EVENTID:
|
||||
return "WMI_LINK_STATS_CONFIG_DONE_EVENT";
|
||||
case WMI_LINK_STATS_EVENTID:
|
||||
return "WMI_LINK_STATS_EVENT";
|
||||
default:
|
||||
return "Untracked EVENT";
|
||||
}
|
||||
@@ -1329,6 +1335,130 @@ wmi_evt_sched_scan_result(struct wil6210_vif *vif, int id, void *d, int len)
|
||||
cfg80211_sched_scan_results(wiphy, 0);
|
||||
}
|
||||
|
||||
static void wil_link_stats_store_basic(struct wil6210_vif *vif,
|
||||
struct wmi_link_stats_basic *basic)
|
||||
{
|
||||
struct wil6210_priv *wil = vif_to_wil(vif);
|
||||
u8 cid = basic->cid;
|
||||
struct wil_sta_info *sta;
|
||||
|
||||
if (cid < 0 || cid >= WIL6210_MAX_CID) {
|
||||
wil_err(wil, "invalid cid %d\n", cid);
|
||||
return;
|
||||
}
|
||||
|
||||
sta = &wil->sta[cid];
|
||||
sta->fw_stats_basic = *basic;
|
||||
}
|
||||
|
||||
static void wil_link_stats_store_global(struct wil6210_vif *vif,
|
||||
struct wmi_link_stats_global *global)
|
||||
{
|
||||
struct wil6210_priv *wil = vif_to_wil(vif);
|
||||
|
||||
wil->fw_stats_global.stats = *global;
|
||||
}
|
||||
|
||||
static void wmi_link_stats_parse(struct wil6210_vif *vif, u64 tsf,
|
||||
bool has_next, void *payload,
|
||||
size_t payload_size)
|
||||
{
|
||||
struct wil6210_priv *wil = vif_to_wil(vif);
|
||||
size_t hdr_size = sizeof(struct wmi_link_stats_record);
|
||||
size_t stats_size, record_size, expected_size;
|
||||
struct wmi_link_stats_record *hdr;
|
||||
|
||||
if (payload_size < hdr_size) {
|
||||
wil_err(wil, "link stats wrong event size %zu\n", payload_size);
|
||||
return;
|
||||
}
|
||||
|
||||
while (payload_size >= hdr_size) {
|
||||
hdr = payload;
|
||||
stats_size = le16_to_cpu(hdr->record_size);
|
||||
record_size = hdr_size + stats_size;
|
||||
|
||||
if (payload_size < record_size) {
|
||||
wil_err(wil, "link stats payload ended unexpectedly, size %zu < %zu\n",
|
||||
payload_size, record_size);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hdr->record_type_id) {
|
||||
case WMI_LINK_STATS_TYPE_BASIC:
|
||||
expected_size = sizeof(struct wmi_link_stats_basic);
|
||||
if (stats_size < expected_size) {
|
||||
wil_err(wil, "link stats invalid basic record size %zu < %zu\n",
|
||||
stats_size, expected_size);
|
||||
return;
|
||||
}
|
||||
if (vif->fw_stats_ready) {
|
||||
/* clean old statistics */
|
||||
vif->fw_stats_tsf = 0;
|
||||
vif->fw_stats_ready = 0;
|
||||
}
|
||||
|
||||
wil_link_stats_store_basic(vif, payload + hdr_size);
|
||||
|
||||
if (!has_next) {
|
||||
vif->fw_stats_tsf = tsf;
|
||||
vif->fw_stats_ready = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
case WMI_LINK_STATS_TYPE_GLOBAL:
|
||||
expected_size = sizeof(struct wmi_link_stats_global);
|
||||
if (stats_size < sizeof(struct wmi_link_stats_global)) {
|
||||
wil_err(wil, "link stats invalid global record size %zu < %zu\n",
|
||||
stats_size, expected_size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (wil->fw_stats_global.ready) {
|
||||
/* clean old statistics */
|
||||
wil->fw_stats_global.tsf = 0;
|
||||
wil->fw_stats_global.ready = 0;
|
||||
}
|
||||
|
||||
wil_link_stats_store_global(vif, payload + hdr_size);
|
||||
|
||||
if (!has_next) {
|
||||
wil->fw_stats_global.tsf = tsf;
|
||||
wil->fw_stats_global.ready = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip to next record */
|
||||
payload += record_size;
|
||||
payload_size -= record_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wmi_evt_link_stats(struct wil6210_vif *vif, int id, void *d, int len)
|
||||
{
|
||||
struct wil6210_priv *wil = vif_to_wil(vif);
|
||||
struct wmi_link_stats_event *evt = d;
|
||||
size_t payload_size;
|
||||
|
||||
if (len < offsetof(struct wmi_link_stats_event, payload)) {
|
||||
wil_err(wil, "stats event way too short %d\n", len);
|
||||
return;
|
||||
}
|
||||
payload_size = le16_to_cpu(evt->payload_size);
|
||||
if (len < sizeof(struct wmi_link_stats_event) + payload_size) {
|
||||
wil_err(wil, "stats event too short %d\n", len);
|
||||
return;
|
||||
}
|
||||
|
||||
wmi_link_stats_parse(vif, le64_to_cpu(evt->tsf), evt->has_next,
|
||||
evt->payload, payload_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some events are ignored for purpose; and need not be interpreted as
|
||||
* "unhandled events"
|
||||
@@ -1359,6 +1489,7 @@ static const struct {
|
||||
{WMI_RING_EN_EVENTID, wmi_evt_ring_en},
|
||||
{WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_ignore},
|
||||
{WMI_SCHED_SCAN_RESULT_EVENTID, wmi_evt_sched_scan_result},
|
||||
{WMI_LINK_STATS_EVENTID, wmi_evt_link_stats},
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -3242,3 +3373,37 @@ int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wmi_link_stats_cfg(struct wil6210_vif *vif, u32 type, u8 cid, u32 interval)
|
||||
{
|
||||
struct wil6210_priv *wil = vif_to_wil(vif);
|
||||
struct wmi_link_stats_cmd cmd = {
|
||||
.record_type_mask = cpu_to_le32(type),
|
||||
.cid = cid,
|
||||
.action = WMI_LINK_STATS_SNAPSHOT,
|
||||
.interval_msec = cpu_to_le32(interval),
|
||||
};
|
||||
struct {
|
||||
struct wmi_cmd_hdr wmi;
|
||||
struct wmi_link_stats_config_done_event evt;
|
||||
} __packed reply = {
|
||||
.evt = {.status = WMI_FW_STATUS_FAILURE},
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = wmi_call(wil, WMI_LINK_STATS_CMDID, vif->mid, &cmd, sizeof(cmd),
|
||||
WMI_LINK_STATS_CONFIG_DONE_EVENTID, &reply,
|
||||
sizeof(reply), WIL_WMI_CALL_GENERAL_TO_MS);
|
||||
if (rc) {
|
||||
wil_err(wil, "WMI_LINK_STATS_CMDID failed, rc %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (reply.evt.status != WMI_FW_STATUS_SUCCESS) {
|
||||
wil_err(wil, "Link statistics config failed, status %d\n",
|
||||
reply.evt.status);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user