wlan-ap-Telecominfraproject/feeds/wifi-ax/mac80211/patches/qca/017-ath11k-factory-test-mode-support.patch
John Crispin 8cd26b4b50 ipq807x: update to 11.4-CS
Signed-off-by: John Crispin <john@phrozen.org>
2021-09-14 09:16:23 +02:00

585 lines
16 KiB
Diff

--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -693,6 +693,7 @@ static int ath11k_ahb_probe(struct platf
ab->hif.ops = &ath11k_ahb_hif_ops;
ab->pdev = pdev;
ab->hw_rev = (enum ath11k_hw_rev)of_id->data;
+ ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL;
ab->mem = mem;
ab->mem_len = resource_size(mem_res);
platform_set_drvdata(pdev, ab);
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -837,7 +837,7 @@ int ath11k_core_qmi_firmware_ready(struc
set_bit(ATH11K_FLAG_RAW_MODE, &ab->dev_flags);
mutex_lock(&ab->core_lock);
- ret = ath11k_core_start(ab, ATH11K_FIRMWARE_MODE_NORMAL);
+ ret = ath11k_core_start(ab, ab->fw_mode);
if (ret) {
ath11k_err(ab, "failed to start core: %d\n", ret);
goto err_dp_free;
@@ -934,7 +934,8 @@ static void ath11k_core_restart(struct w
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
- if (!ar || ar->state == ATH11K_STATE_OFF)
+ if (!ar || ar->state == ATH11K_STATE_OFF ||
+ ar->state == ATH11K_STATE_TM)
continue;
ieee80211_stop_queues(ar->hw);
@@ -992,6 +993,12 @@ static void ath11k_core_restart(struct w
ath11k_warn(ab,
"device is wedged, will not restart radio %d\n", i);
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
@@ -189,6 +189,7 @@ enum ath11k_dev_flags {
ATH11K_FLAG_REGISTERED,
ATH11K_FLAG_QMI_FAIL,
ATH11K_FLAG_HTC_SUSPEND_COMPLETE,
+ ATH11K_FLAG_FW_RESTART_FOR_HOST,
};
enum ath11k_monitor_flags {
@@ -386,12 +387,19 @@ enum ath11k_state {
ATH11K_STATE_RESTARTING,
ATH11K_STATE_RESTARTED,
ATH11K_STATE_WEDGED,
+ ATH11K_STATE_TM,
/* Add other states as required */
};
/* Antenna noise floor */
#define ATH11K_DEFAULT_NOISE_FLOOR -95
+struct ath11k_ftm_event_obj {
+ u32 data_pos;
+ u32 expected_seq;
+ u8 *eventdata;
+};
+
struct ath11k_fw_stats {
struct dentry *debugfs_fwstats;
u32 pdev_id;
@@ -570,6 +578,7 @@ struct ath11k {
#endif
bool dfs_block_radar_events;
struct ath11k_thermal thermal;
+ struct completion fw_mode_reset;
};
struct ath11k_band_cap {
@@ -661,6 +670,7 @@ struct ath11k_soc_dp_stats {
/* Master structure to hold the hw data which may be used in core module */
struct ath11k_base {
enum ath11k_hw_rev hw_rev;
+ enum ath11k_firmware_mode fw_mode;
struct platform_device *pdev;
struct device *dev;
struct ath11k_qmi qmi;
@@ -741,6 +751,7 @@ struct ath11k_base {
/* protected by data_lock */
u32 fw_crash_counter;
} stats;
+ struct ath11k_ftm_event_obj ftm_event_obj;
u32 pktlog_defs_checksum;
struct ath11k_dbring_cap *db_caps;
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -528,7 +528,10 @@ struct ath11k *ath11k_mac_get_ar_by_pdev
return NULL;
for (i = 0; i < ab->num_radios; i++) {
- pdev = rcu_dereference(ab->pdevs_active[i]);
+ if (ab->fw_mode == ATH11K_FIRMWARE_MODE_FTM)
+ pdev = &ab->pdevs[i];
+ else
+ pdev = rcu_dereference(ab->pdevs_active[i]);
if (pdev && pdev->pdev_id == pdev_id)
return (pdev->ar ? pdev->ar : NULL);
@@ -4326,6 +4329,7 @@ static int ath11k_mac_op_start(struct ie
case ATH11K_STATE_RESTARTED:
case ATH11K_STATE_WEDGED:
case ATH11K_STATE_ON:
+ case ATH11K_STATE_TM:
WARN_ON(1);
ret = -EINVAL;
goto err;
--- a/drivers/net/wireless/ath/ath11k/testmode.c
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
@@ -9,6 +9,7 @@
#include "wmi.h"
#include "hw.h"
#include "core.h"
+#include "hif.h"
#include "testmode_i.h"
static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
@@ -23,34 +24,89 @@ static const struct nla_policy ath11k_tm
/* Returns true if callee consumes the skb and the skb should be discarded.
* Returns false if skb is not used. Does not sleep.
*/
-bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb)
+bool ath11k_tm_event_wmi(struct ath11k_base *ab, u32 cmd_id,
+ struct sk_buff *skb)
{
struct sk_buff *nl_skb;
bool consumed;
- int ret;
+ 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(ar->ab, ATH11K_DBG_TESTMODE,
+ ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
"testmode event wmi cmd_id %d skb %pK skb->len %d\n",
cmd_id, skb, skb->len);
- ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
+ ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
+
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = &ab->pdevs[i];
+ ar = pdev->ar;
+ if (ar && ar->state == ATH11K_STATE_TM)
+ break;
+ }
+
+ if (i >= ab->num_radios) {
+ ath11k_dbg(ab, ATH11K_DBG_TESTMODE, "testmode event not handled\n");
+ return false;
+ }
spin_lock_bh(&ar->data_lock);
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;
+
+ 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);
+
+ 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) {
+ ath11k_warn(ab,
+ "Invalid event length date_pos[%d] datalen[%d]\n",
+ data_pos, datalen);
+ goto out;
+ }
+
+ memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen);
+ data_pos += datalen;
+
+ 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",
+ current_seq, total_segments);
+ goto out;
+ }
+
+ ath11k_warn(ab,
+ "total data length[%d] = [%d]\n",
+ data_pos, seghdr_info->len);
+
nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
- 2 * sizeof(u32) + skb->len,
+ 2 * sizeof(u32) + data_pos,
GFP_ATOMIC);
if (!nl_skb) {
- ath11k_warn(ar->ab,
+ 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);
+ ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI_FTM);
if (ret) {
- ath11k_warn(ar->ab,
+ ath11k_warn(ab,
"failed to put testmode wmi event cmd attribute: %d\n",
ret);
kfree_skb(nl_skb);
@@ -59,16 +115,17 @@ bool ath11k_tm_event_wmi(struct ath11k *
ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id);
if (ret) {
- ath11k_warn(ar->ab,
+ ath11k_warn(ab,
"failed to put testmode wmi even cmd_id: %d\n",
ret);
kfree_skb(nl_skb);
goto out;
}
- ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data);
+ ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos,
+ &ab->ftm_event_obj.eventdata[0]);
if (ret) {
- ath11k_warn(ar->ab,
+ ath11k_warn(ab,
"failed to copy skb to testmode wmi event: %d\n",
ret);
kfree_skb(nl_skb);
@@ -115,6 +172,107 @@ static int ath11k_tm_cmd_get_version(str
return cfg80211_testmode_reply(skb);
}
+static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[])
+{
+ 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) {
+ ret = -EALREADY;
+ goto err;
+ }
+
+ /* start utf only when the driver is not in use */
+ if (ar->state != ATH11K_STATE_OFF) {
+ ret = -EBUSY;
+ 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);
+ if (!ar->ab->ftm_event_obj.eventdata) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ar->ab->fw_mode = ATH11K_FIRMWARE_MODE_FTM;
+ ar->state = ATH11K_STATE_TM;
+ 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 ath11k_pdev_wmi *wmi = ar->wmi;
@@ -173,8 +331,95 @@ out:
return ret;
}
+static int ath11k_tm_cmd_process_ftm(struct ath11k *ar, struct nlattr *tb[])
+{
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
+ struct sk_buff *skb;
+ u32 cmd_id, buf_len;
+ 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;
+
+ mutex_lock(&ar->conf_mutex);
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "ar->state %d\n", ar->state);
+
+ if (ar->state != ATH11K_STATE_TM) {
+ ret = -ENETDOWN;
+ goto out;
+ }
+
+ if (!tb[ATH11K_TM_ATTR_DATA]) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ 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;
+
+ if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
+ num_segments++;
+
+ while (buf_len) {
+ if (buf_len > MAX_WMI_UTF_LEN)
+ chunk_len = MAX_WMI_UTF_LEN; /* MAX message */
+ else
+ chunk_len = buf_len;
+
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_ab,
+ (chunk_len + sizeof(seg_hdr) +
+ WMI_TLV_HDR_SIZE));
+ 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;
+ 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);
+
+ ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
+ if (ret) {
+ ath11k_warn(ar->ab, "ftm wmi command fail: %d\n", ret);
+ goto out;
+ }
+
+ buf_len -= chunk_len;
+ bufpos += chunk_len;
+ }
+ ret = 0;
+out:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- void *data, int len)
+ void *data, int len)
{
struct ath11k *ar = hw->priv;
struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1];
@@ -189,9 +434,15 @@ 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_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:
+ case ATH11K_TM_CMD_WMI_FW_TEST:
return ath11k_tm_cmd_wmi(ar, tb);
default:
return -EOPNOTSUPP;
--- a/drivers/net/wireless/ath/ath11k/testmode.h
+++ b/drivers/net/wireless/ath/ath11k/testmode.h
@@ -4,16 +4,33 @@
*/
#include "core.h"
+#include "ahb.h"
+
+#define MAX_WMI_UTF_LEN 252
+#define WMI_TLV_HDR_SIZE 4
+#define WMITLV_TAG_BYTE 17
+#define FTM_MODE_RESET_TIMEOUT_HZ (10 * HZ)
+#define WMITLV_SET_HDR(tlv_buf, tag, len) ({ \
+ (((u32 *)(tlv_buf))[0]) = (((tag) << 16) | ((len) & 0x0000FFFF)); \
+ })
+
+struct seg_hdr_info {
+ u32 len;
+ u32 msgref;
+ u32 segmentinfo;
+ u32 pad;
+};
#ifdef CPTCFG_NL80211_TESTMODE
-bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb);
+bool ath11k_tm_event_wmi(struct ath11k_base *ab, u32 cmd_id,
+ struct sk_buff *skb);
int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
#else
-static inline bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id,
+static inline bool ath11k_tm_event_wmi(struct ath11k_base *ab, u32 cmd_id,
struct sk_buff *skb)
{
return false;
--- a/drivers/net/wireless/ath/ath11k/testmode_i.h
+++ b/drivers/net/wireless/ath/ath11k/testmode_i.h
@@ -14,6 +14,7 @@
#define ATH11K_TESTMODE_VERSION_MINOR 0
#define ATH11K_TM_DATA_MAX_LEN 5000
+#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048
enum ath11k_tm_attr {
__ATH11K_TM_ATTR_INVALID = 0,
@@ -40,11 +41,29 @@ enum ath11k_tm_cmd {
*/
ATH11K_TM_CMD_GET_VERSION = 0,
- /* The command used to transmit a WMI command to the firmware and
- * the event to receive WMI events from the firmware. Without
+ /* Boots the UTF firmware, the netdev interface must be down at the
+ * time.
+ */
+ ATH11K_TM_CMD_TESTMODE_START = 1,
+
+ /* Shuts down the UTF firmware and puts the driver back into OFF
+ * state.
+ */
+ ATH11K_TM_CMD_TESTMODE_STOP = 2,
+
+ /* The command used to transmit a FW test WMI command to the firmware
+ * and the event to receive WMI events from the firmware. Without
* struct wmi_cmd_hdr header, only the WMI payload. Command id is
* provided with ATH11K_TM_ATTR_WMI_CMDID and payload in
* ATH11K_TM_ATTR_DATA.
*/
- ATH11K_TM_CMD_WMI = 1,
+ ATH11K_TM_CMD_WMI_FW_TEST = 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
+ * received only contain the payload, Need to add the tlv
+ * header and send the cmd to fw with commandid WMI_PDEV_UTF_CMDID.
+ */
+ ATH11K_TM_CMD_WMI_FTM = 4,
+
};
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -18,6 +18,7 @@
#include "mac.h"
#include "hw.h"
#include "peer.h"
+#include "testmode.h"
struct wmi_tlv_policy {
size_t min_len;
@@ -7077,6 +7078,9 @@ static void ath11k_wmi_tlv_op_rx(struct
case WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID:
ath11k_probe_resp_tx_status_event(ab, skb);
break;
+ case WMI_PDEV_UTF_EVENTID:
+ ath11k_tm_event_wmi(ab, id, skb);
+ break;
/* add Unsupported events here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
--- a/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs_htt_stats.c
@@ -4469,7 +4469,8 @@ static int ath11k_open_htt_stats(struct
mutex_lock(&ar->conf_mutex);
- if (ar->state != ATH11K_STATE_ON) {
+ if (ar->state != ATH11K_STATE_ON &&
+ ar->ab->fw_mode != ATH11K_FIRMWARE_MODE_FTM) {
ret = -ENETDOWN;
goto err_unlock;
}