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