xref: /freebsd/sys/contrib/dev/athk/ath11k/testmode.c (revision 28348caeee6ee98251b0aaa026e8d52b5032e92c)
1dd4f32aeSBjoern A. Zeeb // SPDX-License-Identifier: BSD-3-Clause-Clear
2dd4f32aeSBjoern A. Zeeb /*
3dd4f32aeSBjoern A. Zeeb  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
4*28348caeSBjoern A. Zeeb  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
5dd4f32aeSBjoern A. Zeeb  */
6dd4f32aeSBjoern A. Zeeb 
7dd4f32aeSBjoern A. Zeeb #include "testmode.h"
8dd4f32aeSBjoern A. Zeeb #include <net/netlink.h>
9dd4f32aeSBjoern A. Zeeb #include "debug.h"
10dd4f32aeSBjoern A. Zeeb #include "wmi.h"
11dd4f32aeSBjoern A. Zeeb #include "hw.h"
12dd4f32aeSBjoern A. Zeeb #include "core.h"
13dd4f32aeSBjoern A. Zeeb #include "testmode_i.h"
14dd4f32aeSBjoern A. Zeeb 
15*28348caeSBjoern A. Zeeb #define ATH11K_FTM_SEGHDR_CURRENT_SEQ		GENMASK(3, 0)
16*28348caeSBjoern A. Zeeb #define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS	GENMASK(7, 4)
17*28348caeSBjoern A. Zeeb 
18dd4f32aeSBjoern A. Zeeb static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = {
19dd4f32aeSBjoern A. Zeeb 	[ATH11K_TM_ATTR_CMD]		= { .type = NLA_U32 },
20dd4f32aeSBjoern A. Zeeb 	[ATH11K_TM_ATTR_DATA]		= { .type = NLA_BINARY,
21dd4f32aeSBjoern A. Zeeb 					    .len = ATH11K_TM_DATA_MAX_LEN },
22dd4f32aeSBjoern A. Zeeb 	[ATH11K_TM_ATTR_WMI_CMDID]	= { .type = NLA_U32 },
23dd4f32aeSBjoern A. Zeeb 	[ATH11K_TM_ATTR_VERSION_MAJOR]	= { .type = NLA_U32 },
24dd4f32aeSBjoern A. Zeeb 	[ATH11K_TM_ATTR_VERSION_MINOR]	= { .type = NLA_U32 },
25dd4f32aeSBjoern A. Zeeb };
26dd4f32aeSBjoern A. Zeeb 
ath11k_tm_get_ar(struct ath11k_base * ab)27*28348caeSBjoern A. Zeeb static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab)
28*28348caeSBjoern A. Zeeb {
29*28348caeSBjoern A. Zeeb 	struct ath11k_pdev *pdev;
30*28348caeSBjoern A. Zeeb 	struct ath11k *ar = NULL;
31*28348caeSBjoern A. Zeeb 	int i;
32*28348caeSBjoern A. Zeeb 
33*28348caeSBjoern A. Zeeb 	for (i = 0; i < ab->num_radios; i++) {
34*28348caeSBjoern A. Zeeb 		pdev = &ab->pdevs[i];
35*28348caeSBjoern A. Zeeb 		ar = pdev->ar;
36*28348caeSBjoern A. Zeeb 
37*28348caeSBjoern A. Zeeb 		if (ar && ar->state == ATH11K_STATE_FTM)
38*28348caeSBjoern A. Zeeb 			break;
39*28348caeSBjoern A. Zeeb 	}
40*28348caeSBjoern A. Zeeb 
41*28348caeSBjoern A. Zeeb 	return ar;
42*28348caeSBjoern A. Zeeb }
43*28348caeSBjoern A. Zeeb 
44*28348caeSBjoern A. Zeeb /* This function handles unsegmented events. Data in various events are aggregated
45*28348caeSBjoern A. Zeeb  * in application layer, this event is unsegmented from host perspective.
46dd4f32aeSBjoern A. Zeeb  */
ath11k_tm_wmi_event_unsegmented(struct ath11k_base * ab,u32 cmd_id,struct sk_buff * skb)47*28348caeSBjoern A. Zeeb static void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
48*28348caeSBjoern A. Zeeb 					    struct sk_buff *skb)
49dd4f32aeSBjoern A. Zeeb {
50dd4f32aeSBjoern A. Zeeb 	struct sk_buff *nl_skb;
51*28348caeSBjoern A. Zeeb 	struct ath11k *ar;
52dd4f32aeSBjoern A. Zeeb 
53*28348caeSBjoern A. Zeeb 	ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
54*28348caeSBjoern A. Zeeb 		   "event wmi cmd_id %d skb length %d\n",
55*28348caeSBjoern A. Zeeb 		   cmd_id, skb->len);
56*28348caeSBjoern A. Zeeb 	ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
57dd4f32aeSBjoern A. Zeeb 
58*28348caeSBjoern A. Zeeb 	ar = ath11k_tm_get_ar(ab);
59*28348caeSBjoern A. Zeeb 	if (!ar) {
60*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "testmode event not handled due to invalid pdev\n");
61*28348caeSBjoern A. Zeeb 		return;
62*28348caeSBjoern A. Zeeb 	}
63dd4f32aeSBjoern A. Zeeb 
64dd4f32aeSBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
65dd4f32aeSBjoern A. Zeeb 
66dd4f32aeSBjoern A. Zeeb 	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
67*28348caeSBjoern A. Zeeb 						   2 * nla_total_size(sizeof(u32)) +
68*28348caeSBjoern A. Zeeb 						   nla_total_size(skb->len),
69dd4f32aeSBjoern A. Zeeb 						   GFP_ATOMIC);
70dd4f32aeSBjoern A. Zeeb 	if (!nl_skb) {
71*28348caeSBjoern A. Zeeb 		ath11k_warn(ab,
72*28348caeSBjoern A. Zeeb 			    "failed to allocate skb for unsegmented testmode wmi event\n");
73dd4f32aeSBjoern A. Zeeb 		goto out;
74dd4f32aeSBjoern A. Zeeb 	}
75dd4f32aeSBjoern A. Zeeb 
76*28348caeSBjoern A. Zeeb 	if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) ||
77*28348caeSBjoern A. Zeeb 	    nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
78*28348caeSBjoern A. Zeeb 	    nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) {
79*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "failed to populate testmode unsegmented event\n");
80dd4f32aeSBjoern A. Zeeb 		kfree_skb(nl_skb);
81dd4f32aeSBjoern A. Zeeb 		goto out;
82dd4f32aeSBjoern A. Zeeb 	}
83dd4f32aeSBjoern A. Zeeb 
84*28348caeSBjoern A. Zeeb 	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
85*28348caeSBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
86*28348caeSBjoern A. Zeeb 	return;
87*28348caeSBjoern A. Zeeb 
88*28348caeSBjoern A. Zeeb out:
89*28348caeSBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
90*28348caeSBjoern A. Zeeb 	ath11k_warn(ab, "Failed to send testmode event to higher layers\n");
91*28348caeSBjoern A. Zeeb }
92*28348caeSBjoern A. Zeeb 
93*28348caeSBjoern A. Zeeb /* This function handles segmented events. Data of various events received
94*28348caeSBjoern A. Zeeb  * from firmware is aggregated and sent to application layer
95*28348caeSBjoern A. Zeeb  */
ath11k_tm_process_event(struct ath11k_base * ab,u32 cmd_id,const struct wmi_ftm_event_msg * ftm_msg,u16 length)96*28348caeSBjoern A. Zeeb static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
97*28348caeSBjoern A. Zeeb 				   const struct wmi_ftm_event_msg *ftm_msg,
98*28348caeSBjoern A. Zeeb 				   u16 length)
99*28348caeSBjoern A. Zeeb {
100*28348caeSBjoern A. Zeeb 	struct sk_buff *nl_skb;
101*28348caeSBjoern A. Zeeb 	int ret = 0;
102*28348caeSBjoern A. Zeeb 	struct ath11k *ar;
103*28348caeSBjoern A. Zeeb 	u8 const *buf_pos;
104*28348caeSBjoern A. Zeeb 	u16 datalen;
105*28348caeSBjoern A. Zeeb 	u8 total_segments, current_seq;
106*28348caeSBjoern A. Zeeb 	u32 data_pos;
107*28348caeSBjoern A. Zeeb 	u32 pdev_id;
108*28348caeSBjoern A. Zeeb 
109*28348caeSBjoern A. Zeeb 	ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
110*28348caeSBjoern A. Zeeb 		   "event wmi cmd_id %d ftm event msg %pK datalen %d\n",
111*28348caeSBjoern A. Zeeb 		   cmd_id, ftm_msg, length);
112*28348caeSBjoern A. Zeeb 	ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length);
113*28348caeSBjoern A. Zeeb 	pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id);
114*28348caeSBjoern A. Zeeb 
115*28348caeSBjoern A. Zeeb 	if (pdev_id >= ab->num_radios) {
116*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "testmode event not handled due to invalid pdev id: %d\n",
117*28348caeSBjoern A. Zeeb 			    pdev_id);
118*28348caeSBjoern A. Zeeb 		return -EINVAL;
119*28348caeSBjoern A. Zeeb 	}
120*28348caeSBjoern A. Zeeb 
121*28348caeSBjoern A. Zeeb 	ar = ab->pdevs[pdev_id].ar;
122*28348caeSBjoern A. Zeeb 	if (!ar) {
123*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "testmode event not handled due to absence of pdev\n");
124*28348caeSBjoern A. Zeeb 		return -ENODEV;
125*28348caeSBjoern A. Zeeb 	}
126*28348caeSBjoern A. Zeeb 
127*28348caeSBjoern A. Zeeb 	current_seq = FIELD_GET(ATH11K_FTM_SEGHDR_CURRENT_SEQ,
128*28348caeSBjoern A. Zeeb 				ftm_msg->seg_hdr.segmentinfo);
129*28348caeSBjoern A. Zeeb 	total_segments = FIELD_GET(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS,
130*28348caeSBjoern A. Zeeb 				   ftm_msg->seg_hdr.segmentinfo);
131*28348caeSBjoern A. Zeeb 	datalen = length - (sizeof(struct wmi_ftm_seg_hdr));
132*28348caeSBjoern A. Zeeb 	buf_pos = ftm_msg->data;
133*28348caeSBjoern A. Zeeb 
134*28348caeSBjoern A. Zeeb 	spin_lock_bh(&ar->data_lock);
135*28348caeSBjoern A. Zeeb 
136*28348caeSBjoern A. Zeeb 	if (current_seq == 0) {
137*28348caeSBjoern A. Zeeb 		ab->testmode.expected_seq = 0;
138*28348caeSBjoern A. Zeeb 		ab->testmode.data_pos = 0;
139*28348caeSBjoern A. Zeeb 	}
140*28348caeSBjoern A. Zeeb 
141*28348caeSBjoern A. Zeeb 	data_pos = ab->testmode.data_pos;
142*28348caeSBjoern A. Zeeb 
143*28348caeSBjoern A. Zeeb 	if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) {
144*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "Invalid ftm event length at %d: %d\n",
145*28348caeSBjoern A. Zeeb 			    data_pos, datalen);
146*28348caeSBjoern A. Zeeb 		ret = -EINVAL;
147dd4f32aeSBjoern A. Zeeb 		goto out;
148dd4f32aeSBjoern A. Zeeb 	}
149dd4f32aeSBjoern A. Zeeb 
150*28348caeSBjoern A. Zeeb 	memcpy(&ab->testmode.eventdata[data_pos], buf_pos, datalen);
151*28348caeSBjoern A. Zeeb 	data_pos += datalen;
152*28348caeSBjoern A. Zeeb 
153*28348caeSBjoern A. Zeeb 	if (++ab->testmode.expected_seq != total_segments) {
154*28348caeSBjoern A. Zeeb 		ab->testmode.data_pos = data_pos;
155*28348caeSBjoern A. Zeeb 		ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
156*28348caeSBjoern A. Zeeb 			   "partial data received current_seq %d total_seg %d\n",
157*28348caeSBjoern A. Zeeb 			    current_seq, total_segments);
158*28348caeSBjoern A. Zeeb 		goto out;
159*28348caeSBjoern A. Zeeb 	}
160*28348caeSBjoern A. Zeeb 
161*28348caeSBjoern A. Zeeb 	ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
162*28348caeSBjoern A. Zeeb 		   "total data length pos %d len %d\n",
163*28348caeSBjoern A. Zeeb 		    data_pos, ftm_msg->seg_hdr.len);
164*28348caeSBjoern A. Zeeb 	nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
165*28348caeSBjoern A. Zeeb 						   2 * nla_total_size(sizeof(u32)) +
166*28348caeSBjoern A. Zeeb 						   nla_total_size(data_pos),
167*28348caeSBjoern A. Zeeb 						   GFP_ATOMIC);
168*28348caeSBjoern A. Zeeb 	if (!nl_skb) {
169*28348caeSBjoern A. Zeeb 		ath11k_warn(ab,
170*28348caeSBjoern A. Zeeb 			    "failed to allocate skb for segmented testmode wmi event\n");
171*28348caeSBjoern A. Zeeb 		ret = -ENOMEM;
172*28348caeSBjoern A. Zeeb 		goto out;
173*28348caeSBjoern A. Zeeb 	}
174*28348caeSBjoern A. Zeeb 
175*28348caeSBjoern A. Zeeb 	if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD,
176*28348caeSBjoern A. Zeeb 			ATH11K_TM_CMD_WMI_FTM) ||
177*28348caeSBjoern A. Zeeb 	    nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) ||
178*28348caeSBjoern A. Zeeb 	    nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos,
179*28348caeSBjoern A. Zeeb 		    &ab->testmode.eventdata[0])) {
180*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "failed to populate segmented testmode event");
181dd4f32aeSBjoern A. Zeeb 		kfree_skb(nl_skb);
182*28348caeSBjoern A. Zeeb 		ret = -ENOBUFS;
183dd4f32aeSBjoern A. Zeeb 		goto out;
184dd4f32aeSBjoern A. Zeeb 	}
185dd4f32aeSBjoern A. Zeeb 
186dd4f32aeSBjoern A. Zeeb 	cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
187dd4f32aeSBjoern A. Zeeb 
188dd4f32aeSBjoern A. Zeeb out:
189dd4f32aeSBjoern A. Zeeb 	spin_unlock_bh(&ar->data_lock);
190*28348caeSBjoern A. Zeeb 	return ret;
191*28348caeSBjoern A. Zeeb }
192dd4f32aeSBjoern A. Zeeb 
ath11k_tm_wmi_event_segmented(struct ath11k_base * ab,u32 cmd_id,struct sk_buff * skb)193*28348caeSBjoern A. Zeeb static void ath11k_tm_wmi_event_segmented(struct ath11k_base *ab, u32 cmd_id,
194*28348caeSBjoern A. Zeeb 					  struct sk_buff *skb)
195*28348caeSBjoern A. Zeeb {
196*28348caeSBjoern A. Zeeb 	const void **tb;
197*28348caeSBjoern A. Zeeb 	const struct wmi_ftm_event_msg *ev;
198*28348caeSBjoern A. Zeeb 	u16 length;
199*28348caeSBjoern A. Zeeb 	int ret;
200*28348caeSBjoern A. Zeeb 
201*28348caeSBjoern A. Zeeb 	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
202*28348caeSBjoern A. Zeeb 	if (IS_ERR(tb)) {
203*28348caeSBjoern A. Zeeb 		ret = PTR_ERR(tb);
204*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
205*28348caeSBjoern A. Zeeb 		return;
206*28348caeSBjoern A. Zeeb 	}
207*28348caeSBjoern A. Zeeb 
208*28348caeSBjoern A. Zeeb 	ev = tb[WMI_TAG_ARRAY_BYTE];
209*28348caeSBjoern A. Zeeb 	if (!ev) {
210*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "failed to fetch ftm msg\n");
211*28348caeSBjoern A. Zeeb 		kfree(tb);
212*28348caeSBjoern A. Zeeb 		return;
213*28348caeSBjoern A. Zeeb 	}
214*28348caeSBjoern A. Zeeb 
215*28348caeSBjoern A. Zeeb 	length = skb->len - TLV_HDR_SIZE;
216*28348caeSBjoern A. Zeeb 	ret = ath11k_tm_process_event(ab, cmd_id, ev, length);
217*28348caeSBjoern A. Zeeb 	if (ret)
218*28348caeSBjoern A. Zeeb 		ath11k_warn(ab, "Failed to process ftm event\n");
219*28348caeSBjoern A. Zeeb 
220*28348caeSBjoern A. Zeeb 	kfree(tb);
221*28348caeSBjoern A. Zeeb }
222*28348caeSBjoern A. Zeeb 
ath11k_tm_wmi_event(struct ath11k_base * ab,u32 cmd_id,struct sk_buff * skb)223*28348caeSBjoern A. Zeeb void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id, struct sk_buff *skb)
224*28348caeSBjoern A. Zeeb {
225*28348caeSBjoern A. Zeeb 	if (test_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags))
226*28348caeSBjoern A. Zeeb 		ath11k_tm_wmi_event_segmented(ab, cmd_id, skb);
227*28348caeSBjoern A. Zeeb 	else
228*28348caeSBjoern A. Zeeb 		ath11k_tm_wmi_event_unsegmented(ab, cmd_id, skb);
229dd4f32aeSBjoern A. Zeeb }
230dd4f32aeSBjoern A. Zeeb 
ath11k_tm_cmd_get_version(struct ath11k * ar,struct nlattr * tb[])231dd4f32aeSBjoern A. Zeeb static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[])
232dd4f32aeSBjoern A. Zeeb {
233dd4f32aeSBjoern A. Zeeb 	struct sk_buff *skb;
234dd4f32aeSBjoern A. Zeeb 	int ret;
235dd4f32aeSBjoern A. Zeeb 
236dd4f32aeSBjoern A. Zeeb 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
237*28348caeSBjoern A. Zeeb 		   "cmd get version_major %d version_minor %d\n",
238dd4f32aeSBjoern A. Zeeb 		   ATH11K_TESTMODE_VERSION_MAJOR,
239dd4f32aeSBjoern A. Zeeb 		   ATH11K_TESTMODE_VERSION_MINOR);
240dd4f32aeSBjoern A. Zeeb 
241dd4f32aeSBjoern A. Zeeb 	skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
242dd4f32aeSBjoern A. Zeeb 						nla_total_size(sizeof(u32)));
243dd4f32aeSBjoern A. Zeeb 	if (!skb)
244dd4f32aeSBjoern A. Zeeb 		return -ENOMEM;
245dd4f32aeSBjoern A. Zeeb 
246dd4f32aeSBjoern A. Zeeb 	ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR,
247dd4f32aeSBjoern A. Zeeb 			  ATH11K_TESTMODE_VERSION_MAJOR);
248dd4f32aeSBjoern A. Zeeb 	if (ret) {
249dd4f32aeSBjoern A. Zeeb 		kfree_skb(skb);
250dd4f32aeSBjoern A. Zeeb 		return ret;
251dd4f32aeSBjoern A. Zeeb 	}
252dd4f32aeSBjoern A. Zeeb 
253dd4f32aeSBjoern A. Zeeb 	ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR,
254dd4f32aeSBjoern A. Zeeb 			  ATH11K_TESTMODE_VERSION_MINOR);
255dd4f32aeSBjoern A. Zeeb 	if (ret) {
256dd4f32aeSBjoern A. Zeeb 		kfree_skb(skb);
257dd4f32aeSBjoern A. Zeeb 		return ret;
258dd4f32aeSBjoern A. Zeeb 	}
259dd4f32aeSBjoern A. Zeeb 
260dd4f32aeSBjoern A. Zeeb 	return cfg80211_testmode_reply(skb);
261dd4f32aeSBjoern A. Zeeb }
262dd4f32aeSBjoern A. Zeeb 
ath11k_tm_cmd_testmode_start(struct ath11k * ar,struct nlattr * tb[])263*28348caeSBjoern A. Zeeb static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[])
264dd4f32aeSBjoern A. Zeeb {
265dd4f32aeSBjoern A. Zeeb 	int ret;
266dd4f32aeSBjoern A. Zeeb 
267dd4f32aeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
268dd4f32aeSBjoern A. Zeeb 
269*28348caeSBjoern A. Zeeb 	if (ar->state == ATH11K_STATE_FTM) {
270*28348caeSBjoern A. Zeeb 		ret = -EALREADY;
271*28348caeSBjoern A. Zeeb 		goto err;
272dd4f32aeSBjoern A. Zeeb 	}
273dd4f32aeSBjoern A. Zeeb 
274*28348caeSBjoern A. Zeeb 	/* start utf only when the driver is not in use  */
275*28348caeSBjoern A. Zeeb 	if (ar->state != ATH11K_STATE_OFF) {
276*28348caeSBjoern A. Zeeb 		ret = -EBUSY;
277*28348caeSBjoern A. Zeeb 		goto err;
278*28348caeSBjoern A. Zeeb 	}
279*28348caeSBjoern A. Zeeb 
280*28348caeSBjoern A. Zeeb 	ar->ab->testmode.eventdata = kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH,
281*28348caeSBjoern A. Zeeb 					     GFP_KERNEL);
282*28348caeSBjoern A. Zeeb 	if (!ar->ab->testmode.eventdata) {
283*28348caeSBjoern A. Zeeb 		ret = -ENOMEM;
284*28348caeSBjoern A. Zeeb 		goto err;
285*28348caeSBjoern A. Zeeb 	}
286*28348caeSBjoern A. Zeeb 
287*28348caeSBjoern A. Zeeb 	ar->state = ATH11K_STATE_FTM;
288*28348caeSBjoern A. Zeeb 	ar->ftm_msgref = 0;
289*28348caeSBjoern A. Zeeb 
290*28348caeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
291*28348caeSBjoern A. Zeeb 
292*28348caeSBjoern A. Zeeb 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "cmd start\n");
293*28348caeSBjoern A. Zeeb 	return 0;
294*28348caeSBjoern A. Zeeb 
295*28348caeSBjoern A. Zeeb err:
296*28348caeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
297*28348caeSBjoern A. Zeeb 	return ret;
298*28348caeSBjoern A. Zeeb }
299*28348caeSBjoern A. Zeeb 
ath11k_tm_cmd_wmi(struct ath11k * ar,struct nlattr * tb[],struct ieee80211_vif * vif)300*28348caeSBjoern A. Zeeb static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[],
301*28348caeSBjoern A. Zeeb 			     struct ieee80211_vif *vif)
302*28348caeSBjoern A. Zeeb {
303*28348caeSBjoern A. Zeeb 	struct ath11k_pdev_wmi *wmi = ar->wmi;
304*28348caeSBjoern A. Zeeb 	struct sk_buff *skb;
305*28348caeSBjoern A. Zeeb 	struct ath11k_vif *arvif;
306*28348caeSBjoern A. Zeeb 	u32 cmd_id, buf_len;
307*28348caeSBjoern A. Zeeb 	int ret, tag;
308*28348caeSBjoern A. Zeeb 	void *buf;
309*28348caeSBjoern A. Zeeb 	u32 *ptr;
310*28348caeSBjoern A. Zeeb 
311*28348caeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
312*28348caeSBjoern A. Zeeb 
313dd4f32aeSBjoern A. Zeeb 	if (!tb[ATH11K_TM_ATTR_DATA]) {
314dd4f32aeSBjoern A. Zeeb 		ret = -EINVAL;
315dd4f32aeSBjoern A. Zeeb 		goto out;
316dd4f32aeSBjoern A. Zeeb 	}
317dd4f32aeSBjoern A. Zeeb 
318dd4f32aeSBjoern A. Zeeb 	if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) {
319dd4f32aeSBjoern A. Zeeb 		ret = -EINVAL;
320dd4f32aeSBjoern A. Zeeb 		goto out;
321dd4f32aeSBjoern A. Zeeb 	}
322dd4f32aeSBjoern A. Zeeb 
323dd4f32aeSBjoern A. Zeeb 	buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
324dd4f32aeSBjoern A. Zeeb 	buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
325*28348caeSBjoern A. Zeeb 	if (!buf_len) {
326*28348caeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "No data present in testmode wmi command\n");
327*28348caeSBjoern A. Zeeb 		ret = -EINVAL;
328*28348caeSBjoern A. Zeeb 		goto out;
329*28348caeSBjoern A. Zeeb 	}
330*28348caeSBjoern A. Zeeb 
331dd4f32aeSBjoern A. Zeeb 	cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]);
332dd4f32aeSBjoern A. Zeeb 
333*28348caeSBjoern A. Zeeb 	/* Make sure that the buffer length is long enough to
334*28348caeSBjoern A. Zeeb 	 * hold TLV and pdev/vdev id.
335*28348caeSBjoern A. Zeeb 	 */
336*28348caeSBjoern A. Zeeb 	if (buf_len < sizeof(struct wmi_tlv) + sizeof(u32)) {
337*28348caeSBjoern A. Zeeb 		ret = -EINVAL;
338*28348caeSBjoern A. Zeeb 		goto out;
339*28348caeSBjoern A. Zeeb 	}
340*28348caeSBjoern A. Zeeb 
341*28348caeSBjoern A. Zeeb 	ptr = buf;
342*28348caeSBjoern A. Zeeb 	tag = FIELD_GET(WMI_TLV_TAG, *ptr);
343*28348caeSBjoern A. Zeeb 
344*28348caeSBjoern A. Zeeb 	/* pdev/vdev id start after TLV header */
345*28348caeSBjoern A. Zeeb 	ptr++;
346*28348caeSBjoern A. Zeeb 
347*28348caeSBjoern A. Zeeb 	if (tag == WMI_TAG_PDEV_SET_PARAM_CMD)
348*28348caeSBjoern A. Zeeb 		*ptr = ar->pdev->pdev_id;
349*28348caeSBjoern A. Zeeb 
350*28348caeSBjoern A. Zeeb 	if (ar->ab->fw_mode != ATH11K_FIRMWARE_MODE_FTM &&
351*28348caeSBjoern A. Zeeb 	    (tag == WMI_TAG_VDEV_SET_PARAM_CMD || tag == WMI_TAG_UNIT_TEST_CMD)) {
352*28348caeSBjoern A. Zeeb 		if (vif) {
353*28348caeSBjoern A. Zeeb 			arvif = (struct ath11k_vif *)vif->drv_priv;
354*28348caeSBjoern A. Zeeb 			*ptr = arvif->vdev_id;
355*28348caeSBjoern A. Zeeb 		} else {
356*28348caeSBjoern A. Zeeb 			ret = -EINVAL;
357*28348caeSBjoern A. Zeeb 			goto out;
358*28348caeSBjoern A. Zeeb 		}
359*28348caeSBjoern A. Zeeb 	}
360*28348caeSBjoern A. Zeeb 
361dd4f32aeSBjoern A. Zeeb 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
362*28348caeSBjoern A. Zeeb 		   "cmd wmi cmd_id %d buf length %d\n",
363*28348caeSBjoern A. Zeeb 		   cmd_id, buf_len);
364dd4f32aeSBjoern A. Zeeb 
365dd4f32aeSBjoern A. Zeeb 	ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
366dd4f32aeSBjoern A. Zeeb 
367dd4f32aeSBjoern A. Zeeb 	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
368dd4f32aeSBjoern A. Zeeb 	if (!skb) {
369dd4f32aeSBjoern A. Zeeb 		ret = -ENOMEM;
370dd4f32aeSBjoern A. Zeeb 		goto out;
371dd4f32aeSBjoern A. Zeeb 	}
372dd4f32aeSBjoern A. Zeeb 
373dd4f32aeSBjoern A. Zeeb 	memcpy(skb->data, buf, buf_len);
374dd4f32aeSBjoern A. Zeeb 
375dd4f32aeSBjoern A. Zeeb 	ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
376dd4f32aeSBjoern A. Zeeb 	if (ret) {
377dd4f32aeSBjoern A. Zeeb 		dev_kfree_skb(skb);
378dd4f32aeSBjoern A. Zeeb 		ath11k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n",
379dd4f32aeSBjoern A. Zeeb 			    ret);
380dd4f32aeSBjoern A. Zeeb 		goto out;
381dd4f32aeSBjoern A. Zeeb 	}
382dd4f32aeSBjoern A. Zeeb 
383dd4f32aeSBjoern A. Zeeb 	ret = 0;
384dd4f32aeSBjoern A. Zeeb 
385dd4f32aeSBjoern A. Zeeb out:
386dd4f32aeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
387dd4f32aeSBjoern A. Zeeb 	return ret;
388dd4f32aeSBjoern A. Zeeb }
389dd4f32aeSBjoern A. Zeeb 
ath11k_tm_cmd_wmi_ftm(struct ath11k * ar,struct nlattr * tb[])390*28348caeSBjoern A. Zeeb static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[])
391*28348caeSBjoern A. Zeeb {
392*28348caeSBjoern A. Zeeb 	struct ath11k_pdev_wmi *wmi = ar->wmi;
393*28348caeSBjoern A. Zeeb 	struct ath11k_base *ab = ar->ab;
394*28348caeSBjoern A. Zeeb 	struct sk_buff *skb;
395*28348caeSBjoern A. Zeeb 	u32 cmd_id, buf_len, hdr_info;
396*28348caeSBjoern A. Zeeb 	int ret;
397*28348caeSBjoern A. Zeeb 	void *buf;
398*28348caeSBjoern A. Zeeb 	u8 segnumber = 0, seginfo;
399*28348caeSBjoern A. Zeeb 	u16 chunk_len, total_bytes, num_segments;
400*28348caeSBjoern A. Zeeb 	u8 *bufpos;
401*28348caeSBjoern A. Zeeb 	struct wmi_ftm_cmd *ftm_cmd;
402*28348caeSBjoern A. Zeeb 
403*28348caeSBjoern A. Zeeb 	set_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags);
404*28348caeSBjoern A. Zeeb 
405*28348caeSBjoern A. Zeeb 	mutex_lock(&ar->conf_mutex);
406*28348caeSBjoern A. Zeeb 
407*28348caeSBjoern A. Zeeb 	if (ar->state != ATH11K_STATE_FTM) {
408*28348caeSBjoern A. Zeeb 		ret = -ENETDOWN;
409*28348caeSBjoern A. Zeeb 		goto out;
410*28348caeSBjoern A. Zeeb 	}
411*28348caeSBjoern A. Zeeb 
412*28348caeSBjoern A. Zeeb 	if (!tb[ATH11K_TM_ATTR_DATA]) {
413*28348caeSBjoern A. Zeeb 		ret = -EINVAL;
414*28348caeSBjoern A. Zeeb 		goto out;
415*28348caeSBjoern A. Zeeb 	}
416*28348caeSBjoern A. Zeeb 
417*28348caeSBjoern A. Zeeb 	buf = nla_data(tb[ATH11K_TM_ATTR_DATA]);
418*28348caeSBjoern A. Zeeb 	buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]);
419*28348caeSBjoern A. Zeeb 	cmd_id = WMI_PDEV_UTF_CMDID;
420*28348caeSBjoern A. Zeeb 
421*28348caeSBjoern A. Zeeb 	ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
422*28348caeSBjoern A. Zeeb 		   "cmd wmi ftm cmd_id %d buffer length %d\n",
423*28348caeSBjoern A. Zeeb 		   cmd_id, buf_len);
424*28348caeSBjoern A. Zeeb 	ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
425*28348caeSBjoern A. Zeeb 
426*28348caeSBjoern A. Zeeb 	bufpos = buf;
427*28348caeSBjoern A. Zeeb 	total_bytes = buf_len;
428*28348caeSBjoern A. Zeeb 	num_segments = total_bytes / MAX_WMI_UTF_LEN;
429*28348caeSBjoern A. Zeeb 
430*28348caeSBjoern A. Zeeb 	if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
431*28348caeSBjoern A. Zeeb 		num_segments++;
432*28348caeSBjoern A. Zeeb 
433*28348caeSBjoern A. Zeeb 	while (buf_len) {
434*28348caeSBjoern A. Zeeb 		chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
435*28348caeSBjoern A. Zeeb 
436*28348caeSBjoern A. Zeeb 		skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len +
437*28348caeSBjoern A. Zeeb 					   sizeof(struct wmi_ftm_cmd)));
438*28348caeSBjoern A. Zeeb 		if (!skb) {
439*28348caeSBjoern A. Zeeb 			ret = -ENOMEM;
440*28348caeSBjoern A. Zeeb 			goto out;
441*28348caeSBjoern A. Zeeb 		}
442*28348caeSBjoern A. Zeeb 
443*28348caeSBjoern A. Zeeb 		ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
444*28348caeSBjoern A. Zeeb 		hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
445*28348caeSBjoern A. Zeeb 			   FIELD_PREP(WMI_TLV_LEN, (chunk_len +
446*28348caeSBjoern A. Zeeb 				      sizeof(struct wmi_ftm_seg_hdr)));
447*28348caeSBjoern A. Zeeb 		ftm_cmd->tlv_header = hdr_info;
448*28348caeSBjoern A. Zeeb 		ftm_cmd->seg_hdr.len = total_bytes;
449*28348caeSBjoern A. Zeeb 		ftm_cmd->seg_hdr.msgref = ar->ftm_msgref;
450*28348caeSBjoern A. Zeeb 		seginfo = FIELD_PREP(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
451*28348caeSBjoern A. Zeeb 			  FIELD_PREP(ATH11K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
452*28348caeSBjoern A. Zeeb 		ftm_cmd->seg_hdr.segmentinfo = seginfo;
453*28348caeSBjoern A. Zeeb 		segnumber++;
454*28348caeSBjoern A. Zeeb 
455*28348caeSBjoern A. Zeeb 		memcpy(&ftm_cmd->data, bufpos, chunk_len);
456*28348caeSBjoern A. Zeeb 
457*28348caeSBjoern A. Zeeb 		ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
458*28348caeSBjoern A. Zeeb 		if (ret) {
459*28348caeSBjoern A. Zeeb 			ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret);
460*28348caeSBjoern A. Zeeb 			goto out;
461*28348caeSBjoern A. Zeeb 		}
462*28348caeSBjoern A. Zeeb 
463*28348caeSBjoern A. Zeeb 		buf_len -= chunk_len;
464*28348caeSBjoern A. Zeeb 		bufpos += chunk_len;
465*28348caeSBjoern A. Zeeb 	}
466*28348caeSBjoern A. Zeeb 
467*28348caeSBjoern A. Zeeb 	ar->ftm_msgref++;
468*28348caeSBjoern A. Zeeb 	ret = 0;
469*28348caeSBjoern A. Zeeb 
470*28348caeSBjoern A. Zeeb out:
471*28348caeSBjoern A. Zeeb 	mutex_unlock(&ar->conf_mutex);
472*28348caeSBjoern A. Zeeb 	return ret;
473*28348caeSBjoern A. Zeeb }
474*28348caeSBjoern A. Zeeb 
ath11k_tm_cmd(struct ieee80211_hw * hw,struct ieee80211_vif * vif,void * data,int len)475dd4f32aeSBjoern A. Zeeb int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
476dd4f32aeSBjoern A. Zeeb 		  void *data, int len)
477dd4f32aeSBjoern A. Zeeb {
478dd4f32aeSBjoern A. Zeeb 	struct ath11k *ar = hw->priv;
479dd4f32aeSBjoern A. Zeeb 	struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1];
480dd4f32aeSBjoern A. Zeeb 	int ret;
481dd4f32aeSBjoern A. Zeeb 
482dd4f32aeSBjoern A. Zeeb 	ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy,
483dd4f32aeSBjoern A. Zeeb 			NULL);
484dd4f32aeSBjoern A. Zeeb 	if (ret)
485dd4f32aeSBjoern A. Zeeb 		return ret;
486dd4f32aeSBjoern A. Zeeb 
487dd4f32aeSBjoern A. Zeeb 	if (!tb[ATH11K_TM_ATTR_CMD])
488dd4f32aeSBjoern A. Zeeb 		return -EINVAL;
489dd4f32aeSBjoern A. Zeeb 
490dd4f32aeSBjoern A. Zeeb 	switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) {
491dd4f32aeSBjoern A. Zeeb 	case ATH11K_TM_CMD_GET_VERSION:
492dd4f32aeSBjoern A. Zeeb 		return ath11k_tm_cmd_get_version(ar, tb);
493dd4f32aeSBjoern A. Zeeb 	case ATH11K_TM_CMD_WMI:
494*28348caeSBjoern A. Zeeb 		return ath11k_tm_cmd_wmi(ar, tb, vif);
495*28348caeSBjoern A. Zeeb 	case ATH11K_TM_CMD_TESTMODE_START:
496*28348caeSBjoern A. Zeeb 		return ath11k_tm_cmd_testmode_start(ar, tb);
497*28348caeSBjoern A. Zeeb 	case ATH11K_TM_CMD_WMI_FTM:
498*28348caeSBjoern A. Zeeb 		return ath11k_tm_cmd_wmi_ftm(ar, tb);
499dd4f32aeSBjoern A. Zeeb 	default:
500dd4f32aeSBjoern A. Zeeb 		return -EOPNOTSUPP;
501dd4f32aeSBjoern A. Zeeb 	}
502dd4f32aeSBjoern A. Zeeb }
503