mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-19 18:31:33 +00:00
688 lines
20 KiB
Diff
688 lines
20 KiB
Diff
From 35a3aff175b3367684823712efa89fb7dfc46f4c Mon Sep 17 00:00:00 2001
|
|
From: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
|
|
Date: Thu, 15 Oct 2020 12:43:43 +0530
|
|
Subject: [PATCH] ath11k: add module param for ftm
|
|
|
|
Device was switched to ftm mode on receiving start command from
|
|
ftm userspace add, now module param has been added to start
|
|
device in ftm mode directly.Device needs to be rebooted or
|
|
ath11k module has to be reinserted with ftm_mode unset to
|
|
bring it back to machine mode. Booting in ftm mode would
|
|
prevent loading of cal data and vap from starting.
|
|
|
|
Command: insmod ath11k ftm_mode=1
|
|
|
|
Signed-off-by: Sowmiya Sree Elavalagan <ssreeela@codeaurora.org>
|
|
---
|
|
drivers/net/wireless/ath/ath11k/core.c | 11 +-
|
|
drivers/net/wireless/ath/ath11k/core.h | 2 +
|
|
drivers/net/wireless/ath/ath11k/debug.h | 1 +
|
|
drivers/net/wireless/ath/ath11k/mac.c | 5 +
|
|
drivers/net/wireless/ath/ath11k/qmi.c | 5 +
|
|
drivers/net/wireless/ath/ath11k/testmode.c | 276 +++++++++++++++------------
|
|
drivers/net/wireless/ath/ath11k/testmode.h | 21 +-
|
|
drivers/net/wireless/ath/ath11k/testmode_i.h | 2 +-
|
|
drivers/net/wireless/ath/ath11k/wmi.c | 36 +++-
|
|
drivers/net/wireless/ath/ath11k/wmi.h | 20 ++
|
|
10 files changed, 247 insertions(+), 132 deletions(-)
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/core.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.c
|
|
@@ -42,6 +42,10 @@ EXPORT_SYMBOL(ath11k_host_ddr_addr);
|
|
module_param_named(host_ddr_addr, ath11k_host_ddr_addr, uint, 0644);
|
|
MODULE_PARM_DESC(host_ddr_addr, "host ddr addr for FW");
|
|
|
|
+unsigned int ath11k_ftm_mode;
|
|
+module_param_named(ftm_mode, ath11k_ftm_mode, uint, 0644);
|
|
+MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
|
|
+
|
|
struct ath11k_base *ath11k_soc;
|
|
|
|
static const struct ath11k_hw_params ath11k_hw_params[] = {
|
|
@@ -673,6 +677,11 @@ static int ath11k_core_soc_create(struct
|
|
{
|
|
int ret;
|
|
|
|
+ if (ath11k_ftm_mode) {
|
|
+ ab->fw_mode = ATH11K_FIRMWARE_MODE_FTM;
|
|
+ ath11k_info(ab, "Booting in ftm mode - %d\n", ab->fw_mode);
|
|
+ }
|
|
+
|
|
ret = ath11k_qmi_init_service(ab);
|
|
if (ret) {
|
|
ath11k_err(ab, "failed to initialize qmi :%d\n", ret);
|
|
@@ -1184,9 +1193,6 @@ static void ath11k_core_restart(struct w
|
|
break;
|
|
case ATH11K_STATE_TM:
|
|
ath11k_warn(ab, "fw mode reset done radio %d\n", i);
|
|
- if (test_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &ar->ab->dev_flags)) {
|
|
- complete(&ar->fw_mode_reset);
|
|
- }
|
|
break;
|
|
}
|
|
mutex_unlock(&ar->conf_mutex);
|
|
--- a/drivers/net/wireless/ath/ath11k/core.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/core.h
|
|
@@ -676,6 +676,7 @@ struct ath11k {
|
|
bool dfs_block_radar_events;
|
|
struct ath11k_thermal thermal;
|
|
struct completion fw_mode_reset;
|
|
+ u8 ftm_msgref;
|
|
int ap_ps_enabled;
|
|
enum ath11k_ap_ps_state ap_ps_state;
|
|
|
|
@@ -863,6 +864,7 @@ struct ath11k_base {
|
|
/* protected by data_lock */
|
|
u32 fw_crash_counter;
|
|
} stats;
|
|
+ bool ftm_segment_handler;
|
|
struct ath11k_ftm_event_obj ftm_event_obj;
|
|
|
|
u32 max_ast_index;
|
|
--- a/drivers/net/wireless/ath/ath11k/debug.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/debug.h
|
|
@@ -33,6 +33,7 @@ __printf(2, 3) void ath11k_err(struct at
|
|
__printf(2, 3) void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...);
|
|
|
|
extern unsigned int ath11k_debug_mask;
|
|
+extern unsigned int ath11k_ftm_mode;
|
|
|
|
#ifdef CPTCFG_ATH11K_DEBUG
|
|
__printf(3, 4) void __ath11k_dbg(struct ath11k_base *ab,
|
|
--- a/drivers/net/wireless/ath/ath11k/mac.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/mac.c
|
|
@@ -5560,6 +5560,11 @@ static int ath11k_mac_op_start(struct ie
|
|
struct device *dev = ab->dev;
|
|
int ret;
|
|
|
|
+ if (ath11k_ftm_mode) {
|
|
+ ath11k_err(ab, "fail to start mac operations in ftm mode\n");
|
|
+ return -EWOULDBLOCK;
|
|
+ }
|
|
+
|
|
ath11k_mac_drain_tx(ar);
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
|
--- a/drivers/net/wireless/ath/ath11k/testmode.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
|
|
@@ -12,6 +12,9 @@
|
|
#include "hif.h"
|
|
#include "testmode_i.h"
|
|
|
|
+#define FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)
|
|
+#define FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)
|
|
+
|
|
static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
|
|
[ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 },
|
|
[ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY,
|
|
@@ -63,20 +66,16 @@ void ath11k_fwlog_write(struct ath11k_ba
|
|
|
|
/* Returns true if callee consumes the skb and the skb should be discarded.
|
|
* Returns false if skb is not used. Does not sleep.
|
|
+ * Unsegmented events are handled here. Segments are aggregated in appln layer
|
|
*/
|
|
-bool ath11k_tm_event_wmi(struct ath11k_base *ab, u32 cmd_id,
|
|
- struct sk_buff *skb)
|
|
+bool ath11k_wmi_tm_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
|
|
+ struct sk_buff *skb)
|
|
{
|
|
struct sk_buff *nl_skb;
|
|
+ struct ath11k *ar;
|
|
bool consumed;
|
|
int ret, i;
|
|
- struct ath11k *ar;
|
|
struct ath11k_pdev *pdev;
|
|
- u8 *buf_pos;
|
|
- u16 datalen;
|
|
- struct seg_hdr_info *seghdr_info;
|
|
- u8 total_segments, current_seq;
|
|
- u32 data_pos;
|
|
|
|
ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
|
|
"testmode event wmi cmd_id %d skb %pK skb->len %d\n",
|
|
@@ -87,30 +86,123 @@ bool ath11k_tm_event_wmi(struct ath11k_b
|
|
for (i = 0; i < ab->num_radios; i++) {
|
|
pdev = &ab->pdevs[i];
|
|
ar = pdev->ar;
|
|
- if (ar && ar->state == ATH11K_STATE_TM)
|
|
- break;
|
|
+ if (ar) {
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
+ if (ar->state == ATH11K_STATE_TM) {
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ break;
|
|
+ }
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
+ }
|
|
}
|
|
|
|
if (i >= ab->num_radios) {
|
|
- ath11k_dbg(ab, ATH11K_DBG_TESTMODE, "testmode event not handled\n");
|
|
+ ath11k_warn(ab, "testmode event not handled due to invalid pdev\n");
|
|
return false;
|
|
}
|
|
|
|
spin_lock_bh(&ar->data_lock);
|
|
|
|
+ /* Only testmode.c should be handling events from utf firmware,
|
|
+ * otherwise all sort of problems will arise as mac80211 operations
|
|
+ * are not initialised.
|
|
+ */
|
|
consumed = true;
|
|
|
|
- seghdr_info = (struct seg_hdr_info *)(skb->data + WMI_TLV_HDR_SIZE);
|
|
- current_seq = (seghdr_info->segmentinfo & 0xF);
|
|
- total_segments = (seghdr_info->segmentinfo >> 4) & 0xF;
|
|
+ nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
|
|
+ 2 * sizeof(u32) + skb->len,
|
|
+ GFP_ATOMIC);
|
|
+ if (!nl_skb) {
|
|
+ ath11k_warn(ab,
|
|
+ "failed to allocate skb for testmode wmi event\n");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab,
|
|
+ "failed to put testmode wmi event cmd attribute: %d\n",
|
|
+ ret);
|
|
+ kfree_skb(nl_skb);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab,
|
|
+ "failed to put testmode wmi event cmd_id: %d\n",
|
|
+ ret);
|
|
+ kfree_skb(nl_skb);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data);
|
|
+ if (ret) {
|
|
+ ath11k_warn(ab,
|
|
+ "failed to copy skb to testmode wmi event: %d\n",
|
|
+ ret);
|
|
+ kfree_skb(nl_skb);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
|
|
+
|
|
+out:
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
+ return consumed;
|
|
+}
|
|
+
|
|
+/* Returns true if callee consumes the skb and the skb should be discarded.
|
|
+ * Returns false if skb is not used. Does not sleep.
|
|
+ * Segmented events are hangled here.
|
|
+ * Data of various events received from fw is aggregated and
|
|
+ * sent to application layer
|
|
+ */
|
|
+bool ath11k_process_tm_event(struct ath11k_base *ab, u32 cmd_id,
|
|
+ const struct wmi_ftm_event_msg *ftm_msg,
|
|
+ u16 length)
|
|
+{
|
|
+ struct sk_buff *nl_skb;
|
|
+ bool consumed;
|
|
+ int ret;
|
|
+ struct ath11k *ar;
|
|
+ u8 *buf_pos;
|
|
+ u16 datalen;
|
|
+ u8 total_segments, current_seq;
|
|
+ u32 data_pos;
|
|
+ u32 pdev_id;
|
|
|
|
- datalen = skb->len - (sizeof(struct seg_hdr_info) + WMI_TLV_HDR_SIZE);
|
|
- buf_pos = skb->data + (sizeof(struct seg_hdr_info) + WMI_TLV_HDR_SIZE);
|
|
+ ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
|
|
+ "testmode event wmi cmd_id %d ftm event msg %pK datalen %d\n",
|
|
+ cmd_id, ftm_msg, length);
|
|
+ ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length);
|
|
+ pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id);
|
|
+
|
|
+ if (pdev_id >= ab->num_radios) {
|
|
+ ath11k_warn(ab, "testmode event not handled due to invalid pdev id\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ ar = ab->pdevs[pdev_id].ar;
|
|
+ if (!ar) {
|
|
+ ath11k_warn(ab, "testmode event not handled due to absence of pdev\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
+ consumed = true;
|
|
+ current_seq = FIELD_GET(FTM_SEGHDR_CURRENT_SEQ,
|
|
+ ftm_msg->seg_hdr.segmentinfo);
|
|
+ total_segments = FIELD_GET(FTM_SEGHDR_TOTAL_SEGMENTS,
|
|
+ ftm_msg->seg_hdr.segmentinfo);
|
|
+ datalen = length - (sizeof(struct wmi_ftm_seg_hdr));
|
|
+ buf_pos = (u8 *)ftm_msg->data;
|
|
|
|
if (current_seq == 0) {
|
|
ab->ftm_event_obj.expected_seq = 0;
|
|
ab->ftm_event_obj.data_pos = 0;
|
|
}
|
|
+
|
|
data_pos = ab->ftm_event_obj.data_pos;
|
|
|
|
if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) {
|
|
@@ -125,16 +217,15 @@ bool ath11k_tm_event_wmi(struct ath11k_b
|
|
|
|
if (++ab->ftm_event_obj.expected_seq != total_segments) {
|
|
ab->ftm_event_obj.data_pos = data_pos;
|
|
- ath11k_warn(ab,
|
|
- "parial data received current_seq[%d], total_seg[%d]\n",
|
|
+ ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
|
|
+ "partial data received current_seq[%d], total_seg[%d]\n",
|
|
current_seq, total_segments);
|
|
goto out;
|
|
}
|
|
|
|
- ath11k_warn(ab,
|
|
- "total data length[%d] = [%d]\n",
|
|
- data_pos, seghdr_info->len);
|
|
-
|
|
+ ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
|
|
+ "total data length[%d] = [%d]\n",
|
|
+ data_pos, ftm_msg->seg_hdr.len);
|
|
nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
|
|
2 * sizeof(u32) + data_pos,
|
|
GFP_ATOMIC);
|
|
@@ -144,7 +235,8 @@ bool ath11k_tm_event_wmi(struct ath11k_b
|
|
goto out;
|
|
}
|
|
|
|
- ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI_FTM);
|
|
+ ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD,
|
|
+ ATH11K_TM_CMD_WMI_FTM);
|
|
if (ret) {
|
|
ath11k_warn(ab,
|
|
"failed to put testmode wmi event cmd attribute: %d\n",
|
|
@@ -176,7 +268,6 @@ bool ath11k_tm_event_wmi(struct ath11k_b
|
|
|
|
out:
|
|
spin_unlock_bh(&ar->data_lock);
|
|
-
|
|
return consumed;
|
|
}
|
|
|
|
@@ -217,7 +308,6 @@ static int ath11k_tm_cmd_testmode_start(
|
|
int ret;
|
|
|
|
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, " enter testmode cmd fw start\n");
|
|
-
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
|
if (ar->state == ATH11K_STATE_TM) {
|
|
@@ -231,88 +321,23 @@ static int ath11k_tm_cmd_testmode_start(
|
|
goto err;
|
|
}
|
|
|
|
- /* Firmware already running in FTM mode */
|
|
- if (ar->ab->fw_mode == ATH11K_FIRMWARE_MODE_FTM) {
|
|
- ar->state = ATH11K_STATE_TM;
|
|
- ret = 0;
|
|
- goto err;
|
|
- }
|
|
- ar->ab->ftm_event_obj.eventdata = kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH,
|
|
- GFP_KERNEL);
|
|
+ ar->ab->ftm_event_obj.eventdata =
|
|
+ kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL);
|
|
if (!ar->ab->ftm_event_obj.eventdata) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
- ar->ab->fw_mode = ATH11K_FIRMWARE_MODE_FTM;
|
|
ar->state = ATH11K_STATE_TM;
|
|
+ ar->ftm_msgref = 0;
|
|
mutex_unlock(&ar->conf_mutex);
|
|
- init_completion(&ar->fw_mode_reset);
|
|
-
|
|
- set_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &ar->ab->dev_flags);
|
|
- ath11k_hif_power_down(ar->ab);
|
|
- ath11k_hif_power_up(ar->ab);
|
|
-
|
|
- if (!wait_for_completion_timeout(&ar->fw_mode_reset,
|
|
- FTM_MODE_RESET_TIMEOUT_HZ)) {
|
|
- clear_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &ar->ab->dev_flags);
|
|
- ath11k_warn(ar->ab, "failed to restat the core in ftm mode\n");
|
|
- return 0;
|
|
- }
|
|
- clear_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &ar->ab->dev_flags);
|
|
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, " enter testmode cmd started\n");
|
|
-
|
|
return 0;
|
|
err:
|
|
mutex_unlock(&ar->conf_mutex);
|
|
return ret;
|
|
}
|
|
|
|
-static int ath11k_tm_cmd_testmode_stop(struct ath11k *ar, struct nlattr *tb[])
|
|
-{
|
|
- int ret;
|
|
-
|
|
- ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "Enter testmode cmd fw stop\n");
|
|
-
|
|
- mutex_lock(&ar->conf_mutex);
|
|
-
|
|
- if (ar->state != ATH11K_STATE_TM) {
|
|
- ret = -ENETDOWN;
|
|
- goto out;
|
|
- }
|
|
-
|
|
- /* Firmware not running in FTM mode */
|
|
- if (ar->ab->fw_mode != ATH11K_FIRMWARE_MODE_FTM) {
|
|
- ar->state = ATH11K_STATE_OFF;
|
|
- ret = 0;
|
|
- goto out;
|
|
- }
|
|
-
|
|
- ar->ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
|
|
- mutex_unlock(&ar->conf_mutex);
|
|
- init_completion(&ar->fw_mode_reset);
|
|
-
|
|
- set_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &ar->ab->dev_flags);
|
|
- ath11k_hif_power_down(ar->ab);
|
|
- ath11k_hif_power_up(ar->ab);
|
|
-
|
|
- if (!wait_for_completion_timeout(&ar->fw_mode_reset,
|
|
- FTM_MODE_RESET_TIMEOUT_HZ)) {
|
|
- clear_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &ar->ab->dev_flags);
|
|
- ath11k_warn(ar->ab, "failed to restat the core in ftm mode\n");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- ar->state = ATH11K_STATE_OFF;
|
|
- clear_bit(ATH11K_FLAG_FW_RESTART_FOR_HOST, &ar->ab->dev_flags);
|
|
- kfree(ar->ab->ftm_event_obj.eventdata);
|
|
- ath11k_info(ar->ab, "UTF firmware stopped\n");
|
|
- return 0;
|
|
-out:
|
|
- mutex_unlock(&ar->conf_mutex);
|
|
- return ret;
|
|
-}
|
|
-
|
|
static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[],
|
|
struct ieee80211_vif *vif)
|
|
{
|
|
@@ -326,11 +351,6 @@ static int ath11k_tm_cmd_wmi(struct ath1
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
|
- if (ar->state != ATH11K_STATE_ON) {
|
|
- ret = -ENETDOWN;
|
|
- goto out;
|
|
- }
|
|
-
|
|
if (!tb[ATH11K_TM_ATTR_DATA]) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
@@ -378,7 +398,6 @@ static int ath11k_tm_cmd_wmi(struct ath1
|
|
ret);
|
|
goto out;
|
|
}
|
|
-
|
|
ret = 0;
|
|
|
|
out:
|
|
@@ -390,19 +409,16 @@ static int ath11k_tm_cmd_process_ftm(str
|
|
{
|
|
struct ath11k_pdev_wmi *wmi = ar->wmi;
|
|
struct sk_buff *skb;
|
|
- u32 cmd_id, buf_len;
|
|
+ u32 cmd_id, buf_len, hdr_info;
|
|
int ret;
|
|
void *buf;
|
|
- u8 *cmd;
|
|
/* if buf_len is 0 no data is sent, return error */
|
|
- static u8 msgref = 1;
|
|
u8 segnumber = 0, seginfo;
|
|
u16 chunk_len, total_bytes, num_segments;
|
|
u8 *bufpos;
|
|
- struct seg_hdr_info seg_hdr;
|
|
+ struct wmi_ftm_cmd *ftm_cmd;
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
-
|
|
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "ar->state %d\n", ar->state);
|
|
|
|
if (ar->state != ATH11K_STATE_TM) {
|
|
@@ -418,12 +434,10 @@ static int ath11k_tm_cmd_process_ftm(str
|
|
buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
|
|
buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
|
|
cmd_id = WMI_PDEV_UTF_CMDID;
|
|
-
|
|
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
|
|
"testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
|
|
cmd_id, buf, buf_len);
|
|
ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
|
|
-
|
|
bufpos = buf;
|
|
total_bytes = buf_len;
|
|
num_segments = total_bytes / MAX_WMI_UTF_LEN;
|
|
@@ -437,27 +451,25 @@ static int ath11k_tm_cmd_process_ftm(str
|
|
else
|
|
chunk_len = buf_len;
|
|
|
|
- skb = ath11k_wmi_alloc_skb(wmi->wmi_ab,
|
|
- (chunk_len + sizeof(seg_hdr) +
|
|
- WMI_TLV_HDR_SIZE));
|
|
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len +
|
|
+ sizeof(struct wmi_ftm_cmd)));
|
|
if (!skb) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
- seg_hdr.len = total_bytes;
|
|
- seg_hdr.msgref = msgref;
|
|
- seginfo = ((num_segments << 4) & 0xF0) | (segnumber & 0xF);
|
|
- seg_hdr.segmentinfo = seginfo;
|
|
- seg_hdr.pad = 0;
|
|
+ ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
|
|
+ hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
|
|
+ FIELD_PREP(WMI_TLV_LEN, (chunk_len +
|
|
+ sizeof(struct wmi_ftm_seg_hdr)));
|
|
+ ftm_cmd->tlv_header = hdr_info;
|
|
+ ftm_cmd->seg_hdr.len = total_bytes;
|
|
+ ftm_cmd->seg_hdr.msgref = ar->ftm_msgref;
|
|
+ seginfo = FIELD_PREP(FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
|
|
+ FIELD_PREP(FTM_SEGHDR_CURRENT_SEQ, segnumber);
|
|
+ ftm_cmd->seg_hdr.segmentinfo = seginfo;
|
|
segnumber++;
|
|
-
|
|
- cmd = (uint8_t *)skb->data;
|
|
- WMITLV_SET_HDR(cmd, WMITLV_TAG_BYTE, (chunk_len + sizeof(seg_hdr)));
|
|
- cmd += WMI_TLV_HDR_SIZE;
|
|
- memcpy(cmd, &seg_hdr, sizeof(seg_hdr));
|
|
- memcpy(&cmd[sizeof(seg_hdr)], bufpos, chunk_len);
|
|
-
|
|
+ memcpy(&ftm_cmd->data, bufpos, chunk_len);
|
|
ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
|
|
if (ret) {
|
|
ath11k_warn(ar->ab, "ftm wmi command fail: %d\n", ret);
|
|
@@ -467,6 +479,7 @@ static int ath11k_tm_cmd_process_ftm(str
|
|
buf_len -= chunk_len;
|
|
bufpos += chunk_len;
|
|
}
|
|
+ ++ar->ftm_msgref;
|
|
ret = 0;
|
|
out:
|
|
mutex_unlock(&ar->conf_mutex);
|
|
@@ -477,6 +490,7 @@ int ath11k_tm_cmd(struct ieee80211_hw *h
|
|
void *data, int len)
|
|
{
|
|
struct ath11k *ar = hw->priv;
|
|
+ struct ath11k_base *ab = ar->ab;
|
|
struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1];
|
|
int ret;
|
|
|
|
@@ -489,16 +503,18 @@ int ath11k_tm_cmd(struct ieee80211_hw *h
|
|
return -EINVAL;
|
|
|
|
switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) {
|
|
- case ATH11K_TM_CMD_WMI_FTM:
|
|
- return ath11k_tm_cmd_process_ftm(ar, tb);
|
|
+ case ATH11K_TM_CMD_WMI:
|
|
+ ab->ftm_segment_handler = 0;
|
|
+ return ath11k_tm_cmd_wmi(ar, tb, vif);
|
|
case ATH11K_TM_CMD_TESTMODE_START:
|
|
return ath11k_tm_cmd_testmode_start(ar, tb);
|
|
- case ATH11K_TM_CMD_TESTMODE_STOP:
|
|
- return ath11k_tm_cmd_testmode_stop(ar, tb);
|
|
case ATH11K_TM_CMD_GET_VERSION:
|
|
return ath11k_tm_cmd_get_version(ar, tb);
|
|
- case ATH11K_TM_CMD_WMI_FW_TEST:
|
|
- return ath11k_tm_cmd_wmi(ar, tb, vif);
|
|
+ case ATH11K_TM_CMD_WMI_FTM:
|
|
+ ab->ftm_segment_handler = 1;
|
|
+ return ath11k_tm_cmd_process_ftm(ar, tb);
|
|
+ case ATH11K_TM_CMD_TESTMODE_STOP:
|
|
+ return 0;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
--- a/drivers/net/wireless/ath/ath11k/testmode.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/testmode.h
|
|
@@ -4,7 +4,7 @@
|
|
*/
|
|
|
|
#include "core.h"
|
|
-#include "ahb.h"
|
|
+#include "hif.h"
|
|
|
|
#define MAX_WMI_UTF_LEN 252
|
|
#define WMI_TLV_HDR_SIZE 4
|
|
@@ -23,16 +23,28 @@ struct seg_hdr_info {
|
|
|
|
#ifdef CPTCFG_NL80211_TESTMODE
|
|
|
|
-bool ath11k_tm_event_wmi(struct ath11k_base *ab, u32 cmd_id,
|
|
- struct sk_buff *skb);
|
|
+bool ath11k_wmi_tm_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
|
|
+ struct sk_buff *skb);
|
|
+
|
|
+bool ath11k_process_tm_event(struct ath11k_base *ab, u32 cmd_id,
|
|
+ const struct wmi_ftm_event_msg *ftm_msg,
|
|
+ u16 length);
|
|
+
|
|
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_wmi_tm_event_unsegmented(struct ath11k_base *ab,
|
|
+ u32 cmd_id,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
|
|
-static inline bool ath11k_tm_event_wmi(struct ath11k_base *ab, u32 cmd_id,
|
|
- struct sk_buff *skb)
|
|
+static inline bool ath11k_process_tm_event(struct ath11k_base *ab, u32 cmd_id,
|
|
+ const struct wmi_ftm_event_msg *msg,
|
|
+ u16 length)
|
|
{
|
|
return false;
|
|
}
|
|
--- a/drivers/net/wireless/ath/ath11k/testmode_i.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/testmode_i.h
|
|
@@ -60,7 +60,7 @@ enum ath11k_tm_cmd {
|
|
* provided with ATH11K_TM_ATTR_WMI_CMDID and payload in
|
|
* ATH11K_TM_ATTR_DATA.
|
|
*/
|
|
- ATH11K_TM_CMD_WMI_FW_TEST = 3,
|
|
+ ATH11K_TM_CMD_WMI = 3,
|
|
|
|
/* The command used to transmit a FTM WMI command to the firmware
|
|
* and the event to receive WMI events from the firmware.The data
|
|
--- a/drivers/net/wireless/ath/ath11k/wmi.c
|
|
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
|
|
@@ -7678,6 +7678,37 @@ exit:
|
|
kfree(tb);
|
|
}
|
|
|
|
+static void ath11k_wmi_tm_event_segmented(struct ath11k_base *ab, u32 cmd_id,
|
|
+ struct sk_buff *skb)
|
|
+{
|
|
+ const void **tb;
|
|
+ const struct wmi_ftm_event_msg *ev;
|
|
+ u16 length;
|
|
+ int ret;
|
|
+ bool consumed;
|
|
+
|
|
+ tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
|
|
+ if (IS_ERR(tb)) {
|
|
+ ret = PTR_ERR(tb);
|
|
+ ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ev = tb[WMI_TAG_ARRAY_BYTE];
|
|
+ if (!ev) {
|
|
+ ath11k_warn(ab, "failed to fetch ftm msg\n");
|
|
+ kfree(tb);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ length = skb->len - TLV_HDR_SIZE;
|
|
+ consumed = ath11k_process_tm_event(ab, cmd_id, ev, length);
|
|
+ if (!consumed)
|
|
+ ath11k_warn(ab, "Failed to process ftm event\n");
|
|
+
|
|
+ kfree(tb);
|
|
+}
|
|
+
|
|
static void
|
|
ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab,
|
|
struct sk_buff *skb)
|
|
@@ -8520,7 +8551,10 @@ static void ath11k_wmi_tlv_op_rx(struct
|
|
ath11k_probe_resp_tx_status_event(ab, skb);
|
|
break;
|
|
case WMI_PDEV_UTF_EVENTID:
|
|
- ath11k_tm_event_wmi(ab, id, skb);
|
|
+ if (ab->ftm_segment_handler)
|
|
+ ath11k_wmi_tm_event_segmented(ab, id, skb);
|
|
+ else
|
|
+ ath11k_wmi_tm_event_unsegmented(ab, id, skb);
|
|
break;
|
|
case WMI_TWT_ADD_DIALOG_EVENTID:
|
|
ath11k_wmi_twt_add_dialog_event(ab, skb);
|
|
--- a/drivers/net/wireless/ath/ath11k/wmi.h
|
|
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
|
|
@@ -3637,6 +3637,26 @@ struct wmi_get_pdev_temperature_cmd {
|
|
u32 pdev_id;
|
|
} __packed;
|
|
|
|
+#define MAX_WMI_UTF_LEN 252
|
|
+
|
|
+struct wmi_ftm_seg_hdr {
|
|
+ u32 len;
|
|
+ u32 msgref;
|
|
+ u32 segmentinfo;
|
|
+ u32 pdev_id;
|
|
+} __packed;
|
|
+
|
|
+struct wmi_ftm_cmd {
|
|
+ u32 tlv_header;
|
|
+ struct wmi_ftm_seg_hdr seg_hdr;
|
|
+ u8 data[];
|
|
+};
|
|
+
|
|
+struct wmi_ftm_event_msg {
|
|
+ struct wmi_ftm_seg_hdr seg_hdr;
|
|
+ u8 data[];
|
|
+};
|
|
+
|
|
#define WMI_BEACON_TX_BUFFER_SIZE 512
|
|
|
|
struct wmi_bcn_tmpl_cmd {
|