mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-20 19:03:39 +00:00
372 lines
11 KiB
Diff
372 lines
11 KiB
Diff
From 653734f16158d44b1bc6977536c1a93061b74e9c Mon Sep 17 00:00:00 2001
|
|
From: Sriram R <srirrama@codeaurora.org>
|
|
Date: Mon, 5 Oct 2020 17:17:21 +0530
|
|
Subject: [PATCH] ath11k: Enable WMI based FW Logging
|
|
|
|
In devices such as QCN9000, FW log data from firmware is sent via WMI Events
|
|
(WMI_DIAG_EVENTID). This data is then posted to the userspace
|
|
via NL testmode cmd as a fwlog attribute.
|
|
|
|
The logging is enabled during bootup using qmi transaction and cannot be disabled
|
|
during runtime.
|
|
|
|
Signed-off-by: Sriram R <srirrama@codeaurora.org>
|
|
---
|
|
drivers/net/wireless/ath/ath11k/core.c | 12 ++++
|
|
drivers/net/wireless/ath/ath11k/hw.h | 1 +
|
|
drivers/net/wireless/ath/ath11k/qmi.c | 92 ++++++++++++++++++++++++++++
|
|
drivers/net/wireless/ath/ath11k/qmi.h | 13 ++++
|
|
drivers/net/wireless/ath/ath11k/testmode.c | 40 ++++++++++++
|
|
drivers/net/wireless/ath/ath11k/testmode.h | 6 ++
|
|
drivers/net/wireless/ath/ath11k/testmode_i.h | 3 +
|
|
drivers/net/wireless/ath/ath11k/wmi.c | 26 ++++++++
|
|
8 files changed, 193 insertions(+)
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/core.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
|
@@ -90,6 +90,7 @@ static const struct ath11k_hw_params ath
|
|
.supports_suspend = false,
|
|
.hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074),
|
|
.reo_dest_ring_map_shift = HAL_REO_DEST_RING_CTRL_HASH_RING_SHIFT,
|
|
+ .ce_fwlog_enable = false,
|
|
},
|
|
{
|
|
.hw_rev = ATH11K_HW_IPQ6018_HW10,
|
|
@@ -136,6 +137,7 @@ static const struct ath11k_hw_params ath
|
|
.supports_suspend = false,
|
|
.hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074),
|
|
.reo_dest_ring_map_shift = HAL_REO_DEST_RING_CTRL_HASH_RING_SHIFT,
|
|
+ .ce_fwlog_enable = false,
|
|
},
|
|
{
|
|
.name = "qca6390 hw2.0",
|
|
@@ -177,6 +179,7 @@ static const struct ath11k_hw_params ath
|
|
.hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074),
|
|
.spectral_fft_sz = 2,
|
|
.reo_dest_ring_map_shift = HAL_REO_DEST_RING_CTRL_HASH_RING_SHIFT,
|
|
+ .ce_fwlog_enable = false,
|
|
},
|
|
{
|
|
.name = "qcn9074 hw1.0",
|
|
@@ -220,6 +223,7 @@ static const struct ath11k_hw_params ath
|
|
.spectral_summary_pad_sz = 16,
|
|
.spectral_fft_hdr_len = 24,
|
|
.reo_dest_ring_map_shift = HAL_REO_DEST_RING_CTRL_HASH_RING_SHIFT,
|
|
+ .ce_fwlog_enable = true,
|
|
},
|
|
{
|
|
.hw_rev = ATH11K_HW_IPQ5018,
|
|
@@ -258,6 +262,7 @@ static const struct ath11k_hw_params ath
|
|
.spectral_summary_pad_sz = 16,
|
|
.spectral_fft_hdr_len = 24,
|
|
.reo_dest_ring_map_shift = HAL_IPQ5018_REO_DEST_RING_CTRL_HASH_RING_SHIFT,
|
|
+ .ce_fwlog_enable = false,
|
|
},
|
|
};
|
|
|
|
@@ -944,6 +949,9 @@ int ath11k_core_ssr_notifier_cb(struct n
|
|
return 0;
|
|
}
|
|
#endif
|
|
+unsigned int ce_fwlog = 1;
|
|
+module_param_named(ce_fwlog, ce_fwlog, uint, 0644);
|
|
+MODULE_PARM_DESC(ce_fwlog, "Enable/Disable CE based FW logging");
|
|
|
|
int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab)
|
|
{
|
|
@@ -1004,6 +1012,14 @@ int ath11k_core_qmi_firmware_ready(struc
|
|
|
|
ath11k_config_qdss(ab);
|
|
|
|
+ if (ab->hw_params.ce_fwlog_enable && ce_fwlog) {
|
|
+ ret = ath11k_enable_fwlog(ab);
|
|
+ if (ret < 0) {
|
|
+ ath11k_err(ab, "failed to enable fwlog: %d\n", ret);
|
|
+ goto err_core_stop;
|
|
+ }
|
|
+ }
|
|
+
|
|
mutex_unlock(&ab->core_lock);
|
|
|
|
return 0;
|
|
--- a/drivers/net/wireless/ath/ath11k/hw.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/hw.h
|
|
@@ -190,6 +190,7 @@ struct ath11k_hw_params {
|
|
u32 hal_desc_sz;
|
|
u32 m3_addr;
|
|
u8 reo_dest_ring_map_shift;
|
|
+ bool ce_fwlog_enable;
|
|
};
|
|
|
|
struct ath11k_hw_ops {
|
|
--- a/drivers/net/wireless/ath/ath11k/qmi.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
|
|
@@ -2056,6 +2056,50 @@ struct qmi_elem_info qmi_wlanfw_qdss_tra
|
|
},
|
|
};
|
|
|
|
+struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = {
|
|
+ {
|
|
+ .data_type = QMI_OPT_FLAG,
|
|
+ .elem_len = 1,
|
|
+ .elem_size = sizeof(u8),
|
|
+ .array_type = NO_ARRAY,
|
|
+ .tlv_type = 0x10,
|
|
+ .offset = offsetof(struct wlfw_ini_req_msg_v01,
|
|
+ enablefwlog_valid),
|
|
+ },
|
|
+ {
|
|
+ .data_type = QMI_UNSIGNED_1_BYTE,
|
|
+ .elem_len = 1,
|
|
+ .elem_size = sizeof(u8),
|
|
+ .array_type = NO_ARRAY,
|
|
+ .tlv_type = 0x10,
|
|
+ .offset = offsetof(struct wlfw_ini_req_msg_v01,
|
|
+ enablefwlog),
|
|
+ },
|
|
+ {
|
|
+ .data_type = QMI_EOTI,
|
|
+ .array_type = NO_ARRAY,
|
|
+ .tlv_type = QMI_COMMON_TLV_TYPE,
|
|
+ },
|
|
+};
|
|
+
|
|
+struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = {
|
|
+ {
|
|
+ .data_type = QMI_STRUCT,
|
|
+ .elem_len = 1,
|
|
+ .elem_size = sizeof(struct qmi_response_type_v01),
|
|
+ .array_type = NO_ARRAY,
|
|
+ .tlv_type = 0x02,
|
|
+ .offset = offsetof(struct wlfw_ini_resp_msg_v01,
|
|
+ resp),
|
|
+ .ei_array = qmi_response_type_v01_ei,
|
|
+ },
|
|
+ {
|
|
+ .data_type = QMI_EOTI,
|
|
+ .array_type = NO_ARRAY,
|
|
+ .tlv_type = QMI_COMMON_TLV_TYPE,
|
|
+ },
|
|
+};
|
|
+
|
|
int wlfw_send_qdss_trace_config_download_req(struct ath11k_base *ab,
|
|
const u8 *buffer, unsigned int file_len)
|
|
{
|
|
@@ -3949,6 +3993,55 @@ static void ath11k_qmi_driver_event_work
|
|
spin_unlock(&qmi->event_lock);
|
|
}
|
|
|
|
+int ath11k_enable_fwlog(struct ath11k_base *ab)
|
|
+{
|
|
+ struct wlfw_ini_req_msg_v01 *req;
|
|
+ struct wlfw_ini_resp_msg_v01 resp;
|
|
+ struct qmi_txn txn = {};
|
|
+ int ret = 0;
|
|
+
|
|
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
|
|
+ if (!req)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memset(&resp, 0, sizeof(resp));
|
|
+
|
|
+ req->enablefwlog_valid = 1;
|
|
+ req->enablefwlog = 1;
|
|
+
|
|
+ ret = qmi_txn_init(&ab->qmi.handle, &txn,
|
|
+ wlfw_ini_resp_msg_v01_ei, &resp);
|
|
+ if (ret < 0)
|
|
+ goto out;
|
|
+
|
|
+ ret = qmi_send_request(&ab->qmi.handle, NULL, &txn,
|
|
+ QMI_WLFW_INI_REQ_V01,
|
|
+ WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN,
|
|
+ wlfw_ini_req_msg_v01_ei, req);
|
|
+
|
|
+ if (ret < 0) {
|
|
+ ath11k_warn(ab, "Failed to send init request for enabling fwlog = %d\n", ret);
|
|
+ qmi_txn_cancel(&txn);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS));
|
|
+ if (ret < 0) {
|
|
+ ath11k_warn(ab, "fwlog enable wait for resp failed: %d\n", ret);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
|
|
+ ath11k_warn(ab, "fwlog enable request failed, result: %d, err: %d\n",
|
|
+ resp.resp.result, resp.resp.error);
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+out:
|
|
+ kfree(req);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
int ath11k_qmi_init_service(struct ath11k_base *ab)
|
|
{
|
|
int ret;
|
|
--- a/drivers/net/wireless/ath/ath11k/qmi.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/qmi.h
|
|
@@ -204,6 +204,18 @@ struct qmi_wlanfw_m3_dump_upload_done_re
|
|
struct qmi_response_type_v01 resp;
|
|
};
|
|
|
|
+struct wlfw_ini_req_msg_v01 {
|
|
+ u8 enablefwlog_valid;
|
|
+ u8 enablefwlog;
|
|
+};
|
|
+
|
|
+struct wlfw_ini_resp_msg_v01 {
|
|
+ struct qmi_response_type_v01 resp;
|
|
+};
|
|
+
|
|
+#define QMI_WLFW_INI_REQ_V01 0x002F
|
|
+#define WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN 4
|
|
+
|
|
#define QMI_WLANFW_QDSS_TRACE_MODE_REQ_V01 0x0045
|
|
#define QMI_WLANFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_LEN 18
|
|
#define QMI_WLANFW_QDSS_TRACE_MODE_RESP_MSG_V01_MAX_LEN 7
|
|
@@ -623,6 +635,7 @@ int wlfw_send_qdss_trace_config_download
|
|
int ath11k_send_qdss_trace_mode_req(struct ath11k_base *ab,
|
|
enum wlfw_qdss_trace_mode_enum_v01 mode);
|
|
int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab);
|
|
+int ath11k_enable_fwlog(struct ath11k_base *ab);
|
|
|
|
|
|
#endif
|
|
--- a/drivers/net/wireless/ath/ath11k/testmode.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
|
|
@@ -19,8 +19,48 @@ static const struct nla_policy ath11k_tm
|
|
[ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
|
|
[ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
|
|
[ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
|
|
+ [ATH11K_TM_ATTR_FWLOG] = { .type = NLA_BINARY,
|
|
+ .len = ATH11K_FWLOG_MAX_LEN },
|
|
};
|
|
|
|
+void ath11k_fwlog_write(struct ath11k_base *ab, u8 *data, int len)
|
|
+{
|
|
+ struct sk_buff *nl_skb;
|
|
+ int ret, i;
|
|
+ struct ath11k *ar = NULL;
|
|
+ struct ath11k_pdev *pdev;
|
|
+
|
|
+ for (i = 0; i < ab->num_radios; i++) {
|
|
+ pdev = &ab->pdevs[i];
|
|
+ if (pdev && pdev->ar) {
|
|
+ ar = pdev->ar;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!ar)
|
|
+ return;
|
|
+
|
|
+ nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
|
|
+ len, GFP_ATOMIC);
|
|
+ if (!nl_skb) {
|
|
+ ath11k_warn(ab,
|
|
+ "failed to allocate skb for fwlog event\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ret = nla_put(nl_skb, ATH11K_TM_ATTR_FWLOG, len, data);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab,
|
|
+ "failed to to put fwlog wmi event to nl: %d\n",
|
|
+ ret);
|
|
+ kfree_skb(nl_skb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
|
|
+}
|
|
+
|
|
/* Returns true if callee consumes the skb and the skb should be discarded.
|
|
* Returns false if skb is not used. Does not sleep.
|
|
*/
|
|
--- a/drivers/net/wireless/ath/ath11k/testmode.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/testmode.h
|
|
@@ -28,6 +28,7 @@ bool ath11k_tm_event_wmi(struct ath11k_b
|
|
int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|
void *data, int len);
|
|
|
|
+void ath11k_fwlog_write(struct ath11k_base *ab, u8 *data, int len);
|
|
#else
|
|
|
|
static inline bool ath11k_tm_event_wmi(struct ath11k_base *ab, u32 cmd_id,
|
|
@@ -43,4 +44,9 @@ static inline int ath11k_tm_cmd(struct i
|
|
return 0;
|
|
}
|
|
|
|
+static inline void ath11k_fwlog_write(struct ath11k_base *ab, u8 *data,
|
|
+ int len)
|
|
+{
|
|
+
|
|
+}
|
|
#endif
|
|
--- a/drivers/net/wireless/ath/ath11k/testmode_i.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/testmode_i.h
|
|
@@ -16,6 +16,8 @@
|
|
#define ATH11K_TM_DATA_MAX_LEN 5000
|
|
#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048
|
|
|
|
+#define ATH11K_FWLOG_MAX_LEN 2048
|
|
+
|
|
enum ath11k_tm_attr {
|
|
__ATH11K_TM_ATTR_INVALID = 0,
|
|
ATH11K_TM_ATTR_CMD = 1,
|
|
@@ -24,6 +26,7 @@ enum ath11k_tm_attr {
|
|
ATH11K_TM_ATTR_VERSION_MAJOR = 4,
|
|
ATH11K_TM_ATTR_VERSION_MINOR = 5,
|
|
ATH11K_TM_ATTR_WMI_OP_VERSION = 6,
|
|
+ ATH11K_TM_ATTR_FWLOG = 7,
|
|
|
|
/* keep last */
|
|
__ATH11K_TM_ATTR_AFTER_LAST,
|
|
--- a/drivers/net/wireless/ath/ath11k/wmi.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
|
|
@@ -8237,6 +8237,29 @@ static void ath11k_wmi_wds_peer_event(st
|
|
wds_addr_arg.dst_macaddr);
|
|
}
|
|
|
|
+static void ath11k_wmi_diag_event(struct ath11k_base *ab, struct sk_buff *skb)
|
|
+{
|
|
+ const struct wmi_tlv *tlv;
|
|
+ u16 tlv_tag, tlv_len;
|
|
+ uint32_t *dev_id;
|
|
+ u8 *data;
|
|
+
|
|
+ tlv = (struct wmi_tlv *)skb->data;
|
|
+ tlv_tag = FIELD_GET(WMI_TLV_TAG, tlv->header);
|
|
+ tlv_len = FIELD_GET(WMI_TLV_LEN, tlv->header);
|
|
+
|
|
+ if (tlv_tag == WMI_TAG_ARRAY_BYTE) {
|
|
+ data = skb->data + sizeof(struct wmi_tlv);
|
|
+ dev_id = (uint32_t *)data;
|
|
+ *dev_id = ab->hw_params.hw_rev;
|
|
+ } else {
|
|
+ ath11k_warn(ab, "WMI Diag Event missing required tlv\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ath11k_fwlog_write(ab,data, tlv_len);
|
|
+}
|
|
+
|
|
static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
|
|
{
|
|
struct wmi_cmd_hdr *cmd_hdr;
|
|
@@ -8365,6 +8388,9 @@ static void ath11k_wmi_tlv_op_rx(struct
|
|
case WMI_MUEDCA_PARAMS_CONFIG_EVENTID:
|
|
ath11k_wmi_pdev_update_muedca_params_status_event(ab, skb);
|
|
break;
|
|
+ case WMI_DIAG_EVENTID:
|
|
+ ath11k_wmi_diag_event(ab, skb);
|
|
+ break;
|
|
/* TODO: Add remaining events */
|
|
default:
|
|
ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
|