File Index
Files and directories
Sign in
FILE

testmode.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/ath/ath10k/testmode.c C
  1// SPDX-License-Identifier: ISC
  2/*
  3 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
  4 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  5 */
  6
  7#include "testmode.h"
  8
  9#include <net/netlink.h>
 10#include <linux/firmware.h>
 11
 12#include "debug.h"
 13#include "wmi.h"
 14#include "wmi-tlv.h"
 15#include "hif.h"
 16#include "hw.h"
 17#include "core.h"
 18
 19#include "testmode_i.h"
 20
 21#define ATH10K_FTM_SEG_NONE			((u32)-1)
 22#define ATH10K_FTM_SEGHDR_CURRENT_SEQ		GENMASK(3, 0)
 23#define ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS	GENMASK(7, 4)
 24
 25static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
 26	[ATH10K_TM_ATTR_CMD]		= { .type = NLA_U32 },
 27	[ATH10K_TM_ATTR_DATA]		= { .type = NLA_BINARY,
 28					    .len = ATH10K_TM_DATA_MAX_LEN },
 29	[ATH10K_TM_ATTR_WMI_CMDID]	= { .type = NLA_U32 },
 30	[ATH10K_TM_ATTR_VERSION_MAJOR]	= { .type = NLA_U32 },
 31	[ATH10K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
 32};
 33
 34static void ath10k_tm_event_unsegmented(struct ath10k *ar, u32 cmd_id,
 35					struct sk_buff *skb)
 36{
 37	struct sk_buff *nl_skb;
 38	int ret;
 39
 40	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
 41						   2 * sizeof(u32) + skb->len,
 42						   GFP_ATOMIC);
 43	if (!nl_skb) {
 44		ath10k_warn(ar,
 45			    "failed to allocate skb for testmode wmi event\n");
 46		return;
 47	}
 48
 49	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
 50	if (ret) {
 51		ath10k_warn(ar,
 52			    "failed to put testmode wmi event cmd attribute: %d\n",
 53			    ret);
 54		kfree_skb(nl_skb);
 55		return;
 56	}
 57
 58	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
 59	if (ret) {
 60		ath10k_warn(ar,
 61			    "failed to put testmode wmi event cmd_id: %d\n",
 62			    ret);
 63		kfree_skb(nl_skb);
 64		return;
 65	}
 66
 67	ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
 68	if (ret) {
 69		ath10k_warn(ar,
 70			    "failed to copy skb to testmode wmi event: %d\n",
 71			    ret);
 72		kfree_skb(nl_skb);
 73		return;
 74	}
 75
 76	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
 77}
 78
 79static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
 80{
 81	struct wmi_ftm_cmd *ftm = (struct wmi_ftm_cmd *)skb->data;
 82	u8 total_segments, current_seq;
 83	struct sk_buff *nl_skb;
 84	u8 const *buf_pos;
 85	u16 datalen;
 86	u32 data_pos;
 87	int ret;
 88
 89	if (skb->len < sizeof(*ftm)) {
 90		ath10k_warn(ar, "Invalid ftm event length: %d\n", skb->len);
 91		return;
 92	}
 93
 94	current_seq = FIELD_GET(ATH10K_FTM_SEGHDR_CURRENT_SEQ,
 95				__le32_to_cpu(ftm->seg_hdr.segmentinfo));
 96	total_segments = FIELD_GET(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS,
 97				   __le32_to_cpu(ftm->seg_hdr.segmentinfo));
 98	datalen = skb->len - sizeof(*ftm);
 99	buf_pos = ftm->data;
100
101	if (current_seq == 0) {
102		ar->testmode.expected_seq = 0;
103		ar->testmode.data_pos = 0;
104	}
105
106	data_pos = ar->testmode.data_pos;
107
108	if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
109		ath10k_warn(ar, "Invalid ftm event length at %u: %u\n",
110			    data_pos, datalen);
111		ret = -EINVAL;
112		return;
113	}
114
115	memcpy(&ar->testmode.eventdata[data_pos], buf_pos, datalen);
116	data_pos += datalen;
117
118	if (++ar->testmode.expected_seq != total_segments) {
119		ar->testmode.data_pos = data_pos;
120		ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "partial data received %u/%u\n",
121			   current_seq + 1, total_segments);
122		return;
123	}
124
125	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "total data length %u\n", data_pos);
126
127	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
128						   2 * sizeof(u32) + data_pos,
129						   GFP_ATOMIC);
130	if (!nl_skb) {
131		ath10k_warn(ar, "failed to allocate skb for testmode wmi event\n");
132		return;
133	}
134
135	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_TLV);
136	if (ret) {
137		ath10k_warn(ar, "failed to put testmode wmi event attribute: %d\n", ret);
138		kfree_skb(nl_skb);
139		return;
140	}
141
142	ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
143	if (ret) {
144		ath10k_warn(ar, "failed to put testmode wmi event cmd_id: %d\n", ret);
145		kfree_skb(nl_skb);
146		return;
147	}
148
149	ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, data_pos, &ar->testmode.eventdata[0]);
150	if (ret) {
151		ath10k_warn(ar, "failed to copy skb to testmode wmi event: %d\n", ret);
152		kfree_skb(nl_skb);
153		return;
154	}
155
156	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
157}
158
159/* Returns true if callee consumes the skb and the skb should be discarded.
160 * Returns false if skb is not used. Does not sleep.
161 */
162bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
163{
164	bool consumed;
165
166	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
167		   "testmode event wmi cmd_id %d skb %p skb->len %d\n",
168		   cmd_id, skb, skb->len);
169
170	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
171
172	spin_lock_bh(&ar->data_lock);
173
174	if (!ar->testmode.utf_monitor) {
175		consumed = false;
176		goto out;
177	}
178
179	/* Only testmode.c should be handling events from utf firmware,
180	 * otherwise all sort of problems will arise as mac80211 operations
181	 * are not initialised.
182	 */
183	consumed = true;
184
185	if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE)
186		ath10k_tm_event_segmented(ar, cmd_id, skb);
187	else
188		ath10k_tm_event_unsegmented(ar, cmd_id, skb);
189
190out:
191	spin_unlock_bh(&ar->data_lock);
192
193	return consumed;
194}
195
196static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])
197{
198	struct sk_buff *skb;
199	int ret;
200
201	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
202		   "testmode cmd get version_major %d version_minor %d\n",
203		   ATH10K_TESTMODE_VERSION_MAJOR,
204		   ATH10K_TESTMODE_VERSION_MINOR);
205
206	skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
207						nla_total_size(sizeof(u32)));
208	if (!skb)
209		return -ENOMEM;
210
211	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,
212			  ATH10K_TESTMODE_VERSION_MAJOR);
213	if (ret) {
214		kfree_skb(skb);
215		return ret;
216	}
217
218	ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,
219			  ATH10K_TESTMODE_VERSION_MINOR);
220	if (ret) {
221		kfree_skb(skb);
222		return ret;
223	}
224
225	ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,
226			  ar->normal_mode_fw.fw_file.wmi_op_version);
227	if (ret) {
228		kfree_skb(skb);
229		return ret;
230	}
231
232	return cfg80211_testmode_reply(skb);
233}
234
235static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,
236					      struct ath10k_fw_file *fw_file)
237{
238	char filename[100];
239	int ret;
240
241	snprintf(filename, sizeof(filename), "%s/%s",
242		 ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
243
244	/* load utf firmware image */
245	ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);
246	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",
247		   filename, ret);
248
249	if (ret) {
250		ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
251			    filename, ret);
252		return ret;
253	}
254
255	/* We didn't find FW UTF API 1 ("utf.bin") does not advertise
256	 * firmware features. Do an ugly hack where we force the firmware
257	 * features to match with 10.1 branch so that wmi.c will use the
258	 * correct WMI interface.
259	 */
260
261	fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
262	fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;
263	fw_file->firmware_data = fw_file->firmware->data;
264	fw_file->firmware_len = fw_file->firmware->size;
265
266	return 0;
267}
268
269static int ath10k_tm_fetch_firmware(struct ath10k *ar)
270{
271	struct ath10k_fw_components *utf_mode_fw;
272	int ret;
273	char fw_name[100];
274	int fw_api2 = 2;
275
276	switch (ar->hif.bus) {
277	case ATH10K_BUS_SDIO:
278	case ATH10K_BUS_USB:
279		scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",
280			  ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),
281			  fw_api2);
282		break;
283	default:
284		scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",
285			  ATH10K_FW_UTF_FILE_BASE, fw_api2);
286		break;
287	}
288
289	ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,
290					       &ar->testmode.utf_mode_fw.fw_file);
291	if (ret == 0) {
292		ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");
293		goto out;
294	}
295
296	ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);
297	if (ret) {
298		ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);
299		return ret;
300	}
301
302	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");
303
304out:
305	utf_mode_fw = &ar->testmode.utf_mode_fw;
306
307	/* Use the same board data file as the normal firmware uses (but
308	 * it's still "owned" by normal_mode_fw so we shouldn't free it.
309	 */
310	utf_mode_fw->board_data = ar->normal_mode_fw.board_data;
311	utf_mode_fw->board_len = ar->normal_mode_fw.board_len;
312
313	if (!utf_mode_fw->fw_file.otp_data) {
314		ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");
315		utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;
316		utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;
317	}
318
319	return 0;
320}
321
322static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
323{
324	const char *ver;
325	int ret;
326
327	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");
328
329	mutex_lock(&ar->conf_mutex);
330
331	if (ar->state == ATH10K_STATE_UTF) {
332		ret = -EALREADY;
333		goto err;
334	}
335
336	/* start utf only when the driver is not in use  */
337	if (ar->state != ATH10K_STATE_OFF) {
338		ret = -EBUSY;
339		goto err;
340	}
341
342	if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {
343		/* utf image is already downloaded, it shouldn't be */
344		ret = -EEXIST;
345		goto err;
346	}
347
348	ret = ath10k_tm_fetch_firmware(ar);
349	if (ret) {
350		ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);
351		goto err;
352	}
353
354	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
355	    ar->testmode.utf_mode_fw.fw_file.codeswap_len) {
356		ret = ath10k_swap_code_seg_init(ar,
357						&ar->testmode.utf_mode_fw.fw_file);
358		if (ret) {
359			ath10k_warn(ar,
360				    "failed to init utf code swap segment: %d\n",
361				    ret);
362			goto err_release_utf_mode_fw;
363		}
364	}
365
366	spin_lock_bh(&ar->data_lock);
367	ar->testmode.utf_monitor = true;
368	spin_unlock_bh(&ar->data_lock);
369
370	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",
371		   ar->testmode.utf_mode_fw.fw_file.wmi_op_version);
372
373	ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);
374	if (ret) {
375		ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);
376		ar->state = ATH10K_STATE_OFF;
377		goto err_release_utf_mode_fw;
378	}
379
380	ar->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL);
381	if (!ar->testmode.eventdata) {
382		ret = -ENOMEM;
383		goto err_power_down;
384	}
385
386	ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
387				&ar->testmode.utf_mode_fw);
388	if (ret) {
389		ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
390		ar->state = ATH10K_STATE_OFF;
391		goto err_release_eventdata;
392	}
393
394	ar->state = ATH10K_STATE_UTF;
395
396	if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)
397		ver = ar->testmode.utf_mode_fw.fw_file.fw_version;
398	else
399		ver = "API 1";
400
401	ath10k_info(ar, "UTF firmware %s started\n", ver);
402
403	mutex_unlock(&ar->conf_mutex);
404
405	return 0;
406
407err_release_eventdata:
408	kfree(ar->testmode.eventdata);
409	ar->testmode.eventdata = NULL;
410
411err_power_down:
412	ath10k_hif_power_down(ar);
413
414err_release_utf_mode_fw:
415	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
416	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)
417		ath10k_swap_code_seg_release(ar,
418					     &ar->testmode.utf_mode_fw.fw_file);
419
420	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
421	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
422
423err:
424	mutex_unlock(&ar->conf_mutex);
425
426	return ret;
427}
428
429static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
430{
431	lockdep_assert_held(&ar->conf_mutex);
432
433	ath10k_core_stop(ar);
434	ath10k_hif_power_down(ar);
435
436	spin_lock_bh(&ar->data_lock);
437
438	ar->testmode.utf_monitor = false;
439
440	spin_unlock_bh(&ar->data_lock);
441
442	if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
443	    ar->testmode.utf_mode_fw.fw_file.codeswap_len)
444		ath10k_swap_code_seg_release(ar,
445					     &ar->testmode.utf_mode_fw.fw_file);
446
447	release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
448	ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
449
450	kfree(ar->testmode.eventdata);
451	ar->testmode.eventdata = NULL;
452
453	ar->state = ATH10K_STATE_OFF;
454}
455
456static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])
457{
458	int ret;
459
460	ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");
461
462	mutex_lock(&ar->conf_mutex);
463
464	if (ar->state != ATH10K_STATE_UTF) {
465		ret = -ENETDOWN;
466		goto out;
467	}
468
469	__ath10k_tm_cmd_utf_stop(ar);
470
471	ret = 0;
472
473	ath10k_info(ar, "UTF firmware stopped\n");
474
475out:
476	mutex_unlock(&ar->conf_mutex);
477	return ret;
478}
479
480static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
481{
482	struct sk_buff *skb;
483	int ret, buf_len;
484	u32 cmd_id;
485	void *buf;
486
487	mutex_lock(&ar->conf_mutex);
488
489	if (ar->state != ATH10K_STATE_UTF) {
490		ret = -ENETDOWN;
491		goto out;
492	}
493
494	if (!tb[ATH10K_TM_ATTR_DATA]) {
495		ret = -EINVAL;
496		goto out;
497	}
498
499	if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {
500		ret = -EINVAL;
501		goto out;
502	}
503
504	buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
505	buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
506	cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);
507
508	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
509		   "testmode cmd wmi cmd_id %d buf %p buf_len %d\n",
510		   cmd_id, buf, buf_len);
511
512	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
513
514	skb = ath10k_wmi_alloc_skb(ar, buf_len);
515	if (!skb) {
516		ret = -ENOMEM;
517		goto out;
518	}
519
520	memcpy(skb->data, buf, buf_len);
521
522	ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
523	if (ret) {
524		ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",
525			    ret);
526		goto out;
527	}
528
529	ret = 0;
530
531out:
532	mutex_unlock(&ar->conf_mutex);
533	return ret;
534}
535
536static int ath10k_tm_cmd_tlv(struct ath10k *ar, struct nlattr *tb[])
537{
538	u16 total_bytes, num_segments;
539	u32 cmd_id, buf_len;
540	u8 segnumber = 0;
541	u8 *bufpos;
542	void *buf;
543	int ret;
544
545	mutex_lock(&ar->conf_mutex);
546
547	if (ar->state != ATH10K_STATE_UTF) {
548		ret = -ENETDOWN;
549		goto out;
550	}
551
552	buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
553	buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
554	cmd_id = WMI_PDEV_UTF_CMDID;
555
556	ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
557		   "cmd wmi ftm cmd_id %d buffer length %d\n",
558		   cmd_id, buf_len);
559	ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
560
561	bufpos = buf;
562	total_bytes = buf_len;
563	num_segments = total_bytes / MAX_WMI_UTF_LEN;
564	ar->testmode.expected_seq = 0;
565
566	if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
567		num_segments++;
568
569	while (buf_len) {
570		u16 chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
571		struct wmi_ftm_cmd *ftm_cmd;
572		struct sk_buff *skb;
573		u32 hdr_info;
574		u8 seginfo;
575
576		skb = ath10k_wmi_alloc_skb(ar, (chunk_len +
577					   sizeof(struct wmi_ftm_cmd)));
578		if (!skb) {
579			ret = -ENOMEM;
580			goto out;
581		}
582
583		ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
584		hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TLV_TAG_ARRAY_BYTE) |
585			   FIELD_PREP(WMI_TLV_LEN, (chunk_len +
586				      sizeof(struct wmi_ftm_seg_hdr)));
587		ftm_cmd->tlv_header = __cpu_to_le32(hdr_info);
588		ftm_cmd->seg_hdr.len = __cpu_to_le32(total_bytes);
589		ftm_cmd->seg_hdr.msgref = __cpu_to_le32(ar->testmode.ftm_msgref);
590		seginfo = FIELD_PREP(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
591			  FIELD_PREP(ATH10K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
592		ftm_cmd->seg_hdr.segmentinfo = __cpu_to_le32(seginfo);
593		segnumber++;
594
595		memcpy(&ftm_cmd->data, bufpos, chunk_len);
596
597		ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
598		if (ret) {
599			ath10k_warn(ar, "failed to send wmi ftm command: %d\n", ret);
600			goto out;
601		}
602
603		buf_len -= chunk_len;
604		bufpos += chunk_len;
605	}
606
607	ar->testmode.ftm_msgref++;
608	ret = 0;
609
610out:
611	mutex_unlock(&ar->conf_mutex);
612	return ret;
613}
614
615int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
616		  void *data, int len)
617{
618	struct ath10k *ar = hw->priv;
619	struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];
620	int ret;
621
622	ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,
623				   ath10k_tm_policy, NULL);
624	if (ret)
625		return ret;
626
627	if (!tb[ATH10K_TM_ATTR_CMD])
628		return -EINVAL;
629
630	ar->testmode.expected_seq = ATH10K_FTM_SEG_NONE;
631
632	switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
633	case ATH10K_TM_CMD_GET_VERSION:
634		if (!tb[ATH10K_TM_ATTR_DATA])
635			return ath10k_tm_cmd_get_version(ar, tb);
636		else /* ATH10K_TM_CMD_TLV */
637			return ath10k_tm_cmd_tlv(ar, tb);
638	case ATH10K_TM_CMD_UTF_START:
639		return ath10k_tm_cmd_utf_start(ar, tb);
640	case ATH10K_TM_CMD_UTF_STOP:
641		return ath10k_tm_cmd_utf_stop(ar, tb);
642	case ATH10K_TM_CMD_WMI:
643		return ath10k_tm_cmd_wmi(ar, tb);
644	default:
645		return -EOPNOTSUPP;
646	}
647}
648
649void ath10k_testmode_destroy(struct ath10k *ar)
650{
651	mutex_lock(&ar->conf_mutex);
652
653	if (ar->state != ATH10K_STATE_UTF) {
654		/* utf firmware is not running, nothing to do */
655		goto out;
656	}
657
658	__ath10k_tm_cmd_utf_stop(ar);
659
660out:
661	mutex_unlock(&ar->conf_mutex);
662}
~/ Read-only file preview.