xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/ath/ath10k/testmode.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: ISC
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include "testmode.h"
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <net/netlink.h>
9*4882a593Smuzhiyun #include <linux/firmware.h>
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include "debug.h"
12*4882a593Smuzhiyun #include "wmi.h"
13*4882a593Smuzhiyun #include "hif.h"
14*4882a593Smuzhiyun #include "hw.h"
15*4882a593Smuzhiyun #include "core.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include "testmode_i.h"
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
20*4882a593Smuzhiyun 	[ATH10K_TM_ATTR_CMD]		= { .type = NLA_U32 },
21*4882a593Smuzhiyun 	[ATH10K_TM_ATTR_DATA]		= { .type = NLA_BINARY,
22*4882a593Smuzhiyun 					    .len = ATH10K_TM_DATA_MAX_LEN },
23*4882a593Smuzhiyun 	[ATH10K_TM_ATTR_WMI_CMDID]	= { .type = NLA_U32 },
24*4882a593Smuzhiyun 	[ATH10K_TM_ATTR_VERSION_MAJOR]	= { .type = NLA_U32 },
25*4882a593Smuzhiyun 	[ATH10K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
26*4882a593Smuzhiyun };
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /* Returns true if callee consumes the skb and the skb should be discarded.
29*4882a593Smuzhiyun  * Returns false if skb is not used. Does not sleep.
30*4882a593Smuzhiyun  */
ath10k_tm_event_wmi(struct ath10k * ar,u32 cmd_id,struct sk_buff * skb)31*4882a593Smuzhiyun bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun 	struct sk_buff *nl_skb;
34*4882a593Smuzhiyun 	bool consumed;
35*4882a593Smuzhiyun 	int ret;
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
38*4882a593Smuzhiyun 		   "testmode event wmi cmd_id %d skb %pK skb->len %d\n",
39*4882a593Smuzhiyun 		   cmd_id, skb, skb->len);
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	spin_lock_bh(&ar->data_lock);
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	if (!ar->testmode.utf_monitor) {
46*4882a593Smuzhiyun 		consumed = false;
47*4882a593Smuzhiyun 		goto out;
48*4882a593Smuzhiyun 	}
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	/* Only testmode.c should be handling events from utf firmware,
51*4882a593Smuzhiyun 	 * otherwise all sort of problems will arise as mac80211 operations
52*4882a593Smuzhiyun 	 * are not initialised.
53*4882a593Smuzhiyun 	 */
54*4882a593Smuzhiyun 	consumed = true;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
57*4882a593Smuzhiyun 						   2 * sizeof(u32) + skb->len,
58*4882a593Smuzhiyun 						   GFP_ATOMIC);
59*4882a593Smuzhiyun 	if (!nl_skb) {
60*4882a593Smuzhiyun 		ath10k_warn(ar,
61*4882a593Smuzhiyun 			    "failed to allocate skb for testmode wmi event\n");
62*4882a593Smuzhiyun 		goto out;
63*4882a593Smuzhiyun 	}
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
66*4882a593Smuzhiyun 	if (ret) {
67*4882a593Smuzhiyun 		ath10k_warn(ar,
68*4882a593Smuzhiyun 			    "failed to put testmode wmi event cmd attribute: %d\n",
69*4882a593Smuzhiyun 			    ret);
70*4882a593Smuzhiyun 		kfree_skb(nl_skb);
71*4882a593Smuzhiyun 		goto out;
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
75*4882a593Smuzhiyun 	if (ret) {
76*4882a593Smuzhiyun 		ath10k_warn(ar,
77*4882a593Smuzhiyun 			    "failed to put testmode wmi event cmd_id: %d\n",
78*4882a593Smuzhiyun 			    ret);
79*4882a593Smuzhiyun 		kfree_skb(nl_skb);
80*4882a593Smuzhiyun 		goto out;
81*4882a593Smuzhiyun 	}
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
84*4882a593Smuzhiyun 	if (ret) {
85*4882a593Smuzhiyun 		ath10k_warn(ar,
86*4882a593Smuzhiyun 			    "failed to copy skb to testmode wmi event: %d\n",
87*4882a593Smuzhiyun 			    ret);
88*4882a593Smuzhiyun 		kfree_skb(nl_skb);
89*4882a593Smuzhiyun 		goto out;
90*4882a593Smuzhiyun 	}
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun out:
95*4882a593Smuzhiyun 	spin_unlock_bh(&ar->data_lock);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	return consumed;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun 
ath10k_tm_cmd_get_version(struct ath10k * ar,struct nlattr * tb[])100*4882a593Smuzhiyun static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun 	struct sk_buff *skb;
103*4882a593Smuzhiyun 	int ret;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
106*4882a593Smuzhiyun 		   "testmode cmd get version_major %d version_minor %d\n",
107*4882a593Smuzhiyun 		   ATH10K_TESTMODE_VERSION_MAJOR,
108*4882a593Smuzhiyun 		   ATH10K_TESTMODE_VERSION_MINOR);
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
111*4882a593Smuzhiyun 						nla_total_size(sizeof(u32)));
112*4882a593Smuzhiyun 	if (!skb)
113*4882a593Smuzhiyun 		return -ENOMEM;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,
116*4882a593Smuzhiyun 			  ATH10K_TESTMODE_VERSION_MAJOR);
117*4882a593Smuzhiyun 	if (ret) {
118*4882a593Smuzhiyun 		kfree_skb(skb);
119*4882a593Smuzhiyun 		return ret;
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,
123*4882a593Smuzhiyun 			  ATH10K_TESTMODE_VERSION_MINOR);
124*4882a593Smuzhiyun 	if (ret) {
125*4882a593Smuzhiyun 		kfree_skb(skb);
126*4882a593Smuzhiyun 		return ret;
127*4882a593Smuzhiyun 	}
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,
130*4882a593Smuzhiyun 			  ar->normal_mode_fw.fw_file.wmi_op_version);
131*4882a593Smuzhiyun 	if (ret) {
132*4882a593Smuzhiyun 		kfree_skb(skb);
133*4882a593Smuzhiyun 		return ret;
134*4882a593Smuzhiyun 	}
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	return cfg80211_testmode_reply(skb);
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
ath10k_tm_fetch_utf_firmware_api_1(struct ath10k * ar,struct ath10k_fw_file * fw_file)139*4882a593Smuzhiyun static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,
140*4882a593Smuzhiyun 					      struct ath10k_fw_file *fw_file)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun 	char filename[100];
143*4882a593Smuzhiyun 	int ret;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	snprintf(filename, sizeof(filename), "%s/%s",
146*4882a593Smuzhiyun 		 ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	/* load utf firmware image */
149*4882a593Smuzhiyun 	ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);
150*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",
151*4882a593Smuzhiyun 		   filename, ret);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	if (ret) {
154*4882a593Smuzhiyun 		ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
155*4882a593Smuzhiyun 			    filename, ret);
156*4882a593Smuzhiyun 		return ret;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	/* We didn't find FW UTF API 1 ("utf.bin") does not advertise
160*4882a593Smuzhiyun 	 * firmware features. Do an ugly hack where we force the firmware
161*4882a593Smuzhiyun 	 * features to match with 10.1 branch so that wmi.c will use the
162*4882a593Smuzhiyun 	 * correct WMI interface.
163*4882a593Smuzhiyun 	 */
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
166*4882a593Smuzhiyun 	fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;
167*4882a593Smuzhiyun 	fw_file->firmware_data = fw_file->firmware->data;
168*4882a593Smuzhiyun 	fw_file->firmware_len = fw_file->firmware->size;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	return 0;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun 
ath10k_tm_fetch_firmware(struct ath10k * ar)173*4882a593Smuzhiyun static int ath10k_tm_fetch_firmware(struct ath10k *ar)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun 	struct ath10k_fw_components *utf_mode_fw;
176*4882a593Smuzhiyun 	int ret;
177*4882a593Smuzhiyun 	char fw_name[100];
178*4882a593Smuzhiyun 	int fw_api2 = 2;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	switch (ar->hif.bus) {
181*4882a593Smuzhiyun 	case ATH10K_BUS_SDIO:
182*4882a593Smuzhiyun 	case ATH10K_BUS_USB:
183*4882a593Smuzhiyun 		scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",
184*4882a593Smuzhiyun 			  ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),
185*4882a593Smuzhiyun 			  fw_api2);
186*4882a593Smuzhiyun 		break;
187*4882a593Smuzhiyun 	default:
188*4882a593Smuzhiyun 		scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",
189*4882a593Smuzhiyun 			  ATH10K_FW_UTF_FILE_BASE, fw_api2);
190*4882a593Smuzhiyun 		break;
191*4882a593Smuzhiyun 	}
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,
194*4882a593Smuzhiyun 					       &ar->testmode.utf_mode_fw.fw_file);
195*4882a593Smuzhiyun 	if (ret == 0) {
196*4882a593Smuzhiyun 		ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");
197*4882a593Smuzhiyun 		goto out;
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);
201*4882a593Smuzhiyun 	if (ret) {
202*4882a593Smuzhiyun 		ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);
203*4882a593Smuzhiyun 		return ret;
204*4882a593Smuzhiyun 	}
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun out:
209*4882a593Smuzhiyun 	utf_mode_fw = &ar->testmode.utf_mode_fw;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	/* Use the same board data file as the normal firmware uses (but
212*4882a593Smuzhiyun 	 * it's still "owned" by normal_mode_fw so we shouldn't free it.
213*4882a593Smuzhiyun 	 */
214*4882a593Smuzhiyun 	utf_mode_fw->board_data = ar->normal_mode_fw.board_data;
215*4882a593Smuzhiyun 	utf_mode_fw->board_len = ar->normal_mode_fw.board_len;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	if (!utf_mode_fw->fw_file.otp_data) {
218*4882a593Smuzhiyun 		ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");
219*4882a593Smuzhiyun 		utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;
220*4882a593Smuzhiyun 		utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;
221*4882a593Smuzhiyun 	}
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	return 0;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun 
ath10k_tm_cmd_utf_start(struct ath10k * ar,struct nlattr * tb[])226*4882a593Smuzhiyun static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun 	const char *ver;
229*4882a593Smuzhiyun 	int ret;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	mutex_lock(&ar->conf_mutex);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	if (ar->state == ATH10K_STATE_UTF) {
236*4882a593Smuzhiyun 		ret = -EALREADY;
237*4882a593Smuzhiyun 		goto err;
238*4882a593Smuzhiyun 	}
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	/* start utf only when the driver is not in use  */
241*4882a593Smuzhiyun 	if (ar->state != ATH10K_STATE_OFF) {
242*4882a593Smuzhiyun 		ret = -EBUSY;
243*4882a593Smuzhiyun 		goto err;
244*4882a593Smuzhiyun 	}
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {
247*4882a593Smuzhiyun 		/* utf image is already downloaded, it shouldn't be */
248*4882a593Smuzhiyun 		ret = -EEXIST;
249*4882a593Smuzhiyun 		goto err;
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	ret = ath10k_tm_fetch_firmware(ar);
253*4882a593Smuzhiyun 	if (ret) {
254*4882a593Smuzhiyun 		ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);
255*4882a593Smuzhiyun 		goto err;
256*4882a593Smuzhiyun 	}
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
259*4882a593Smuzhiyun 	    ar->testmode.utf_mode_fw.fw_file.codeswap_len) {
260*4882a593Smuzhiyun 		ret = ath10k_swap_code_seg_init(ar,
261*4882a593Smuzhiyun 						&ar->testmode.utf_mode_fw.fw_file);
262*4882a593Smuzhiyun 		if (ret) {
263*4882a593Smuzhiyun 			ath10k_warn(ar,
264*4882a593Smuzhiyun 				    "failed to init utf code swap segment: %d\n",
265*4882a593Smuzhiyun 				    ret);
266*4882a593Smuzhiyun 			goto err_release_utf_mode_fw;
267*4882a593Smuzhiyun 		}
268*4882a593Smuzhiyun 	}
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	spin_lock_bh(&ar->data_lock);
271*4882a593Smuzhiyun 	ar->testmode.utf_monitor = true;
272*4882a593Smuzhiyun 	spin_unlock_bh(&ar->data_lock);
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",
275*4882a593Smuzhiyun 		   ar->testmode.utf_mode_fw.fw_file.wmi_op_version);
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);
278*4882a593Smuzhiyun 	if (ret) {
279*4882a593Smuzhiyun 		ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);
280*4882a593Smuzhiyun 		ar->state = ATH10K_STATE_OFF;
281*4882a593Smuzhiyun 		goto err_release_utf_mode_fw;
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
285*4882a593Smuzhiyun 				&ar->testmode.utf_mode_fw);
286*4882a593Smuzhiyun 	if (ret) {
287*4882a593Smuzhiyun 		ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
288*4882a593Smuzhiyun 		ar->state = ATH10K_STATE_OFF;
289*4882a593Smuzhiyun 		goto err_power_down;
290*4882a593Smuzhiyun 	}
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	ar->state = ATH10K_STATE_UTF;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)
295*4882a593Smuzhiyun 		ver = ar->testmode.utf_mode_fw.fw_file.fw_version;
296*4882a593Smuzhiyun 	else
297*4882a593Smuzhiyun 		ver = "API 1";
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	ath10k_info(ar, "UTF firmware %s started\n", ver);
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	mutex_unlock(&ar->conf_mutex);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	return 0;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun err_power_down:
306*4882a593Smuzhiyun 	ath10k_hif_power_down(ar);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun err_release_utf_mode_fw:
309*4882a593Smuzhiyun 	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
310*4882a593Smuzhiyun 	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)
311*4882a593Smuzhiyun 		ath10k_swap_code_seg_release(ar,
312*4882a593Smuzhiyun 					     &ar->testmode.utf_mode_fw.fw_file);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
315*4882a593Smuzhiyun 	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun err:
318*4882a593Smuzhiyun 	mutex_unlock(&ar->conf_mutex);
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	return ret;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
__ath10k_tm_cmd_utf_stop(struct ath10k * ar)323*4882a593Smuzhiyun static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	lockdep_assert_held(&ar->conf_mutex);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	ath10k_core_stop(ar);
328*4882a593Smuzhiyun 	ath10k_hif_power_down(ar);
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	spin_lock_bh(&ar->data_lock);
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	ar->testmode.utf_monitor = false;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	spin_unlock_bh(&ar->data_lock);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
337*4882a593Smuzhiyun 	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)
338*4882a593Smuzhiyun 		ath10k_swap_code_seg_release(ar,
339*4882a593Smuzhiyun 					     &ar->testmode.utf_mode_fw.fw_file);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
342*4882a593Smuzhiyun 	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	ar->state = ATH10K_STATE_OFF;
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun 
ath10k_tm_cmd_utf_stop(struct ath10k * ar,struct nlattr * tb[])347*4882a593Smuzhiyun static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun 	int ret;
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	mutex_lock(&ar->conf_mutex);
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	if (ar->state != ATH10K_STATE_UTF) {
356*4882a593Smuzhiyun 		ret = -ENETDOWN;
357*4882a593Smuzhiyun 		goto out;
358*4882a593Smuzhiyun 	}
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	__ath10k_tm_cmd_utf_stop(ar);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	ret = 0;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	ath10k_info(ar, "UTF firmware stopped\n");
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun out:
367*4882a593Smuzhiyun 	mutex_unlock(&ar->conf_mutex);
368*4882a593Smuzhiyun 	return ret;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun 
ath10k_tm_cmd_wmi(struct ath10k * ar,struct nlattr * tb[])371*4882a593Smuzhiyun static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 	struct sk_buff *skb;
374*4882a593Smuzhiyun 	int ret, buf_len;
375*4882a593Smuzhiyun 	u32 cmd_id;
376*4882a593Smuzhiyun 	void *buf;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	mutex_lock(&ar->conf_mutex);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	if (ar->state != ATH10K_STATE_UTF) {
381*4882a593Smuzhiyun 		ret = -ENETDOWN;
382*4882a593Smuzhiyun 		goto out;
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	if (!tb[ATH10K_TM_ATTR_DATA]) {
386*4882a593Smuzhiyun 		ret = -EINVAL;
387*4882a593Smuzhiyun 		goto out;
388*4882a593Smuzhiyun 	}
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {
391*4882a593Smuzhiyun 		ret = -EINVAL;
392*4882a593Smuzhiyun 		goto out;
393*4882a593Smuzhiyun 	}
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
396*4882a593Smuzhiyun 	buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
397*4882a593Smuzhiyun 	cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
400*4882a593Smuzhiyun 		   "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
401*4882a593Smuzhiyun 		   cmd_id, buf, buf_len);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	skb = ath10k_wmi_alloc_skb(ar, buf_len);
406*4882a593Smuzhiyun 	if (!skb) {
407*4882a593Smuzhiyun 		ret = -ENOMEM;
408*4882a593Smuzhiyun 		goto out;
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	memcpy(skb->data, buf, buf_len);
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
414*4882a593Smuzhiyun 	if (ret) {
415*4882a593Smuzhiyun 		ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",
416*4882a593Smuzhiyun 			    ret);
417*4882a593Smuzhiyun 		goto out;
418*4882a593Smuzhiyun 	}
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	ret = 0;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun out:
423*4882a593Smuzhiyun 	mutex_unlock(&ar->conf_mutex);
424*4882a593Smuzhiyun 	return ret;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun 
ath10k_tm_cmd(struct ieee80211_hw * hw,struct ieee80211_vif * vif,void * data,int len)427*4882a593Smuzhiyun int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
428*4882a593Smuzhiyun 		  void *data, int len)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun 	struct ath10k *ar = hw->priv;
431*4882a593Smuzhiyun 	struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];
432*4882a593Smuzhiyun 	int ret;
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,
435*4882a593Smuzhiyun 				   ath10k_tm_policy, NULL);
436*4882a593Smuzhiyun 	if (ret)
437*4882a593Smuzhiyun 		return ret;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	if (!tb[ATH10K_TM_ATTR_CMD])
440*4882a593Smuzhiyun 		return -EINVAL;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
443*4882a593Smuzhiyun 	case ATH10K_TM_CMD_GET_VERSION:
444*4882a593Smuzhiyun 		return ath10k_tm_cmd_get_version(ar, tb);
445*4882a593Smuzhiyun 	case ATH10K_TM_CMD_UTF_START:
446*4882a593Smuzhiyun 		return ath10k_tm_cmd_utf_start(ar, tb);
447*4882a593Smuzhiyun 	case ATH10K_TM_CMD_UTF_STOP:
448*4882a593Smuzhiyun 		return ath10k_tm_cmd_utf_stop(ar, tb);
449*4882a593Smuzhiyun 	case ATH10K_TM_CMD_WMI:
450*4882a593Smuzhiyun 		return ath10k_tm_cmd_wmi(ar, tb);
451*4882a593Smuzhiyun 	default:
452*4882a593Smuzhiyun 		return -EOPNOTSUPP;
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun 
ath10k_testmode_destroy(struct ath10k * ar)456*4882a593Smuzhiyun void ath10k_testmode_destroy(struct ath10k *ar)
457*4882a593Smuzhiyun {
458*4882a593Smuzhiyun 	mutex_lock(&ar->conf_mutex);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	if (ar->state != ATH10K_STATE_UTF) {
461*4882a593Smuzhiyun 		/* utf firmware is not running, nothing to do */
462*4882a593Smuzhiyun 		goto out;
463*4882a593Smuzhiyun 	}
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	__ath10k_tm_cmd_utf_stop(ar);
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun out:
468*4882a593Smuzhiyun 	mutex_unlock(&ar->conf_mutex);
469*4882a593Smuzhiyun }
470