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,
68a67bcec3SChristophe 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,
77a67bcec3SChristophe 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