18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "testmode.h"
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <net/netlink.h>
98c2ecf20Sopenharmony_ci#include <linux/firmware.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "debug.h"
128c2ecf20Sopenharmony_ci#include "wmi.h"
138c2ecf20Sopenharmony_ci#include "hif.h"
148c2ecf20Sopenharmony_ci#include "hw.h"
158c2ecf20Sopenharmony_ci#include "core.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "testmode_i.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
208c2ecf20Sopenharmony_ci	[ATH10K_TM_ATTR_CMD]		= { .type = NLA_U32 },
218c2ecf20Sopenharmony_ci	[ATH10K_TM_ATTR_DATA]		= { .type = NLA_BINARY,
228c2ecf20Sopenharmony_ci					    .len = ATH10K_TM_DATA_MAX_LEN },
238c2ecf20Sopenharmony_ci	[ATH10K_TM_ATTR_WMI_CMDID]	= { .type = NLA_U32 },
248c2ecf20Sopenharmony_ci	[ATH10K_TM_ATTR_VERSION_MAJOR]	= { .type = NLA_U32 },
258c2ecf20Sopenharmony_ci	[ATH10K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Returns true if callee consumes the skb and the skb should be discarded.
298c2ecf20Sopenharmony_ci * Returns false if skb is not used. Does not sleep.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cibool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct sk_buff *nl_skb;
348c2ecf20Sopenharmony_ci	bool consumed;
358c2ecf20Sopenharmony_ci	int ret;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
388c2ecf20Sopenharmony_ci		   "testmode event wmi cmd_id %d skb %pK skb->len %d\n",
398c2ecf20Sopenharmony_ci		   cmd_id, skb, skb->len);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (!ar->testmode.utf_monitor) {
468c2ecf20Sopenharmony_ci		consumed = false;
478c2ecf20Sopenharmony_ci		goto out;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* Only testmode.c should be handling events from utf firmware,
518c2ecf20Sopenharmony_ci	 * otherwise all sort of problems will arise as mac80211 operations
528c2ecf20Sopenharmony_ci	 * are not initialised.
538c2ecf20Sopenharmony_ci	 */
548c2ecf20Sopenharmony_ci	consumed = true;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
578c2ecf20Sopenharmony_ci						   2 * sizeof(u32) + skb->len,
588c2ecf20Sopenharmony_ci						   GFP_ATOMIC);
598c2ecf20Sopenharmony_ci	if (!nl_skb) {
608c2ecf20Sopenharmony_ci		ath10k_warn(ar,
618c2ecf20Sopenharmony_ci			    "failed to allocate skb for testmode wmi event\n");
628c2ecf20Sopenharmony_ci		goto out;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
668c2ecf20Sopenharmony_ci	if (ret) {
678c2ecf20Sopenharmony_ci		ath10k_warn(ar,
688c2ecf20Sopenharmony_ci			    "failed to put testmode wmi event cmd attribute: %d\n",
698c2ecf20Sopenharmony_ci			    ret);
708c2ecf20Sopenharmony_ci		kfree_skb(nl_skb);
718c2ecf20Sopenharmony_ci		goto out;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
758c2ecf20Sopenharmony_ci	if (ret) {
768c2ecf20Sopenharmony_ci		ath10k_warn(ar,
778c2ecf20Sopenharmony_ci			    "failed to put testmode wmi event cmd_id: %d\n",
788c2ecf20Sopenharmony_ci			    ret);
798c2ecf20Sopenharmony_ci		kfree_skb(nl_skb);
808c2ecf20Sopenharmony_ci		goto out;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
848c2ecf20Sopenharmony_ci	if (ret) {
858c2ecf20Sopenharmony_ci		ath10k_warn(ar,
868c2ecf20Sopenharmony_ci			    "failed to copy skb to testmode wmi event: %d\n",
878c2ecf20Sopenharmony_ci			    ret);
888c2ecf20Sopenharmony_ci		kfree_skb(nl_skb);
898c2ecf20Sopenharmony_ci		goto out;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ciout:
958c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return consumed;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1038c2ecf20Sopenharmony_ci	int ret;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
1068c2ecf20Sopenharmony_ci		   "testmode cmd get version_major %d version_minor %d\n",
1078c2ecf20Sopenharmony_ci		   ATH10K_TESTMODE_VERSION_MAJOR,
1088c2ecf20Sopenharmony_ci		   ATH10K_TESTMODE_VERSION_MINOR);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
1118c2ecf20Sopenharmony_ci						nla_total_size(sizeof(u32)));
1128c2ecf20Sopenharmony_ci	if (!skb)
1138c2ecf20Sopenharmony_ci		return -ENOMEM;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,
1168c2ecf20Sopenharmony_ci			  ATH10K_TESTMODE_VERSION_MAJOR);
1178c2ecf20Sopenharmony_ci	if (ret) {
1188c2ecf20Sopenharmony_ci		kfree_skb(skb);
1198c2ecf20Sopenharmony_ci		return ret;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,
1238c2ecf20Sopenharmony_ci			  ATH10K_TESTMODE_VERSION_MINOR);
1248c2ecf20Sopenharmony_ci	if (ret) {
1258c2ecf20Sopenharmony_ci		kfree_skb(skb);
1268c2ecf20Sopenharmony_ci		return ret;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,
1308c2ecf20Sopenharmony_ci			  ar->normal_mode_fw.fw_file.wmi_op_version);
1318c2ecf20Sopenharmony_ci	if (ret) {
1328c2ecf20Sopenharmony_ci		kfree_skb(skb);
1338c2ecf20Sopenharmony_ci		return ret;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return cfg80211_testmode_reply(skb);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,
1408c2ecf20Sopenharmony_ci					      struct ath10k_fw_file *fw_file)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	char filename[100];
1438c2ecf20Sopenharmony_ci	int ret;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	snprintf(filename, sizeof(filename), "%s/%s",
1468c2ecf20Sopenharmony_ci		 ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* load utf firmware image */
1498c2ecf20Sopenharmony_ci	ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);
1508c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",
1518c2ecf20Sopenharmony_ci		   filename, ret);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (ret) {
1548c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
1558c2ecf20Sopenharmony_ci			    filename, ret);
1568c2ecf20Sopenharmony_ci		return ret;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* We didn't find FW UTF API 1 ("utf.bin") does not advertise
1608c2ecf20Sopenharmony_ci	 * firmware features. Do an ugly hack where we force the firmware
1618c2ecf20Sopenharmony_ci	 * features to match with 10.1 branch so that wmi.c will use the
1628c2ecf20Sopenharmony_ci	 * correct WMI interface.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
1668c2ecf20Sopenharmony_ci	fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;
1678c2ecf20Sopenharmony_ci	fw_file->firmware_data = fw_file->firmware->data;
1688c2ecf20Sopenharmony_ci	fw_file->firmware_len = fw_file->firmware->size;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return 0;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int ath10k_tm_fetch_firmware(struct ath10k *ar)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct ath10k_fw_components *utf_mode_fw;
1768c2ecf20Sopenharmony_ci	int ret;
1778c2ecf20Sopenharmony_ci	char fw_name[100];
1788c2ecf20Sopenharmony_ci	int fw_api2 = 2;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	switch (ar->hif.bus) {
1818c2ecf20Sopenharmony_ci	case ATH10K_BUS_SDIO:
1828c2ecf20Sopenharmony_ci	case ATH10K_BUS_USB:
1838c2ecf20Sopenharmony_ci		scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",
1848c2ecf20Sopenharmony_ci			  ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),
1858c2ecf20Sopenharmony_ci			  fw_api2);
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	default:
1888c2ecf20Sopenharmony_ci		scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",
1898c2ecf20Sopenharmony_ci			  ATH10K_FW_UTF_FILE_BASE, fw_api2);
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,
1948c2ecf20Sopenharmony_ci					       &ar->testmode.utf_mode_fw.fw_file);
1958c2ecf20Sopenharmony_ci	if (ret == 0) {
1968c2ecf20Sopenharmony_ci		ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");
1978c2ecf20Sopenharmony_ci		goto out;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);
2018c2ecf20Sopenharmony_ci	if (ret) {
2028c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);
2038c2ecf20Sopenharmony_ci		return ret;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciout:
2098c2ecf20Sopenharmony_ci	utf_mode_fw = &ar->testmode.utf_mode_fw;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* Use the same board data file as the normal firmware uses (but
2128c2ecf20Sopenharmony_ci	 * it's still "owned" by normal_mode_fw so we shouldn't free it.
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	utf_mode_fw->board_data = ar->normal_mode_fw.board_data;
2158c2ecf20Sopenharmony_ci	utf_mode_fw->board_len = ar->normal_mode_fw.board_len;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (!utf_mode_fw->fw_file.otp_data) {
2188c2ecf20Sopenharmony_ci		ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");
2198c2ecf20Sopenharmony_ci		utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;
2208c2ecf20Sopenharmony_ci		utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	const char *ver;
2298c2ecf20Sopenharmony_ci	int ret;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (ar->state == ATH10K_STATE_UTF) {
2368c2ecf20Sopenharmony_ci		ret = -EALREADY;
2378c2ecf20Sopenharmony_ci		goto err;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* start utf only when the driver is not in use  */
2418c2ecf20Sopenharmony_ci	if (ar->state != ATH10K_STATE_OFF) {
2428c2ecf20Sopenharmony_ci		ret = -EBUSY;
2438c2ecf20Sopenharmony_ci		goto err;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {
2478c2ecf20Sopenharmony_ci		/* utf image is already downloaded, it shouldn't be */
2488c2ecf20Sopenharmony_ci		ret = -EEXIST;
2498c2ecf20Sopenharmony_ci		goto err;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	ret = ath10k_tm_fetch_firmware(ar);
2538c2ecf20Sopenharmony_ci	if (ret) {
2548c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);
2558c2ecf20Sopenharmony_ci		goto err;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
2598c2ecf20Sopenharmony_ci	    ar->testmode.utf_mode_fw.fw_file.codeswap_len) {
2608c2ecf20Sopenharmony_ci		ret = ath10k_swap_code_seg_init(ar,
2618c2ecf20Sopenharmony_ci						&ar->testmode.utf_mode_fw.fw_file);
2628c2ecf20Sopenharmony_ci		if (ret) {
2638c2ecf20Sopenharmony_ci			ath10k_warn(ar,
2648c2ecf20Sopenharmony_ci				    "failed to init utf code swap segment: %d\n",
2658c2ecf20Sopenharmony_ci				    ret);
2668c2ecf20Sopenharmony_ci			goto err_release_utf_mode_fw;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
2718c2ecf20Sopenharmony_ci	ar->testmode.utf_monitor = true;
2728c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",
2758c2ecf20Sopenharmony_ci		   ar->testmode.utf_mode_fw.fw_file.wmi_op_version);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);
2788c2ecf20Sopenharmony_ci	if (ret) {
2798c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);
2808c2ecf20Sopenharmony_ci		ar->state = ATH10K_STATE_OFF;
2818c2ecf20Sopenharmony_ci		goto err_release_utf_mode_fw;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
2858c2ecf20Sopenharmony_ci				&ar->testmode.utf_mode_fw);
2868c2ecf20Sopenharmony_ci	if (ret) {
2878c2ecf20Sopenharmony_ci		ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
2888c2ecf20Sopenharmony_ci		ar->state = ATH10K_STATE_OFF;
2898c2ecf20Sopenharmony_ci		goto err_power_down;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ar->state = ATH10K_STATE_UTF;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)
2958c2ecf20Sopenharmony_ci		ver = ar->testmode.utf_mode_fw.fw_file.fw_version;
2968c2ecf20Sopenharmony_ci	else
2978c2ecf20Sopenharmony_ci		ver = "API 1";
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	ath10k_info(ar, "UTF firmware %s started\n", ver);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return 0;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cierr_power_down:
3068c2ecf20Sopenharmony_ci	ath10k_hif_power_down(ar);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cierr_release_utf_mode_fw:
3098c2ecf20Sopenharmony_ci	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
3108c2ecf20Sopenharmony_ci	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)
3118c2ecf20Sopenharmony_ci		ath10k_swap_code_seg_release(ar,
3128c2ecf20Sopenharmony_ci					     &ar->testmode.utf_mode_fw.fw_file);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
3158c2ecf20Sopenharmony_ci	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cierr:
3188c2ecf20Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return ret;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	lockdep_assert_held(&ar->conf_mutex);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	ath10k_core_stop(ar);
3288c2ecf20Sopenharmony_ci	ath10k_hif_power_down(ar);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	spin_lock_bh(&ar->data_lock);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	ar->testmode.utf_monitor = false;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	spin_unlock_bh(&ar->data_lock);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
3378c2ecf20Sopenharmony_ci	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)
3388c2ecf20Sopenharmony_ci		ath10k_swap_code_seg_release(ar,
3398c2ecf20Sopenharmony_ci					     &ar->testmode.utf_mode_fw.fw_file);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
3428c2ecf20Sopenharmony_ci	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	ar->state = ATH10K_STATE_OFF;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	int ret;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (ar->state != ATH10K_STATE_UTF) {
3568c2ecf20Sopenharmony_ci		ret = -ENETDOWN;
3578c2ecf20Sopenharmony_ci		goto out;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	__ath10k_tm_cmd_utf_stop(ar);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	ret = 0;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	ath10k_info(ar, "UTF firmware stopped\n");
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ciout:
3678c2ecf20Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
3688c2ecf20Sopenharmony_ci	return ret;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3748c2ecf20Sopenharmony_ci	int ret, buf_len;
3758c2ecf20Sopenharmony_ci	u32 cmd_id;
3768c2ecf20Sopenharmony_ci	void *buf;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (ar->state != ATH10K_STATE_UTF) {
3818c2ecf20Sopenharmony_ci		ret = -ENETDOWN;
3828c2ecf20Sopenharmony_ci		goto out;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (!tb[ATH10K_TM_ATTR_DATA]) {
3868c2ecf20Sopenharmony_ci		ret = -EINVAL;
3878c2ecf20Sopenharmony_ci		goto out;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {
3918c2ecf20Sopenharmony_ci		ret = -EINVAL;
3928c2ecf20Sopenharmony_ci		goto out;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
3968c2ecf20Sopenharmony_ci	buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
3978c2ecf20Sopenharmony_ci	cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
4008c2ecf20Sopenharmony_ci		   "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
4018c2ecf20Sopenharmony_ci		   cmd_id, buf, buf_len);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	skb = ath10k_wmi_alloc_skb(ar, buf_len);
4068c2ecf20Sopenharmony_ci	if (!skb) {
4078c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4088c2ecf20Sopenharmony_ci		goto out;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	memcpy(skb->data, buf, buf_len);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
4148c2ecf20Sopenharmony_ci	if (ret) {
4158c2ecf20Sopenharmony_ci		ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",
4168c2ecf20Sopenharmony_ci			    ret);
4178c2ecf20Sopenharmony_ci		goto out;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	ret = 0;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ciout:
4238c2ecf20Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
4248c2ecf20Sopenharmony_ci	return ret;
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ciint ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
4288c2ecf20Sopenharmony_ci		  void *data, int len)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct ath10k *ar = hw->priv;
4318c2ecf20Sopenharmony_ci	struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];
4328c2ecf20Sopenharmony_ci	int ret;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,
4358c2ecf20Sopenharmony_ci				   ath10k_tm_policy, NULL);
4368c2ecf20Sopenharmony_ci	if (ret)
4378c2ecf20Sopenharmony_ci		return ret;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (!tb[ATH10K_TM_ATTR_CMD])
4408c2ecf20Sopenharmony_ci		return -EINVAL;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
4438c2ecf20Sopenharmony_ci	case ATH10K_TM_CMD_GET_VERSION:
4448c2ecf20Sopenharmony_ci		return ath10k_tm_cmd_get_version(ar, tb);
4458c2ecf20Sopenharmony_ci	case ATH10K_TM_CMD_UTF_START:
4468c2ecf20Sopenharmony_ci		return ath10k_tm_cmd_utf_start(ar, tb);
4478c2ecf20Sopenharmony_ci	case ATH10K_TM_CMD_UTF_STOP:
4488c2ecf20Sopenharmony_ci		return ath10k_tm_cmd_utf_stop(ar, tb);
4498c2ecf20Sopenharmony_ci	case ATH10K_TM_CMD_WMI:
4508c2ecf20Sopenharmony_ci		return ath10k_tm_cmd_wmi(ar, tb);
4518c2ecf20Sopenharmony_ci	default:
4528c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_civoid ath10k_testmode_destroy(struct ath10k *ar)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	mutex_lock(&ar->conf_mutex);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	if (ar->state != ATH10K_STATE_UTF) {
4618c2ecf20Sopenharmony_ci		/* utf firmware is not running, nothing to do */
4628c2ecf20Sopenharmony_ci		goto out;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	__ath10k_tm_cmd_utf_stop(ar);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ciout:
4688c2ecf20Sopenharmony_ci	mutex_unlock(&ar->conf_mutex);
4698c2ecf20Sopenharmony_ci}
470