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