Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/mld/debugfs.c
48287 views
1
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2
/*
3
* Copyright (C) 2024-2025 Intel Corporation
4
*/
5
6
#include "mld.h"
7
#include "debugfs.h"
8
#include "iwl-io.h"
9
#include "hcmd.h"
10
#include "iface.h"
11
#include "sta.h"
12
#include "tlc.h"
13
#include "power.h"
14
#include "notif.h"
15
#include "ap.h"
16
#include "iwl-utils.h"
17
#include "scan.h"
18
#ifdef CONFIG_THERMAL
19
#include "thermal.h"
20
#endif
21
22
#include "fw/api/rs.h"
23
#include "fw/api/dhc.h"
24
#include "fw/api/rfi.h"
25
#include "fw/dhc-utils.h"
26
#include <linux/dmi.h>
27
28
#define MLD_DEBUGFS_READ_FILE_OPS(name, bufsz) \
29
_MLD_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_mld)
30
31
#define MLD_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
32
debugfs_create_file(alias, mode, parent, mld, \
33
&iwl_dbgfs_##name##_ops)
34
#define MLD_DEBUGFS_ADD_FILE(name, parent, mode) \
35
MLD_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
36
37
static bool iwl_mld_dbgfs_fw_cmd_disabled(struct iwl_mld *mld)
38
{
39
#ifdef CONFIG_PM_SLEEP
40
return !mld->fw_status.running || mld->fw_status.in_d3;
41
#else
42
return !mld->fw_status.running;
43
#endif /* CONFIG_PM_SLEEP */
44
}
45
46
static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mld *mld,
47
char *buf, size_t count)
48
{
49
/* If the firmware is not running, silently succeed since there is
50
* no data to clear.
51
*/
52
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
53
return 0;
54
55
iwl_fw_dbg_clear_monitor_buf(&mld->fwrt);
56
57
return count;
58
}
59
60
static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mld *mld, char *buf,
61
size_t count)
62
{
63
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
64
return -EIO;
65
66
IWL_ERR(mld, "Triggering an NMI from debugfs\n");
67
68
if (count == 6 && !strcmp(buf, "nolog\n"))
69
mld->fw_status.do_not_dump_once = true;
70
71
iwl_force_nmi(mld->trans);
72
73
return count;
74
}
75
76
static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf,
77
size_t count)
78
{
79
int __maybe_unused ret;
80
81
if (!iwlwifi_mod_params.fw_restart)
82
return -EPERM;
83
84
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
85
return -EIO;
86
87
if (count == 6 && !strcmp(buf, "nolog\n")) {
88
mld->fw_status.do_not_dump_once = true;
89
iwl_trans_suppress_cmd_error_once(mld->trans);
90
}
91
92
/* take the return value to make compiler happy - it will
93
* fail anyway
94
*/
95
ret = iwl_mld_send_cmd_empty(mld, WIDE_ID(LONG_GROUP, REPLY_ERROR));
96
97
return count;
98
}
99
100
static ssize_t iwl_dbgfs_send_echo_cmd_write(struct iwl_mld *mld, char *buf,
101
size_t count)
102
{
103
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
104
return -EIO;
105
106
return iwl_mld_send_cmd_empty(mld, ECHO_CMD) ?: count;
107
}
108
109
struct iwl_mld_sniffer_apply {
110
struct iwl_mld *mld;
111
const u8 *bssid;
112
u16 aid;
113
};
114
115
static bool iwl_mld_sniffer_apply(struct iwl_notif_wait_data *notif_data,
116
struct iwl_rx_packet *pkt, void *data)
117
{
118
struct iwl_mld_sniffer_apply *apply = data;
119
120
apply->mld->monitor.cur_aid = cpu_to_le16(apply->aid);
121
memcpy(apply->mld->monitor.cur_bssid, apply->bssid,
122
sizeof(apply->mld->monitor.cur_bssid));
123
124
return true;
125
}
126
127
static ssize_t
128
iwl_dbgfs_he_sniffer_params_write(struct iwl_mld *mld, char *buf,
129
size_t count)
130
{
131
struct iwl_notification_wait wait;
132
struct iwl_he_monitor_cmd he_mon_cmd = {};
133
struct iwl_mld_sniffer_apply apply = {
134
.mld = mld,
135
};
136
u16 wait_cmds[] = {
137
WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD),
138
};
139
u32 aid;
140
int ret;
141
142
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
143
return -EIO;
144
145
if (!mld->monitor.on)
146
return -ENODEV;
147
148
ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid,
149
&he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1],
150
&he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3],
151
&he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]);
152
if (ret != 7)
153
return -EINVAL;
154
155
he_mon_cmd.aid = cpu_to_le16(aid);
156
157
apply.aid = aid;
158
apply.bssid = (void *)he_mon_cmd.bssid;
159
160
/* Use the notification waiter to get our function triggered
161
* in sequence with other RX. This ensures that frames we get
162
* on the RX queue _before_ the new configuration is applied
163
* still have mld->cur_aid pointing to the old AID, and that
164
* frames on the RX queue _after_ the firmware processed the
165
* new configuration (and sent the response, synchronously)
166
* get mld->cur_aid correctly set to the new AID.
167
*/
168
iwl_init_notification_wait(&mld->notif_wait, &wait,
169
wait_cmds, ARRAY_SIZE(wait_cmds),
170
iwl_mld_sniffer_apply, &apply);
171
172
ret = iwl_mld_send_cmd_pdu(mld,
173
WIDE_ID(DATA_PATH_GROUP,
174
HE_AIR_SNIFFER_CONFIG_CMD),
175
&he_mon_cmd);
176
177
/* no need to really wait, we already did anyway */
178
iwl_remove_notification(&mld->notif_wait, &wait);
179
180
return ret ?: count;
181
}
182
183
static ssize_t
184
iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, char *buf, size_t count)
185
{
186
return scnprintf(buf, count,
187
"%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
188
le16_to_cpu(mld->monitor.cur_aid),
189
mld->monitor.cur_bssid[0], mld->monitor.cur_bssid[1],
190
mld->monitor.cur_bssid[2], mld->monitor.cur_bssid[3],
191
mld->monitor.cur_bssid[4], mld->monitor.cur_bssid[5]);
192
}
193
194
/* The size computation is as follows:
195
* each number needs at most 3 characters, number of rows is the size of
196
* the table; So, need 5 chars for the "freq: " part and each tuple afterwards
197
* needs 6 characters for numbers and 5 for the punctuation around. 32 bytes
198
* for feature support message.
199
*/
200
#define IWL_RFI_DDR_BUF_SIZE (IWL_RFI_DDR_LUT_INSTALLED_SIZE *\
201
(5 + IWL_RFI_DDR_LUT_ENTRY_CHANNELS_NUM *\
202
(6 + 5)) + 32)
203
#define IWL_RFI_DLVR_BUF_SIZE (IWL_RFI_DLVR_LUT_INSTALLED_SIZE *\
204
(5 + IWL_RFI_DLVR_LUT_ENTRY_CHANNELS_NUM *\
205
(6 + 5)) + 32)
206
#define IWL_RFI_DESENSE_BUF_SIZE IWL_RFI_DDR_BUF_SIZE
207
208
/* Extra 32 for "DDR and DLVR table" message */
209
#define IWL_RFI_BUF_SIZE (IWL_RFI_DDR_BUF_SIZE + IWL_RFI_DLVR_BUF_SIZE +\
210
IWL_RFI_DESENSE_BUF_SIZE + 32)
211
212
static size_t iwl_mld_dump_tas_resp(struct iwl_dhc_tas_status_resp *resp,
213
size_t count, u8 *buf)
214
{
215
const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = {
216
[TAS_DISABLED_DUE_TO_BIOS] =
217
"Due To BIOS",
218
[TAS_DISABLED_DUE_TO_SAR_6DBM] =
219
"Due To SAR Limit Less Than 6 dBm",
220
[TAS_DISABLED_REASON_INVALID] =
221
"N/A",
222
[TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] =
223
"Due to table source invalid"
224
};
225
const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = {
226
[TAS_DYNA_INACTIVE] = "INACTIVE",
227
[TAS_DYNA_INACTIVE_MVM_MODE] =
228
"inactive due to mvm mode",
229
[TAS_DYNA_INACTIVE_TRIGGER_MODE] =
230
"inactive due to trigger mode",
231
[TAS_DYNA_INACTIVE_BLOCK_LISTED] =
232
"inactive due to block listed",
233
[TAS_DYNA_INACTIVE_UHB_NON_US] =
234
"inactive due to uhb non US",
235
[TAS_DYNA_ACTIVE] = "ACTIVE",
236
};
237
ssize_t pos = 0;
238
239
if (resp->header.version != 1) {
240
pos += scnprintf(buf + pos, count - pos,
241
"Unsupported TAS response version:%d",
242
resp->header.version);
243
return pos;
244
}
245
246
pos += scnprintf(buf + pos, count - pos, "TAS Report\n");
247
switch (resp->tas_config_info.table_source) {
248
case BIOS_SOURCE_NONE:
249
pos += scnprintf(buf + pos, count - pos,
250
"BIOS SOURCE NONE ");
251
break;
252
case BIOS_SOURCE_ACPI:
253
pos += scnprintf(buf + pos, count - pos,
254
"BIOS SOURCE ACPI ");
255
break;
256
case BIOS_SOURCE_UEFI:
257
pos += scnprintf(buf + pos, count - pos,
258
"BIOS SOURCE UEFI ");
259
break;
260
default:
261
pos += scnprintf(buf + pos, count - pos,
262
"BIOS SOURCE UNKNOWN (%d) ",
263
resp->tas_config_info.table_source);
264
break;
265
}
266
267
pos += scnprintf(buf + pos, count - pos,
268
"revision is: %d data is: 0x%08x\n",
269
resp->tas_config_info.table_revision,
270
resp->tas_config_info.value);
271
pos += scnprintf(buf + pos, count - pos, "Current MCC: 0x%x\n",
272
le16_to_cpu(resp->curr_mcc));
273
274
pos += scnprintf(buf + pos, count - pos, "Block list entries:");
275
for (int i = 0; i < ARRAY_SIZE(resp->mcc_block_list); i++)
276
pos += scnprintf(buf + pos, count - pos, " 0x%x",
277
le16_to_cpu(resp->mcc_block_list[i]));
278
279
pos += scnprintf(buf + pos, count - pos,
280
"\nDo TAS Support Dual Radio?: %s\n",
281
hweight8(resp->valid_radio_mask) > 1 ?
282
"TRUE" : "FALSE");
283
284
for (int i = 0; i < ARRAY_SIZE(resp->tas_status_radio); i++) {
285
int tmp;
286
unsigned long dynamic_status;
287
288
if (!(resp->valid_radio_mask & BIT(i)))
289
continue;
290
291
pos += scnprintf(buf + pos, count - pos,
292
"TAS report for radio:%d\n", i + 1);
293
pos += scnprintf(buf + pos, count - pos,
294
"Static status: %sabled\n",
295
resp->tas_status_radio[i].static_status ?
296
"En" : "Dis");
297
if (!resp->tas_status_radio[i].static_status) {
298
u8 static_disable_reason =
299
resp->tas_status_radio[i].static_disable_reason;
300
301
pos += scnprintf(buf + pos, count - pos,
302
"\tStatic Disabled Reason: ");
303
if (static_disable_reason >= TAS_DISABLED_REASON_MAX) {
304
pos += scnprintf(buf + pos, count - pos,
305
"unsupported value (%d)\n",
306
static_disable_reason);
307
continue;
308
}
309
310
pos += scnprintf(buf + pos, count - pos,
311
"%s (%d)\n",
312
tas_dis_reason[static_disable_reason],
313
static_disable_reason);
314
continue;
315
}
316
317
pos += scnprintf(buf + pos, count - pos, "\tANT A %s and ",
318
(resp->tas_status_radio[i].dynamic_status_ant_a
319
& BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF");
320
321
pos += scnprintf(buf + pos, count - pos, "ANT B %s for ",
322
(resp->tas_status_radio[i].dynamic_status_ant_b
323
& BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF");
324
325
switch (resp->tas_status_radio[i].band) {
326
case PHY_BAND_5:
327
pos += scnprintf(buf + pos, count - pos, "HB\n");
328
break;
329
case PHY_BAND_24:
330
pos += scnprintf(buf + pos, count - pos, "LB\n");
331
break;
332
case PHY_BAND_6:
333
pos += scnprintf(buf + pos, count - pos, "UHB\n");
334
break;
335
default:
336
pos += scnprintf(buf + pos, count - pos,
337
"Unsupported band (%d)\n",
338
resp->tas_status_radio[i].band);
339
break;
340
}
341
342
pos += scnprintf(buf + pos, count - pos,
343
"Is near disconnection?: %s\n",
344
resp->tas_status_radio[i].near_disconnection ?
345
"True" : "False");
346
347
pos += scnprintf(buf + pos, count - pos,
348
"Dynamic status antenna A:\n");
349
dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_a;
350
for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) {
351
pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n",
352
tas_current_status[tmp], tmp);
353
}
354
pos += scnprintf(buf + pos, count - pos,
355
"\nDynamic status antenna B:\n");
356
dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_b;
357
for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) {
358
pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n",
359
tas_current_status[tmp], tmp);
360
}
361
362
tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_a);
363
pos += scnprintf(buf + pos, count - pos,
364
"Max antenna A regulatory pwr limit (dBm): %d.%03d\n",
365
tmp / 8, 125 * (tmp % 8));
366
tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_b);
367
pos += scnprintf(buf + pos, count - pos,
368
"Max antenna B regulatory pwr limit (dBm): %d.%03d\n",
369
tmp / 8, 125 * (tmp % 8));
370
371
tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_a);
372
pos += scnprintf(buf + pos, count - pos,
373
"Antenna A SAR limit (dBm): %d.%03d\n",
374
tmp / 8, 125 * (tmp % 8));
375
tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_b);
376
pos += scnprintf(buf + pos, count - pos,
377
"Antenna B SAR limit (dBm): %d.%03d\n",
378
tmp / 8, 125 * (tmp % 8));
379
}
380
381
return pos;
382
}
383
384
static ssize_t iwl_dbgfs_tas_get_status_read(struct iwl_mld *mld, char *buf,
385
size_t count)
386
{
387
struct iwl_dhc_cmd cmd = {
388
.index_and_mask = cpu_to_le32(DHC_TABLE_TOOLS |
389
DHC_TARGET_UMAC |
390
DHC_TOOLS_UMAC_GET_TAS_STATUS),
391
};
392
struct iwl_host_cmd hcmd = {
393
.id = WIDE_ID(LEGACY_GROUP, DEBUG_HOST_COMMAND),
394
.flags = CMD_WANT_SKB,
395
.len[0] = sizeof(cmd),
396
.data[0] = &cmd,
397
};
398
struct iwl_dhc_tas_status_resp *resp = NULL;
399
u32 resp_len = 0;
400
ssize_t pos = 0;
401
u32 status;
402
int ret;
403
404
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
405
return -EIO;
406
407
ret = iwl_mld_send_cmd(mld, &hcmd);
408
if (ret)
409
return ret;
410
411
pos += scnprintf(buf + pos, count - pos, "\nOEM name: %s\n",
412
dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
413
pos += scnprintf(buf + pos, count - pos,
414
"\tVendor In Approved List: %s\n",
415
iwl_is_tas_approved() ? "YES" : "NO");
416
417
status = iwl_dhc_resp_status(mld->fwrt.fw, hcmd.resp_pkt);
418
if (status != 1) {
419
pos += scnprintf(buf + pos, count - pos,
420
"response status is not success: %d\n",
421
status);
422
goto out;
423
}
424
425
resp = iwl_dhc_resp_data(mld->fwrt.fw, hcmd.resp_pkt, &resp_len);
426
if (IS_ERR(resp) || resp_len != sizeof(*resp)) {
427
pos += scnprintf(buf + pos, count - pos,
428
"Invalid size for TAS response (%u instead of %zd)\n",
429
resp_len, sizeof(*resp));
430
goto out;
431
}
432
433
pos += iwl_mld_dump_tas_resp(resp, count - pos, buf + pos);
434
435
out:
436
iwl_free_resp(&hcmd);
437
return pos;
438
}
439
440
WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_nmi, 10);
441
WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_restart, 10);
442
WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(he_sniffer_params, 32);
443
WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_dbg_clear, 10);
444
WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(send_echo_cmd, 8);
445
WIPHY_DEBUGFS_READ_FILE_OPS_MLD(tas_get_status, 2048);
446
447
static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld *mld,
448
size_t count, u8 *buf)
449
{
450
int err;
451
u32 value;
452
453
err = iwl_bios_get_dsm(&mld->fwrt, DSM_FUNC_ENABLE_6E, &value);
454
if (err)
455
return err;
456
457
return scnprintf(buf, count, "0x%08x\n", value);
458
}
459
460
MLD_DEBUGFS_READ_FILE_OPS(wifi_6e_enable, 64);
461
462
static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mld *mld,
463
char *buf, size_t count)
464
{
465
struct iwl_op_mode *opmode = container_of((void *)mld,
466
struct iwl_op_mode,
467
op_mode_specific);
468
struct iwl_rx_cmd_buffer rxb = {};
469
struct iwl_rx_packet *pkt;
470
int n_bytes = count / 2;
471
int ret = -EINVAL;
472
473
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
474
return -EIO;
475
476
rxb._page = alloc_pages(GFP_KERNEL, 0);
477
if (!rxb._page)
478
return -ENOMEM;
479
pkt = rxb_addr(&rxb);
480
481
ret = hex2bin(page_address(rxb._page), buf, n_bytes);
482
if (ret)
483
goto out;
484
485
/* avoid invalid memory access and malformed packet */
486
if (n_bytes < sizeof(*pkt) ||
487
n_bytes != sizeof(*pkt) + iwl_rx_packet_payload_len(pkt))
488
goto out;
489
490
local_bh_disable();
491
iwl_mld_rx(opmode, NULL, &rxb);
492
local_bh_enable();
493
ret = 0;
494
495
out:
496
iwl_free_rxb(&rxb);
497
498
return ret ?: count;
499
}
500
501
WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(inject_packet, 512);
502
503
#ifdef CONFIG_THERMAL
504
505
static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mld *mld,
506
char *buf, size_t count)
507
{
508
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
509
return -EIO;
510
511
return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state,
512
CTDP_CMD_OPERATION_STOP) ? : count;
513
}
514
515
WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(stop_ctdp, 8);
516
517
static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mld *mld,
518
char *buf, size_t count)
519
{
520
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
521
return -EIO;
522
523
return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state,
524
CTDP_CMD_OPERATION_START) ? : count;
525
}
526
527
WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(start_ctdp, 8);
528
529
#endif /* CONFIG_THERMAL */
530
531
void
532
iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir)
533
{
534
/* Add debugfs files here */
535
536
MLD_DEBUGFS_ADD_FILE(fw_nmi, debugfs_dir, 0200);
537
MLD_DEBUGFS_ADD_FILE(fw_restart, debugfs_dir, 0200);
538
MLD_DEBUGFS_ADD_FILE(wifi_6e_enable, debugfs_dir, 0400);
539
MLD_DEBUGFS_ADD_FILE(he_sniffer_params, debugfs_dir, 0600);
540
MLD_DEBUGFS_ADD_FILE(fw_dbg_clear, debugfs_dir, 0200);
541
MLD_DEBUGFS_ADD_FILE(send_echo_cmd, debugfs_dir, 0200);
542
MLD_DEBUGFS_ADD_FILE(tas_get_status, debugfs_dir, 0400);
543
#ifdef CONFIG_THERMAL
544
MLD_DEBUGFS_ADD_FILE(start_ctdp, debugfs_dir, 0200);
545
MLD_DEBUGFS_ADD_FILE(stop_ctdp, debugfs_dir, 0200);
546
#endif
547
MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200);
548
549
#ifdef CONFIG_PM_SLEEP
550
debugfs_create_u32("max_sleep", 0600, debugfs_dir,
551
&mld->debug_max_sleep);
552
#endif
553
554
debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir,
555
&mld->monitor.ptp_time);
556
557
/* Create a symlink with mac80211. It will be removed when mac80211
558
* exits (before the opmode exits which removes the target.)
559
*/
560
if (!IS_ERR(debugfs_dir)) {
561
char buf[100];
562
563
snprintf(buf, 100, "../../%pd2", debugfs_dir->d_parent);
564
debugfs_create_symlink("iwlwifi", mld->wiphy->debugfsdir,
565
buf);
566
}
567
}
568
569
#define VIF_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
570
WIPHY_DEBUGFS_WRITE_FILE_OPS(vif_##name, bufsz, vif)
571
572
#define VIF_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
573
IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(vif_##name, bufsz, vif) \
574
575
#define VIF_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
576
debugfs_create_file(alias, mode, parent, vif, \
577
&iwl_dbgfs_vif_##name##_ops)
578
#define VIF_DEBUGFS_ADD_FILE(name, parent, mode) \
579
VIF_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
580
581
static ssize_t iwl_dbgfs_vif_bf_params_write(struct iwl_mld *mld, char *buf,
582
size_t count, void *data)
583
{
584
struct ieee80211_vif *vif = data;
585
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
586
int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
587
struct ieee80211_bss_conf *link_conf;
588
int val;
589
590
if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
591
if (sscanf(buf + 24, "%d", &val) != 1)
592
return -EINVAL;
593
} else {
594
return -EINVAL;
595
}
596
597
if (val != 0 && val != 1)
598
return -EINVAL;
599
600
link_conf = link_conf_dereference_protected(vif, link_id);
601
if (WARN_ON(!link_conf))
602
return -ENODEV;
603
604
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
605
return -EIO;
606
607
mld_vif->disable_bf = !val;
608
609
if (val)
610
return iwl_mld_enable_beacon_filter(mld, link_conf,
611
false) ?: count;
612
else
613
return iwl_mld_disable_beacon_filter(mld, vif) ?: count;
614
}
615
616
static ssize_t iwl_dbgfs_vif_pm_params_write(struct iwl_mld *mld,
617
char *buf,
618
size_t count, void *data)
619
{
620
struct ieee80211_vif *vif = data;
621
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
622
int val;
623
624
if (!strncmp("use_ps_poll=", buf, 12)) {
625
if (sscanf(buf + 12, "%d", &val) != 1)
626
return -EINVAL;
627
} else {
628
return -EINVAL;
629
}
630
631
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
632
return -EIO;
633
634
mld_vif->use_ps_poll = val;
635
636
return iwl_mld_update_mac_power(mld, vif, false) ?: count;
637
}
638
639
static ssize_t iwl_dbgfs_vif_low_latency_write(struct iwl_mld *mld,
640
char *buf, size_t count,
641
void *data)
642
{
643
struct ieee80211_vif *vif = data;
644
u8 value;
645
int ret;
646
647
ret = kstrtou8(buf, 0, &value);
648
if (ret)
649
return ret;
650
651
if (value > 1)
652
return -EINVAL;
653
654
iwl_mld_vif_update_low_latency(mld, vif, value, LOW_LATENCY_DEBUGFS);
655
656
return count;
657
}
658
659
static ssize_t iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif *vif,
660
size_t count, char *buf)
661
{
662
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
663
char format[] = "traffic=%d\ndbgfs=%d\nvif_type=%d\nactual=%d\n";
664
u8 ll_causes;
665
666
if (WARN_ON(count < sizeof(format)))
667
return -EINVAL;
668
669
ll_causes = READ_ONCE(mld_vif->low_latency_causes);
670
671
/* all values in format are boolean so the size of format is enough
672
* for holding the result string
673
*/
674
return scnprintf(buf, count, format,
675
!!(ll_causes & LOW_LATENCY_TRAFFIC),
676
!!(ll_causes & LOW_LATENCY_DEBUGFS),
677
!!(ll_causes & LOW_LATENCY_VIF_TYPE),
678
!!(ll_causes));
679
}
680
681
VIF_DEBUGFS_WRITE_FILE_OPS(pm_params, 32);
682
VIF_DEBUGFS_WRITE_FILE_OPS(bf_params, 32);
683
VIF_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 45);
684
685
static int
686
_iwl_dbgfs_inject_beacon_ie(struct iwl_mld *mld, struct ieee80211_vif *vif,
687
char *bin, ssize_t len,
688
bool restore)
689
{
690
struct iwl_mld_vif *mld_vif;
691
struct iwl_mld_link *mld_link;
692
struct iwl_mac_beacon_cmd beacon_cmd = {};
693
int n_bytes = len / 2;
694
695
/* Element len should be represented by u8 */
696
if (n_bytes >= U8_MAX)
697
return -EINVAL;
698
699
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
700
return -EIO;
701
702
if (!vif)
703
return -EINVAL;
704
705
mld_vif = iwl_mld_vif_from_mac80211(vif);
706
mld_vif->beacon_inject_active = true;
707
mld->hw->extra_beacon_tailroom = n_bytes;
708
709
for_each_mld_vif_valid_link(mld_vif, mld_link) {
710
u32 offset;
711
struct ieee80211_tx_info *info;
712
struct ieee80211_bss_conf *link_conf =
713
link_conf_dereference_protected(vif, link_id);
714
struct ieee80211_chanctx_conf *ctx =
715
wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
716
struct sk_buff *beacon =
717
ieee80211_beacon_get_template(mld->hw, vif,
718
NULL, link_id);
719
720
if (!beacon)
721
return -EINVAL;
722
723
if (!restore && (WARN_ON(!n_bytes || !bin) ||
724
hex2bin(skb_put_zero(beacon, n_bytes),
725
bin, n_bytes))) {
726
dev_kfree_skb(beacon);
727
return -EINVAL;
728
}
729
730
info = IEEE80211_SKB_CB(beacon);
731
732
beacon_cmd.flags =
733
cpu_to_le16(iwl_mld_get_rate_flags(mld, info, vif,
734
link_conf,
735
ctx->def.chan->band));
736
beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len);
737
beacon_cmd.link_id =
738
cpu_to_le32(mld_link->fw_id);
739
740
iwl_mld_set_tim_idx(mld, &beacon_cmd.tim_idx,
741
beacon->data, beacon->len);
742
743
offset = iwl_find_ie_offset(beacon->data,
744
WLAN_EID_S1G_TWT,
745
beacon->len);
746
747
beacon_cmd.btwt_offset = cpu_to_le32(offset);
748
749
iwl_mld_send_beacon_template_cmd(mld, beacon, &beacon_cmd);
750
dev_kfree_skb(beacon);
751
}
752
753
if (restore)
754
mld_vif->beacon_inject_active = false;
755
756
return 0;
757
}
758
759
static ssize_t
760
iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld *mld,
761
char *buf, size_t count,
762
void *data)
763
{
764
struct ieee80211_vif *vif = data;
765
int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, buf,
766
count, false);
767
768
mld->hw->extra_beacon_tailroom = 0;
769
return ret ?: count;
770
}
771
772
VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512);
773
774
static ssize_t
775
iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld *mld,
776
char *buf,
777
size_t count,
778
void *data)
779
{
780
struct ieee80211_vif *vif = data;
781
int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, NULL,
782
0, true);
783
784
mld->hw->extra_beacon_tailroom = 0;
785
return ret ?: count;
786
}
787
788
VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512);
789
790
static ssize_t
791
iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count,
792
void *data)
793
{
794
struct iwl_host_cmd hcmd = {
795
.id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, DEBUG_HOST_COMMAND),
796
};
797
struct ieee80211_vif *vif = data;
798
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
799
struct iwl_dhc_cmd *cmd __free(kfree) = NULL;
800
struct iwl_dhc_twt_operation *dhc_twt_cmd;
801
u64 target_wake_time;
802
u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration;
803
u8 trigger, flow_type, flow_id, protection, tenth_param;
804
u8 twt_request = 1, broadcast = 0;
805
int ret;
806
807
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
808
return -EIO;
809
810
ret = sscanf(buf, "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu",
811
&twt_operation, &target_wake_time, &interval_exp,
812
&interval_mantissa, &min_wake_duration, &trigger,
813
&flow_type, &flow_id, &protection, &tenth_param);
814
815
/* the new twt_request parameter is optional for station */
816
if ((ret != 9 && ret != 10) ||
817
(ret == 10 && vif->type != NL80211_IFTYPE_STATION &&
818
tenth_param == 1))
819
return -EINVAL;
820
821
/* The 10th parameter:
822
* In STA mode - the TWT type (broadcast or individual)
823
* In AP mode - the role (0 responder, 2 unsolicited)
824
*/
825
if (ret == 10) {
826
if (vif->type == NL80211_IFTYPE_STATION)
827
broadcast = tenth_param;
828
else
829
twt_request = tenth_param;
830
}
831
832
cmd = kzalloc(sizeof(*cmd) + sizeof(*dhc_twt_cmd), GFP_KERNEL);
833
if (!cmd)
834
return -ENOMEM;
835
836
dhc_twt_cmd = (void *)cmd->data;
837
dhc_twt_cmd->mac_id = cpu_to_le32(mld_vif->fw_id);
838
dhc_twt_cmd->twt_operation = cpu_to_le32(twt_operation);
839
dhc_twt_cmd->target_wake_time = cpu_to_le64(target_wake_time);
840
dhc_twt_cmd->interval_exp = cpu_to_le32(interval_exp);
841
dhc_twt_cmd->interval_mantissa = cpu_to_le32(interval_mantissa);
842
dhc_twt_cmd->min_wake_duration = cpu_to_le32(min_wake_duration);
843
dhc_twt_cmd->trigger = trigger;
844
dhc_twt_cmd->flow_type = flow_type;
845
dhc_twt_cmd->flow_id = flow_id;
846
dhc_twt_cmd->protection = protection;
847
dhc_twt_cmd->twt_request = twt_request;
848
dhc_twt_cmd->negotiation_type = broadcast ? 3 : 0;
849
850
cmd->length = cpu_to_le32(sizeof(*dhc_twt_cmd) >> 2);
851
cmd->index_and_mask =
852
cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC |
853
DHC_INT_UMAC_TWT_OPERATION);
854
855
hcmd.len[0] = sizeof(*cmd) + sizeof(*dhc_twt_cmd);
856
hcmd.data[0] = cmd;
857
858
ret = iwl_mld_send_cmd(mld, &hcmd);
859
860
return ret ?: count;
861
}
862
863
VIF_DEBUGFS_WRITE_FILE_OPS(twt_setup, 256);
864
865
static ssize_t
866
iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count,
867
void *data)
868
{
869
struct ieee80211_vif *vif = data;
870
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
871
struct iwl_twt_operation_cmd twt_cmd = {};
872
int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
873
struct iwl_mld_link *mld_link = iwl_mld_link_dereference_check(mld_vif,
874
link_id);
875
int ret;
876
877
if (WARN_ON(!mld_link))
878
return -ENODEV;
879
880
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
881
return -EIO;
882
883
if (hweight16(vif->active_links) > 1)
884
return -EOPNOTSUPP;
885
886
ret = sscanf(buf,
887
"%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu",
888
&twt_cmd.twt_operation, &twt_cmd.target_wake_time,
889
&twt_cmd.interval_exponent, &twt_cmd.interval_mantissa,
890
&twt_cmd.minimum_wake_duration, &twt_cmd.trigger,
891
&twt_cmd.flow_type, &twt_cmd.flow_id,
892
&twt_cmd.twt_protection, &twt_cmd.ndp_paging_indicator,
893
&twt_cmd.responder_pm_mode, &twt_cmd.negotiation_type,
894
&twt_cmd.twt_request, &twt_cmd.implicit,
895
&twt_cmd.twt_group_assignment, &twt_cmd.twt_channel,
896
&twt_cmd.restricted_info_present, &twt_cmd.dl_bitmap_valid,
897
&twt_cmd.ul_bitmap_valid, &twt_cmd.dl_tid_bitmap,
898
&twt_cmd.ul_tid_bitmap);
899
900
if (ret != 21)
901
return -EINVAL;
902
903
twt_cmd.link_id = cpu_to_le32(mld_link->fw_id);
904
905
ret = iwl_mld_send_cmd_pdu(mld,
906
WIDE_ID(MAC_CONF_GROUP, TWT_OPERATION_CMD),
907
&twt_cmd);
908
return ret ?: count;
909
}
910
911
VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256);
912
913
static ssize_t iwl_dbgfs_vif_int_mlo_scan_write(struct iwl_mld *mld, char *buf,
914
size_t count, void *data)
915
{
916
struct ieee80211_vif *vif = data;
917
u32 action;
918
int ret;
919
920
if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
921
return -EINVAL;
922
923
if (kstrtou32(buf, 0, &action))
924
return -EINVAL;
925
926
if (action == 0) {
927
ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_INT_MLO, false);
928
} else if (action == 1) {
929
iwl_mld_int_mlo_scan(mld, vif);
930
ret = 0;
931
} else {
932
ret = -EINVAL;
933
}
934
935
return ret ?: count;
936
}
937
938
VIF_DEBUGFS_WRITE_FILE_OPS(int_mlo_scan, 32);
939
940
void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
941
struct ieee80211_vif *vif)
942
{
943
struct dentry *mld_vif_dbgfs =
944
debugfs_create_dir("iwlmld", vif->debugfs_dir);
945
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
946
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
947
char target[3 * 3 + 11 + (NL80211_WIPHY_NAME_MAXLEN + 1) +
948
(7 + IFNAMSIZ + 1) + 6 + 1];
949
char name[7 + IFNAMSIZ + 1];
950
951
/* Create symlink for convenience pointing to interface specific
952
* debugfs entries for the driver. For example, under
953
* /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmld/
954
* find
955
* netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmld/
956
*/
957
snprintf(name, sizeof(name), "%pd", vif->debugfs_dir);
958
snprintf(target, sizeof(target), "../../../%pd3/iwlmld",
959
vif->debugfs_dir);
960
if (!mld_vif->dbgfs_slink)
961
mld_vif->dbgfs_slink =
962
debugfs_create_symlink(name, mld->debugfs_dir, target);
963
964
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
965
vif->type == NL80211_IFTYPE_STATION) {
966
VIF_DEBUGFS_ADD_FILE(pm_params, mld_vif_dbgfs, 0200);
967
VIF_DEBUGFS_ADD_FILE(bf_params, mld_vif_dbgfs, 0200);
968
}
969
970
if (vif->type == NL80211_IFTYPE_AP) {
971
VIF_DEBUGFS_ADD_FILE(inject_beacon_ie, mld_vif_dbgfs, 0200);
972
VIF_DEBUGFS_ADD_FILE(inject_beacon_ie_restore,
973
mld_vif_dbgfs, 0200);
974
}
975
976
VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600);
977
VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200);
978
VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200);
979
VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200);
980
}
981
#define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
982
WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf)
983
984
#define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
985
debugfs_create_file(alias, mode, parent, link_conf, \
986
&iwl_dbgfs_link_##name##_ops)
987
#define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \
988
LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
989
990
void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
991
struct ieee80211_vif *vif,
992
struct ieee80211_bss_conf *link_conf,
993
struct dentry *dir)
994
{
995
struct dentry *mld_link_dir;
996
997
mld_link_dir = debugfs_lookup("iwlmld", dir);
998
999
/* For non-MLO vifs, the dir of deflink is the same as the vif's one.
1000
* so if iwlmld dir already exists, this means that this is deflink.
1001
* If not, this is a per-link dir of a MLO vif, add in it the iwlmld
1002
* dir.
1003
*/
1004
if (!mld_link_dir)
1005
mld_link_dir = debugfs_create_dir("iwlmld", dir);
1006
}
1007
1008
static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
1009
size_t count, void *data, bool v3)
1010
{
1011
struct ieee80211_link_sta *link_sta = data;
1012
struct iwl_mld_link_sta *mld_link_sta;
1013
u32 rate;
1014
u32 partial = false;
1015
char pretty_rate[100];
1016
int ret;
1017
u8 fw_sta_id;
1018
1019
mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
1020
if (WARN_ON(!mld_link_sta))
1021
return -EINVAL;
1022
1023
fw_sta_id = mld_link_sta->fw_id;
1024
1025
if (sscanf(buf, "%i %i", &rate, &partial) == 0)
1026
return -EINVAL;
1027
1028
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
1029
return -EIO;
1030
1031
/* input is in FW format (v2 or v3) so convert to v3 */
1032
rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3);
1033
rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3));
1034
1035
ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id,
1036
partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE :
1037
IWL_TLC_DEBUG_FIXED_RATE,
1038
rate);
1039
1040
rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), rate);
1041
1042
IWL_DEBUG_RATE(mld, "sta_id %d rate %s partial: %d, ret:%d\n",
1043
fw_sta_id, pretty_rate, partial, ret);
1044
1045
return ret ? : count;
1046
}
1047
1048
static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
1049
size_t count, void *data)
1050
{
1051
return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false);
1052
}
1053
1054
static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf,
1055
size_t count, void *data)
1056
{
1057
return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true);
1058
}
1059
1060
static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf,
1061
size_t count, void *data)
1062
{
1063
struct ieee80211_link_sta *link_sta = data;
1064
struct iwl_mld_link_sta *mld_link_sta;
1065
u32 type, value;
1066
int ret;
1067
u8 fw_sta_id;
1068
1069
mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
1070
if (WARN_ON(!mld_link_sta))
1071
return -EINVAL;
1072
1073
fw_sta_id = mld_link_sta->fw_id;
1074
1075
if (sscanf(buf, "%i %i", &type, &value) != 2) {
1076
IWL_DEBUG_RATE(mld, "usage <type> <value>\n");
1077
return -EINVAL;
1078
}
1079
1080
if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
1081
return -EIO;
1082
1083
ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, type, value);
1084
1085
return ret ? : count;
1086
}
1087
1088
#define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
1089
debugfs_create_file(alias, mode, parent, link_sta, \
1090
&iwl_dbgfs_##name##_ops)
1091
#define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \
1092
LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
1093
1094
#define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \
1095
WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta)
1096
1097
LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64);
1098
LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64);
1099
LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64);
1100
1101
void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
1102
struct ieee80211_vif *vif,
1103
struct ieee80211_link_sta *link_sta,
1104
struct dentry *dir)
1105
{
1106
LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200);
1107
LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200);
1108
LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200);
1109
}
1110
1111