From 283c1dcd17ed571119aa452c4c170bff34fe87af Mon Sep 17 00:00:00 2001 From: Alexei Avshalom Lazar Date: Wed, 4 Dec 2019 09:52:08 +0200 Subject: [PATCH] wil6210: publish/receive WMI events/commands through nl For general debug purposes added functionality for sending WMI events and receiving WMI commands to/from user-space through vendor commands. Change-Id: I889d30bf1f060f75d7ae933735d1d959e53e513e Signed-off-by: Alexei Avshalom Lazar [liord@codeaurora.org: fix merge conflicts, checkpatch errors] Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/cfg80211.c | 235 ++++++++++++++++++++ drivers/net/wireless/ath/wil6210/ftm.h | 1 + drivers/net/wireless/ath/wil6210/main.c | 2 + drivers/net/wireless/ath/wil6210/wil6210.h | 6 + drivers/net/wireless/ath/wil6210/wmi.c | 23 +- 5 files changed, 262 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 1819b2a35937..05b25f43f2b5 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -102,6 +102,49 @@ static int wil_tx_cb_mode_to_n_bonded(u8 cb_mode) } } +enum wil_nl_60g_cmd_type { + NL_60G_CMD_FW_WMI, + NL_60G_CMD_DEBUG, + NL_60G_CMD_STATISTICS, + NL_60G_CMD_REGISTER, +}; + +enum wil_nl_60g_evt_type { + NL_60G_EVT_DRIVER_ERROR, + NL_60G_EVT_FW_ERROR, + NL_60G_EVT_FW_WMI, + NL_60G_EVT_DRIVER_SHUTOWN, + NL_60G_EVT_DRIVER_DEBUG_EVENT, +}; + +enum wil_nl_60g_debug_cmd { + NL_60G_DBG_FORCE_WMI_SEND, +}; + +struct wil_nl_60g_send_receive_wmi { + u32 cmd_id; /* enum wmi_command_id or enum wmi_event_id */ + u8 reserved[2]; + u8 dev_id; /* mid */ + u16 buf_len; + u8 buf[0]; +} __packed; + +struct wil_nl_60g_event { + u32 evt_type; /* wil_nl_60g_evt_type */ + u32 buf_len; + u8 reserved[9]; + u8 buf[0]; +} __packed; + +struct wil_nl_60g_debug { /* NL_60G_CMD_DEBUG */ + u32 cmd_id; /* wil_nl_60g_debug_cmd */ +} __packed; + +struct wil_nl_60g_debug_force_wmi { + struct wil_nl_60g_debug hdr; + u32 enable; +} __packed; + static void wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len) { @@ -157,6 +200,8 @@ void update_supported_bands(struct wil6210_priv *wil) enum qca_wlan_vendor_attr_wil { QCA_ATTR_MAC_ADDR = 6, + QCA_ATTR_FEATURE_FLAGS = 7, + QCA_ATTR_TEST = 8, QCA_ATTR_PAD = 13, QCA_ATTR_TSF = 29, QCA_ATTR_DMG_RF_SECTOR_INDEX = 30, @@ -168,6 +213,9 @@ enum qca_wlan_vendor_attr_wil { QCA_ATTR_WIL_MAX, }; +#define WIL_ATTR_60G_CMD_TYPE QCA_ATTR_FEATURE_FLAGS +#define WIL_ATTR_60G_BUF QCA_ATTR_TEST + enum qca_wlan_vendor_attr_dmg_rf_sector_type { QCA_ATTR_DMG_RF_SECTOR_TYPE_RX, QCA_ATTR_DMG_RF_SECTOR_TYPE_TX, @@ -224,7 +272,14 @@ nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = { [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 }, }; +static const struct +nla_policy wil_nl_60g_policy[QCA_ATTR_WIL_MAX + 1] = { + [WIL_ATTR_60G_CMD_TYPE] = { .type = NLA_U32 }, + [WIL_ATTR_60G_BUF] = { .type = NLA_BINARY }, +}; + enum qca_nl80211_vendor_subcmds { + QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130, @@ -256,6 +311,8 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, static int wil_brp_set_ant_limit(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); +static int wil_nl_60g_handle_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); /* vendor specific commands */ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { { @@ -341,6 +398,13 @@ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_brp_set_ant_limit }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_UNSPEC, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = wil_nl_60g_handle_cmd + }, }; /* vendor specific events */ @@ -357,6 +421,10 @@ static const struct nl80211_vendor_cmd_info wil_nl80211_vendor_events[] = { .vendor_id = QCA_NL80211_VENDOR_ID, .subcmd = QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT }, + [QCA_EVENT_UNSPEC_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_UNSPEC + }, }; static struct ieee80211_supported_band wil_band_60ghz = { @@ -3518,3 +3586,170 @@ static int wil_brp_set_ant_limit(struct wiphy *wiphy, struct wireless_dev *wdev, return wil_brp_wmi_set_ant_limit(wil, vif->mid, cid, limit_mode, antenna_num_limit); } + +static int wil_nl_60g_handle_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct wil6210_priv *wil = wdev_to_wil(wdev); + struct nlattr *tb[QCA_ATTR_WIL_MAX + 1]; + struct wil_nl_60g_send_receive_wmi *cmd; + struct wil_nl_60g_debug_force_wmi debug_force_wmi; + int rc, len; + u32 wil_nl_60g_cmd_type, publish; + + rc = nla_parse(tb, QCA_ATTR_WIL_MAX, data, data_len, + wil_nl_60g_policy, NULL); + if (rc) { + wil_err(wil, "Invalid nl_60g_cmd ATTR\n"); + return rc; + } + + if (!tb[WIL_ATTR_60G_CMD_TYPE]) { + wil_err(wil, "Invalid nl_60g_cmd type\n"); + return -EINVAL; + } + + wil_nl_60g_cmd_type = nla_get_u32(tb[WIL_ATTR_60G_CMD_TYPE]); + + switch (wil_nl_60g_cmd_type) { + case NL_60G_CMD_REGISTER: + if (!tb[WIL_ATTR_60G_BUF]) { + wil_err(wil, "Invalid nl_60g_cmd spec\n"); + return -EINVAL; + } + + len = nla_len(tb[WIL_ATTR_60G_BUF]); + if (len != sizeof(publish)) { + wil_err(wil, "cmd buffer wrong len %d\n", len); + return -EINVAL; + } + memcpy(&publish, nla_data(tb[WIL_ATTR_60G_BUF]), len); + wil->publish_nl_evt = publish; + + wil_dbg_wmi(wil, "Publish wmi event %s\n", + publish ? "enabled" : "disabled"); + break; + case NL_60G_CMD_DEBUG: + if (!tb[WIL_ATTR_60G_BUF]) { + wil_err(wil, "Invalid nl_60g_cmd spec\n"); + return -EINVAL; + } + + len = nla_len(tb[WIL_ATTR_60G_BUF]); + if (len < sizeof(struct wil_nl_60g_debug)) { + wil_err(wil, "cmd buffer too short %d\n", len); + return -EINVAL; + } + + memcpy(&debug_force_wmi, nla_data(tb[WIL_ATTR_60G_BUF]), + sizeof(struct wil_nl_60g_debug)); + + switch (debug_force_wmi.hdr.cmd_id) { + case NL_60G_DBG_FORCE_WMI_SEND: + if (len != sizeof(debug_force_wmi)) { + wil_err(wil, "cmd buffer wrong len %d\n", len); + return -EINVAL; + } + + memcpy(&debug_force_wmi, nla_data(tb[WIL_ATTR_60G_BUF]), + sizeof(debug_force_wmi)); + wil->force_wmi_send = debug_force_wmi.enable; + + wil_dbg_wmi(wil, "force sending wmi commands %d\n", + wil->force_wmi_send); + break; + default: + rc = -EINVAL; + wil_err(wil, "invalid debug_cmd id %d", + debug_force_wmi.hdr.cmd_id); + } + break; + case NL_60G_CMD_FW_WMI: + if (!tb[WIL_ATTR_60G_BUF]) { + wil_err(wil, "Invalid nl_60g_cmd spec\n"); + return -EINVAL; + } + + len = nla_len(tb[WIL_ATTR_60G_BUF]); + if (len < offsetof(struct wil_nl_60g_send_receive_wmi, buf)) { + wil_err(wil, "wmi cmd buffer too small\n"); + return -EINVAL; + } + + cmd = kmalloc(len, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + memcpy(cmd, nla_data(tb[WIL_ATTR_60G_BUF]), (unsigned int)len); + + wil_dbg_wmi(wil, "sending user-space command (0x%04x) [%d]\n", + cmd->cmd_id, cmd->buf_len); + + if (wil->force_wmi_send) + rc = wmi_force_send(wil, cmd->cmd_id, cmd->dev_id, + cmd->buf, cmd->buf_len); + else + rc = wmi_send(wil, cmd->cmd_id, cmd->dev_id, + cmd->buf, cmd->buf_len); + + kfree(cmd); + break; + default: + rc = -EINVAL; + wil_err(wil, "invalid nl_60g_cmd type %d", wil_nl_60g_cmd_type); + } + + return rc; +} + +void wil_nl_60g_receive_wmi_evt(struct wil6210_priv *wil, u8 *cmd, int len) +{ + struct sk_buff *vendor_event = NULL; + struct wil_nl_60g_event *evt; + struct wil_nl_60g_send_receive_wmi *wmi_buf; + struct wmi_cmd_hdr *wmi_hdr = (struct wmi_cmd_hdr *)cmd; + int data_len; + + if (!wil->publish_nl_evt) + return; + + wil_dbg_wmi(wil, "report wmi event to user-space (0x%04x) [%d]\n", + le16_to_cpu(wmi_hdr->command_id), len); + + data_len = len - sizeof(struct wmi_cmd_hdr); + + evt = kzalloc(sizeof(*evt) + sizeof(*wmi_buf) + data_len, GFP_KERNEL); + if (!evt) + return; + + evt->evt_type = NL_60G_EVT_FW_WMI; + evt->buf_len = sizeof(*wmi_buf) + data_len; + + wmi_buf = (struct wil_nl_60g_send_receive_wmi *)evt->buf; + + wmi_buf->cmd_id = le16_to_cpu(wmi_hdr->command_id); + wmi_buf->dev_id = wmi_hdr->mid; + wmi_buf->buf_len = data_len; + memcpy(wmi_buf->buf, cmd + sizeof(struct wmi_cmd_hdr), data_len); + + vendor_event = + cfg80211_vendor_event_alloc(wil_to_wiphy(wil), + NULL, + data_len + 4 + NLMSG_HDRLEN + + sizeof(*evt) + sizeof(*wmi_buf), + QCA_EVENT_UNSPEC_INDEX, + GFP_KERNEL); + if (!vendor_event) + goto out; + + if (nla_put(vendor_event, WIL_ATTR_60G_BUF, + sizeof(*evt) + sizeof(*wmi_buf) + data_len, evt)) { + wil_err(wil, "failed to fill WIL_ATTR_60G_BUF\n"); + goto out; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + +out: + kfree(evt); +} diff --git a/drivers/net/wireless/ath/wil6210/ftm.h b/drivers/net/wireless/ath/wil6210/ftm.h index ec9a1464a657..cb4a0c8f9890 100644 --- a/drivers/net/wireless/ath/wil6210/ftm.h +++ b/drivers/net/wireless/ath/wil6210/ftm.h @@ -401,6 +401,7 @@ enum qca_events_index { QCA_EVENT_FTM_MEAS_RESULT_INDEX, QCA_EVENT_FTM_SESSION_DONE_INDEX, QCA_EVENT_AOA_MEAS_RESULT_INDEX, + QCA_EVENT_UNSPEC_INDEX, }; /* measurement parameters. Specified for each peer as part diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index c0c4f913115d..8b1baa7b35db 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1691,6 +1691,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wmi_event_flush(wil); + wil->force_wmi_send = false; + flush_workqueue(wil->wq_service); flush_workqueue(wil->wmi_wq); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index aef6e90b0fef..74ec64e4bf53 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1060,6 +1060,9 @@ struct wil6210_priv { u32 rgf_ucode_assert_code_addr; u32 iccm_base; + u8 publish_nl_evt; /* deliver WMI events to user space */ + u8 force_wmi_send; /* allow WMI command while FW in sysassert */ + /* relevant only for eDMA */ bool use_compressed_rx_status; u32 rx_status_ring_order; @@ -1251,6 +1254,8 @@ void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, struct wil6210_mbox_hdr *hdr); int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len); +int wmi_force_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, + u16 len); void wmi_recv_cmd(struct wil6210_priv *wil); int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len, u16 reply_id, void *reply, u16 reply_size, int to_msec); @@ -1444,6 +1449,7 @@ void wil_ftm_evt_per_dest_res(struct wil6210_vif *vif, void wil_aoa_evt_meas(struct wil6210_vif *vif, struct wmi_aoa_meas_event *evt, int len); +void wil_nl_60g_receive_wmi_evt(struct wil6210_priv *wil, u8 *cmd, int len); /* link loss */ int wmi_link_maintain_cfg_write(struct wil6210_priv *wil, const u8 *addr, diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 51978483f6d5..4898c4503059 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -631,7 +631,7 @@ static const char *eventid2name(u16 eventid) } static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, - void *buf, u16 len) + void *buf, u16 len, bool force_send) { struct { struct wil6210_mbox_hdr hdr; @@ -663,7 +663,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, might_sleep(); - if (!test_bit(wil_status_fwready, wil->status)) { + if (!test_bit(wil_status_fwready, wil->status) && !force_send) { wil_err(wil, "WMI: cannot send command while FW not ready\n"); return -EAGAIN; } @@ -702,7 +702,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); /* wait till FW finish with previous command */ for (retry = 5; retry > 0; retry--) { - if (!test_bit(wil_status_fwready, wil->status)) { + if (!test_bit(wil_status_fwready, wil->status) && !force_send) { wil_err(wil, "WMI: cannot send command while FW not ready\n"); rc = -EAGAIN; goto out; @@ -757,7 +757,19 @@ int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len) int rc; mutex_lock(&wil->wmi_mutex); - rc = __wmi_send(wil, cmdid, mid, buf, len); + rc = __wmi_send(wil, cmdid, mid, buf, len, false); + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +int wmi_force_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, + u16 len) +{ + int rc; + + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send(wil, cmdid, mid, buf, len, true); mutex_unlock(&wil->wmi_mutex); return rc; @@ -1988,6 +2000,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) u16 id = le16_to_cpu(wmi->command_id); u8 mid = wmi->mid; u32 tstamp = le32_to_cpu(wmi->fw_timestamp); + wil_nl_60g_receive_wmi_evt(wil, cmd, len); if (test_bit(wil_status_resuming, wil->status)) { if (id == WMI_TRAFFIC_RESUME_EVENTID) clear_bit(wil_status_resuming, @@ -2064,7 +2077,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len, reinit_completion(&wil->wmi_call); spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); - rc = __wmi_send(wil, cmdid, mid, buf, len); + rc = __wmi_send(wil, cmdid, mid, buf, len, false); if (rc) goto out;