1f0553ca9SKalle Valo // SPDX-License-Identifier: ISC
243d2a30fSKalle Valo /*
38b1083d6SKalle Valo * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
443d2a30fSKalle Valo */
543d2a30fSKalle Valo
643d2a30fSKalle Valo #include "testmode.h"
743d2a30fSKalle Valo
843d2a30fSKalle Valo #include <net/netlink.h>
943d2a30fSKalle Valo #include <linux/firmware.h>
1043d2a30fSKalle Valo
1143d2a30fSKalle Valo #include "debug.h"
1243d2a30fSKalle Valo #include "wmi.h"
1343d2a30fSKalle Valo #include "hif.h"
1443d2a30fSKalle Valo #include "hw.h"
15ebce1a5eSTamizh chelvam #include "core.h"
1643d2a30fSKalle Valo
1743d2a30fSKalle Valo #include "testmode_i.h"
1843d2a30fSKalle Valo
1943d2a30fSKalle Valo static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
2043d2a30fSKalle Valo [ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 },
2143d2a30fSKalle Valo [ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY,
2243d2a30fSKalle Valo .len = ATH10K_TM_DATA_MAX_LEN },
2343d2a30fSKalle Valo [ATH10K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
2443d2a30fSKalle Valo [ATH10K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
2543d2a30fSKalle Valo [ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
2643d2a30fSKalle Valo };
2743d2a30fSKalle Valo
2843d2a30fSKalle Valo /* Returns true if callee consumes the skb and the skb should be discarded.
2943d2a30fSKalle Valo * Returns false if skb is not used. Does not sleep.
3043d2a30fSKalle Valo */
ath10k_tm_event_wmi(struct ath10k * ar,u32 cmd_id,struct sk_buff * skb)3143d2a30fSKalle Valo bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
3243d2a30fSKalle Valo {
3343d2a30fSKalle Valo struct sk_buff *nl_skb;
3443d2a30fSKalle Valo bool consumed;
3543d2a30fSKalle Valo int ret;
3643d2a30fSKalle Valo
3743d2a30fSKalle Valo ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
3875b34800SMaharaja Kennadyrajan "testmode event wmi cmd_id %d skb %pK skb->len %d\n",
3943d2a30fSKalle Valo cmd_id, skb, skb->len);
4043d2a30fSKalle Valo
4143d2a30fSKalle Valo ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
4243d2a30fSKalle Valo
4343d2a30fSKalle Valo spin_lock_bh(&ar->data_lock);
4443d2a30fSKalle Valo
4543d2a30fSKalle Valo if (!ar->testmode.utf_monitor) {
4643d2a30fSKalle Valo consumed = false;
4743d2a30fSKalle Valo goto out;
4843d2a30fSKalle Valo }
4943d2a30fSKalle Valo
5043d2a30fSKalle Valo /* Only testmode.c should be handling events from utf firmware,
5143d2a30fSKalle Valo * otherwise all sort of problems will arise as mac80211 operations
5243d2a30fSKalle Valo * are not initialised.
5343d2a30fSKalle Valo */
5443d2a30fSKalle Valo consumed = true;
5543d2a30fSKalle Valo
5643d2a30fSKalle Valo nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
5743d2a30fSKalle Valo 2 * sizeof(u32) + skb->len,
5843d2a30fSKalle Valo GFP_ATOMIC);
5943d2a30fSKalle Valo if (!nl_skb) {
6043d2a30fSKalle Valo ath10k_warn(ar,
6143d2a30fSKalle Valo "failed to allocate skb for testmode wmi event\n");
6243d2a30fSKalle Valo goto out;
6343d2a30fSKalle Valo }
6443d2a30fSKalle Valo
6543d2a30fSKalle Valo ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
6643d2a30fSKalle Valo if (ret) {
6743d2a30fSKalle Valo ath10k_warn(ar,
68*a67bcec3SChristophe JAILLET "failed to put testmode wmi event cmd attribute: %d\n",
6943d2a30fSKalle Valo ret);
7043d2a30fSKalle Valo kfree_skb(nl_skb);
7143d2a30fSKalle Valo goto out;
7243d2a30fSKalle Valo }
7343d2a30fSKalle Valo
7443d2a30fSKalle Valo ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
7543d2a30fSKalle Valo if (ret) {
7643d2a30fSKalle Valo ath10k_warn(ar,
77*a67bcec3SChristophe JAILLET "failed to put testmode wmi event cmd_id: %d\n",
7843d2a30fSKalle Valo ret);
7943d2a30fSKalle Valo kfree_skb(nl_skb);
8043d2a30fSKalle Valo goto out;
8143d2a30fSKalle Valo }
8243d2a30fSKalle Valo
8343d2a30fSKalle Valo ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
8443d2a30fSKalle Valo if (ret) {
8543d2a30fSKalle Valo ath10k_warn(ar,
8643d2a30fSKalle Valo "failed to copy skb to testmode wmi event: %d\n",
8743d2a30fSKalle Valo ret);
8843d2a30fSKalle Valo kfree_skb(nl_skb);
8943d2a30fSKalle Valo goto out;
9043d2a30fSKalle Valo }
9143d2a30fSKalle Valo
9243d2a30fSKalle Valo cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
9343d2a30fSKalle Valo
9443d2a30fSKalle Valo out:
9543d2a30fSKalle Valo spin_unlock_bh(&ar->data_lock);
9643d2a30fSKalle Valo
9743d2a30fSKalle Valo return consumed;
9843d2a30fSKalle Valo }
9943d2a30fSKalle Valo
ath10k_tm_cmd_get_version(struct ath10k * ar,struct nlattr * tb[])10043d2a30fSKalle Valo static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])
10143d2a30fSKalle Valo {
10243d2a30fSKalle Valo struct sk_buff *skb;
10343d2a30fSKalle Valo int ret;
10443d2a30fSKalle Valo
10543d2a30fSKalle Valo ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
10643d2a30fSKalle Valo "testmode cmd get version_major %d version_minor %d\n",
10743d2a30fSKalle Valo ATH10K_TESTMODE_VERSION_MAJOR,
10843d2a30fSKalle Valo ATH10K_TESTMODE_VERSION_MINOR);
10943d2a30fSKalle Valo
11043d2a30fSKalle Valo skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
11143d2a30fSKalle Valo nla_total_size(sizeof(u32)));
11243d2a30fSKalle Valo if (!skb)
11343d2a30fSKalle Valo return -ENOMEM;
11443d2a30fSKalle Valo
11543d2a30fSKalle Valo ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,
11643d2a30fSKalle Valo ATH10K_TESTMODE_VERSION_MAJOR);
11743d2a30fSKalle Valo if (ret) {
11843d2a30fSKalle Valo kfree_skb(skb);
11943d2a30fSKalle Valo return ret;
12043d2a30fSKalle Valo }
12143d2a30fSKalle Valo
12243d2a30fSKalle Valo ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,
12343d2a30fSKalle Valo ATH10K_TESTMODE_VERSION_MINOR);
12443d2a30fSKalle Valo if (ret) {
12543d2a30fSKalle Valo kfree_skb(skb);
12643d2a30fSKalle Valo return ret;
12743d2a30fSKalle Valo }
12843d2a30fSKalle Valo
1294dcb7808SRyan Hsu ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,
1304dcb7808SRyan Hsu ar->normal_mode_fw.fw_file.wmi_op_version);
1314dcb7808SRyan Hsu if (ret) {
1324dcb7808SRyan Hsu kfree_skb(skb);
1334dcb7808SRyan Hsu return ret;
1344dcb7808SRyan Hsu }
1354dcb7808SRyan Hsu
13643d2a30fSKalle Valo return cfg80211_testmode_reply(skb);
13743d2a30fSKalle Valo }
13843d2a30fSKalle Valo
ath10k_tm_fetch_utf_firmware_api_1(struct ath10k * ar,struct ath10k_fw_file * fw_file)1397ebf721dSKalle Valo static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,
1407ebf721dSKalle Valo struct ath10k_fw_file *fw_file)
14143d2a30fSKalle Valo {
14243d2a30fSKalle Valo char filename[100];
14343d2a30fSKalle Valo int ret;
14443d2a30fSKalle Valo
145a81a98ceSAlan Liu snprintf(filename, sizeof(filename), "%s/%s",
146a81a98ceSAlan Liu ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
147a81a98ceSAlan Liu
148a81a98ceSAlan Liu /* load utf firmware image */
1491bc4d68bSAndres Rodriguez ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);
1509f5bcfe9SMichal Kazior ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",
1519f5bcfe9SMichal Kazior filename, ret);
1529f5bcfe9SMichal Kazior
153a81a98ceSAlan Liu if (ret) {
154a81a98ceSAlan Liu ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
155a81a98ceSAlan Liu filename, ret);
156a81a98ceSAlan Liu return ret;
157a81a98ceSAlan Liu }
158a81a98ceSAlan Liu
159a81a98ceSAlan Liu /* We didn't find FW UTF API 1 ("utf.bin") does not advertise
160a81a98ceSAlan Liu * firmware features. Do an ugly hack where we force the firmware
161a81a98ceSAlan Liu * features to match with 10.1 branch so that wmi.c will use the
162a81a98ceSAlan Liu * correct WMI interface.
163a81a98ceSAlan Liu */
164a81a98ceSAlan Liu
165bf3c13abSKalle Valo fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
16677561f93SKalle Valo fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;
1677ebf721dSKalle Valo fw_file->firmware_data = fw_file->firmware->data;
1687ebf721dSKalle Valo fw_file->firmware_len = fw_file->firmware->size;
169a81a98ceSAlan Liu
170a81a98ceSAlan Liu return 0;
171a81a98ceSAlan Liu }
172a81a98ceSAlan Liu
ath10k_tm_fetch_firmware(struct ath10k * ar)173a81a98ceSAlan Liu static int ath10k_tm_fetch_firmware(struct ath10k *ar)
174a81a98ceSAlan Liu {
1757ebf721dSKalle Valo struct ath10k_fw_components *utf_mode_fw;
176a81a98ceSAlan Liu int ret;
17754f6643bSWen Gong char fw_name[100];
17854f6643bSWen Gong int fw_api2 = 2;
179a81a98ceSAlan Liu
18054f6643bSWen Gong switch (ar->hif.bus) {
18154f6643bSWen Gong case ATH10K_BUS_SDIO:
18254f6643bSWen Gong case ATH10K_BUS_USB:
18354f6643bSWen Gong scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",
18454f6643bSWen Gong ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),
18554f6643bSWen Gong fw_api2);
18654f6643bSWen Gong break;
18754f6643bSWen Gong default:
18854f6643bSWen Gong scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",
18954f6643bSWen Gong ATH10K_FW_UTF_FILE_BASE, fw_api2);
19054f6643bSWen Gong break;
19154f6643bSWen Gong }
19254f6643bSWen Gong
19354f6643bSWen Gong ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,
1949dfe240bSKalle Valo &ar->testmode.utf_mode_fw.fw_file);
195a81a98ceSAlan Liu if (ret == 0) {
196a81a98ceSAlan Liu ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");
1977ebf721dSKalle Valo goto out;
198a81a98ceSAlan Liu }
199a81a98ceSAlan Liu
2007ebf721dSKalle Valo ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);
201a81a98ceSAlan Liu if (ret) {
202a81a98ceSAlan Liu ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);
203a81a98ceSAlan Liu return ret;
204a81a98ceSAlan Liu }
205a81a98ceSAlan Liu
206a81a98ceSAlan Liu ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");
207a81a98ceSAlan Liu
2087ebf721dSKalle Valo out:
2097ebf721dSKalle Valo utf_mode_fw = &ar->testmode.utf_mode_fw;
2107ebf721dSKalle Valo
2117ebf721dSKalle Valo /* Use the same board data file as the normal firmware uses (but
2127ebf721dSKalle Valo * it's still "owned" by normal_mode_fw so we shouldn't free it.
2137ebf721dSKalle Valo */
2147ebf721dSKalle Valo utf_mode_fw->board_data = ar->normal_mode_fw.board_data;
2157ebf721dSKalle Valo utf_mode_fw->board_len = ar->normal_mode_fw.board_len;
2167ebf721dSKalle Valo
2177ebf721dSKalle Valo if (!utf_mode_fw->fw_file.otp_data) {
2187ebf721dSKalle Valo ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");
2197ebf721dSKalle Valo utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;
2207ebf721dSKalle Valo utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;
2217ebf721dSKalle Valo }
2227ebf721dSKalle Valo
223a81a98ceSAlan Liu return 0;
224a81a98ceSAlan Liu }
225a81a98ceSAlan Liu
ath10k_tm_cmd_utf_start(struct ath10k * ar,struct nlattr * tb[])226a81a98ceSAlan Liu static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
227a81a98ceSAlan Liu {
228a81a98ceSAlan Liu const char *ver;
229a81a98ceSAlan Liu int ret;
230a81a98ceSAlan Liu
23143d2a30fSKalle Valo ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");
23243d2a30fSKalle Valo
23343d2a30fSKalle Valo mutex_lock(&ar->conf_mutex);
23443d2a30fSKalle Valo
23543d2a30fSKalle Valo if (ar->state == ATH10K_STATE_UTF) {
23643d2a30fSKalle Valo ret = -EALREADY;
23743d2a30fSKalle Valo goto err;
23843d2a30fSKalle Valo }
23943d2a30fSKalle Valo
24043d2a30fSKalle Valo /* start utf only when the driver is not in use */
24143d2a30fSKalle Valo if (ar->state != ATH10K_STATE_OFF) {
24243d2a30fSKalle Valo ret = -EBUSY;
24343d2a30fSKalle Valo goto err;
24443d2a30fSKalle Valo }
24543d2a30fSKalle Valo
2467ebf721dSKalle Valo if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {
24743d2a30fSKalle Valo /* utf image is already downloaded, it shouldn't be */
24843d2a30fSKalle Valo ret = -EEXIST;
24943d2a30fSKalle Valo goto err;
25043d2a30fSKalle Valo }
25143d2a30fSKalle Valo
252a81a98ceSAlan Liu ret = ath10k_tm_fetch_firmware(ar);
25343d2a30fSKalle Valo if (ret) {
254a81a98ceSAlan Liu ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);
25543d2a30fSKalle Valo goto err;
25643d2a30fSKalle Valo }
25743d2a30fSKalle Valo
258ebce1a5eSTamizh chelvam if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
259ebce1a5eSTamizh chelvam ar->testmode.utf_mode_fw.fw_file.codeswap_len) {
260ebce1a5eSTamizh chelvam ret = ath10k_swap_code_seg_init(ar,
261ebce1a5eSTamizh chelvam &ar->testmode.utf_mode_fw.fw_file);
262ebce1a5eSTamizh chelvam if (ret) {
263ebce1a5eSTamizh chelvam ath10k_warn(ar,
264ebce1a5eSTamizh chelvam "failed to init utf code swap segment: %d\n",
265ebce1a5eSTamizh chelvam ret);
266ebce1a5eSTamizh chelvam goto err_release_utf_mode_fw;
267ebce1a5eSTamizh chelvam }
268ebce1a5eSTamizh chelvam }
269ebce1a5eSTamizh chelvam
27043d2a30fSKalle Valo spin_lock_bh(&ar->data_lock);
27143d2a30fSKalle Valo ar->testmode.utf_monitor = true;
27243d2a30fSKalle Valo spin_unlock_bh(&ar->data_lock);
27343d2a30fSKalle Valo
274a81a98ceSAlan Liu ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",
275bf3c13abSKalle Valo ar->testmode.utf_mode_fw.fw_file.wmi_op_version);
27643d2a30fSKalle Valo
2773c545a25SRakesh Pillai ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);
27843d2a30fSKalle Valo if (ret) {
27943d2a30fSKalle Valo ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);
28043d2a30fSKalle Valo ar->state = ATH10K_STATE_OFF;
281bf3c13abSKalle Valo goto err_release_utf_mode_fw;
28243d2a30fSKalle Valo }
28343d2a30fSKalle Valo
2847ebf721dSKalle Valo ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
2857ebf721dSKalle Valo &ar->testmode.utf_mode_fw);
28643d2a30fSKalle Valo if (ret) {
28743d2a30fSKalle Valo ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
28843d2a30fSKalle Valo ar->state = ATH10K_STATE_OFF;
28943d2a30fSKalle Valo goto err_power_down;
29043d2a30fSKalle Valo }
29143d2a30fSKalle Valo
29243d2a30fSKalle Valo ar->state = ATH10K_STATE_UTF;
29343d2a30fSKalle Valo
29445317355SKalle Valo if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)
29545317355SKalle Valo ver = ar->testmode.utf_mode_fw.fw_file.fw_version;
296a81a98ceSAlan Liu else
297a81a98ceSAlan Liu ver = "API 1";
298a81a98ceSAlan Liu
299a81a98ceSAlan Liu ath10k_info(ar, "UTF firmware %s started\n", ver);
30043d2a30fSKalle Valo
30143d2a30fSKalle Valo mutex_unlock(&ar->conf_mutex);
30243d2a30fSKalle Valo
30343d2a30fSKalle Valo return 0;
30443d2a30fSKalle Valo
30543d2a30fSKalle Valo err_power_down:
30643d2a30fSKalle Valo ath10k_hif_power_down(ar);
30743d2a30fSKalle Valo
308bf3c13abSKalle Valo err_release_utf_mode_fw:
309ebce1a5eSTamizh chelvam if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
310ebce1a5eSTamizh chelvam ar->testmode.utf_mode_fw.fw_file.codeswap_len)
311ebce1a5eSTamizh chelvam ath10k_swap_code_seg_release(ar,
312ebce1a5eSTamizh chelvam &ar->testmode.utf_mode_fw.fw_file);
313ebce1a5eSTamizh chelvam
3147ebf721dSKalle Valo release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
3157ebf721dSKalle Valo ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
31643d2a30fSKalle Valo
31743d2a30fSKalle Valo err:
31843d2a30fSKalle Valo mutex_unlock(&ar->conf_mutex);
31943d2a30fSKalle Valo
32043d2a30fSKalle Valo return ret;
32143d2a30fSKalle Valo }
32243d2a30fSKalle Valo
__ath10k_tm_cmd_utf_stop(struct ath10k * ar)32343d2a30fSKalle Valo static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
32443d2a30fSKalle Valo {
32543d2a30fSKalle Valo lockdep_assert_held(&ar->conf_mutex);
32643d2a30fSKalle Valo
32743d2a30fSKalle Valo ath10k_core_stop(ar);
32843d2a30fSKalle Valo ath10k_hif_power_down(ar);
32943d2a30fSKalle Valo
33043d2a30fSKalle Valo spin_lock_bh(&ar->data_lock);
33143d2a30fSKalle Valo
33243d2a30fSKalle Valo ar->testmode.utf_monitor = false;
33343d2a30fSKalle Valo
33443d2a30fSKalle Valo spin_unlock_bh(&ar->data_lock);
33543d2a30fSKalle Valo
336ebce1a5eSTamizh chelvam if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
337ebce1a5eSTamizh chelvam ar->testmode.utf_mode_fw.fw_file.codeswap_len)
338ebce1a5eSTamizh chelvam ath10k_swap_code_seg_release(ar,
339ebce1a5eSTamizh chelvam &ar->testmode.utf_mode_fw.fw_file);
340ebce1a5eSTamizh chelvam
3417ebf721dSKalle Valo release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
3427ebf721dSKalle Valo ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
34343d2a30fSKalle Valo
34443d2a30fSKalle Valo ar->state = ATH10K_STATE_OFF;
34543d2a30fSKalle Valo }
34643d2a30fSKalle Valo
ath10k_tm_cmd_utf_stop(struct ath10k * ar,struct nlattr * tb[])34743d2a30fSKalle Valo static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])
34843d2a30fSKalle Valo {
34943d2a30fSKalle Valo int ret;
35043d2a30fSKalle Valo
35143d2a30fSKalle Valo ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");
35243d2a30fSKalle Valo
35343d2a30fSKalle Valo mutex_lock(&ar->conf_mutex);
35443d2a30fSKalle Valo
35543d2a30fSKalle Valo if (ar->state != ATH10K_STATE_UTF) {
35643d2a30fSKalle Valo ret = -ENETDOWN;
35743d2a30fSKalle Valo goto out;
35843d2a30fSKalle Valo }
35943d2a30fSKalle Valo
36043d2a30fSKalle Valo __ath10k_tm_cmd_utf_stop(ar);
36143d2a30fSKalle Valo
36243d2a30fSKalle Valo ret = 0;
36343d2a30fSKalle Valo
36443d2a30fSKalle Valo ath10k_info(ar, "UTF firmware stopped\n");
36543d2a30fSKalle Valo
36643d2a30fSKalle Valo out:
36743d2a30fSKalle Valo mutex_unlock(&ar->conf_mutex);
36843d2a30fSKalle Valo return ret;
36943d2a30fSKalle Valo }
37043d2a30fSKalle Valo
ath10k_tm_cmd_wmi(struct ath10k * ar,struct nlattr * tb[])37143d2a30fSKalle Valo static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
37243d2a30fSKalle Valo {
37343d2a30fSKalle Valo struct sk_buff *skb;
37443d2a30fSKalle Valo int ret, buf_len;
37543d2a30fSKalle Valo u32 cmd_id;
37643d2a30fSKalle Valo void *buf;
37743d2a30fSKalle Valo
37843d2a30fSKalle Valo mutex_lock(&ar->conf_mutex);
37943d2a30fSKalle Valo
38043d2a30fSKalle Valo if (ar->state != ATH10K_STATE_UTF) {
38143d2a30fSKalle Valo ret = -ENETDOWN;
38243d2a30fSKalle Valo goto out;
38343d2a30fSKalle Valo }
38443d2a30fSKalle Valo
38543d2a30fSKalle Valo if (!tb[ATH10K_TM_ATTR_DATA]) {
38643d2a30fSKalle Valo ret = -EINVAL;
38743d2a30fSKalle Valo goto out;
38843d2a30fSKalle Valo }
38943d2a30fSKalle Valo
39043d2a30fSKalle Valo if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {
39143d2a30fSKalle Valo ret = -EINVAL;
39243d2a30fSKalle Valo goto out;
39343d2a30fSKalle Valo }
39443d2a30fSKalle Valo
39543d2a30fSKalle Valo buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
39643d2a30fSKalle Valo buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
39743d2a30fSKalle Valo cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);
39843d2a30fSKalle Valo
39943d2a30fSKalle Valo ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
40075b34800SMaharaja Kennadyrajan "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
40143d2a30fSKalle Valo cmd_id, buf, buf_len);
40243d2a30fSKalle Valo
40343d2a30fSKalle Valo ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
40443d2a30fSKalle Valo
40543d2a30fSKalle Valo skb = ath10k_wmi_alloc_skb(ar, buf_len);
40643d2a30fSKalle Valo if (!skb) {
40743d2a30fSKalle Valo ret = -ENOMEM;
40843d2a30fSKalle Valo goto out;
40943d2a30fSKalle Valo }
41043d2a30fSKalle Valo
41143d2a30fSKalle Valo memcpy(skb->data, buf, buf_len);
41243d2a30fSKalle Valo
41343d2a30fSKalle Valo ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
41443d2a30fSKalle Valo if (ret) {
41543d2a30fSKalle Valo ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",
41643d2a30fSKalle Valo ret);
41743d2a30fSKalle Valo goto out;
41843d2a30fSKalle Valo }
41943d2a30fSKalle Valo
42043d2a30fSKalle Valo ret = 0;
42143d2a30fSKalle Valo
42243d2a30fSKalle Valo out:
42343d2a30fSKalle Valo mutex_unlock(&ar->conf_mutex);
42443d2a30fSKalle Valo return ret;
42543d2a30fSKalle Valo }
42643d2a30fSKalle Valo
ath10k_tm_cmd(struct ieee80211_hw * hw,struct ieee80211_vif * vif,void * data,int len)42743d2a30fSKalle Valo int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
42843d2a30fSKalle Valo void *data, int len)
42943d2a30fSKalle Valo {
43043d2a30fSKalle Valo struct ath10k *ar = hw->priv;
43143d2a30fSKalle Valo struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];
43243d2a30fSKalle Valo int ret;
43343d2a30fSKalle Valo
4348cb08174SJohannes Berg ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,
4358cb08174SJohannes Berg ath10k_tm_policy, NULL);
43643d2a30fSKalle Valo if (ret)
43743d2a30fSKalle Valo return ret;
43843d2a30fSKalle Valo
43943d2a30fSKalle Valo if (!tb[ATH10K_TM_ATTR_CMD])
44043d2a30fSKalle Valo return -EINVAL;
44143d2a30fSKalle Valo
44243d2a30fSKalle Valo switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
44343d2a30fSKalle Valo case ATH10K_TM_CMD_GET_VERSION:
44443d2a30fSKalle Valo return ath10k_tm_cmd_get_version(ar, tb);
44543d2a30fSKalle Valo case ATH10K_TM_CMD_UTF_START:
44643d2a30fSKalle Valo return ath10k_tm_cmd_utf_start(ar, tb);
44743d2a30fSKalle Valo case ATH10K_TM_CMD_UTF_STOP:
44843d2a30fSKalle Valo return ath10k_tm_cmd_utf_stop(ar, tb);
44943d2a30fSKalle Valo case ATH10K_TM_CMD_WMI:
45043d2a30fSKalle Valo return ath10k_tm_cmd_wmi(ar, tb);
45143d2a30fSKalle Valo default:
45243d2a30fSKalle Valo return -EOPNOTSUPP;
45343d2a30fSKalle Valo }
45443d2a30fSKalle Valo }
45543d2a30fSKalle Valo
ath10k_testmode_destroy(struct ath10k * ar)45643d2a30fSKalle Valo void ath10k_testmode_destroy(struct ath10k *ar)
45743d2a30fSKalle Valo {
45843d2a30fSKalle Valo mutex_lock(&ar->conf_mutex);
45943d2a30fSKalle Valo
46043d2a30fSKalle Valo if (ar->state != ATH10K_STATE_UTF) {
46143d2a30fSKalle Valo /* utf firmware is not running, nothing to do */
46243d2a30fSKalle Valo goto out;
46343d2a30fSKalle Valo }
46443d2a30fSKalle Valo
46543d2a30fSKalle Valo __ath10k_tm_cmd_utf_stop(ar);
46643d2a30fSKalle Valo
46743d2a30fSKalle Valo out:
46843d2a30fSKalle Valo mutex_unlock(&ar->conf_mutex);
46943d2a30fSKalle Valo }
470