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