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