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
iwl_mld_dbgfs_fw_cmd_disabled(struct iwl_mld * mld)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
iwl_dbgfs_fw_dbg_clear_write(struct iwl_mld * mld,char * buf,size_t count)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
iwl_dbgfs_fw_nmi_write(struct iwl_mld * mld,char * buf,size_t count)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
iwl_dbgfs_fw_restart_write(struct iwl_mld * mld,char * buf,size_t count)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 set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mld->trans->status);
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
iwl_dbgfs_send_echo_cmd_write(struct iwl_mld * mld,char * buf,size_t count)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
iwl_mld_sniffer_apply(struct iwl_notif_wait_data * notif_data,struct iwl_rx_packet * pkt,void * data)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
iwl_dbgfs_he_sniffer_params_write(struct iwl_mld * mld,char * buf,size_t count)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
iwl_dbgfs_he_sniffer_params_read(struct iwl_mld * mld,char * buf,size_t count)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
iwl_mld_dump_tas_resp(struct iwl_dhc_tas_status_resp * resp,size_t count,u8 * buf)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
iwl_dbgfs_tas_get_status_read(struct iwl_mld * mld,char * buf,size_t count)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
iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld * mld,size_t count,u8 * buf)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
iwl_dbgfs_inject_packet_write(struct iwl_mld * mld,char * buf,size_t count)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
iwl_dbgfs_stop_ctdp_write(struct iwl_mld * mld,char * buf,size_t count)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
iwl_dbgfs_start_ctdp_write(struct iwl_mld * mld,char * buf,size_t count)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
iwl_mld_add_debugfs_files(struct iwl_mld * mld,struct dentry * debugfs_dir)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 debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir,
550 &mld->monitor.ptp_time);
551
552 /* Create a symlink with mac80211. It will be removed when mac80211
553 * exits (before the opmode exits which removes the target.)
554 */
555 if (!IS_ERR(debugfs_dir)) {
556 char buf[100];
557
558 snprintf(buf, 100, "../../%pd2", debugfs_dir->d_parent);
559 debugfs_create_symlink("iwlwifi", mld->wiphy->debugfsdir,
560 buf);
561 }
562 }
563
564 #define VIF_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
565 WIPHY_DEBUGFS_WRITE_FILE_OPS(vif_##name, bufsz, vif)
566
567 #define VIF_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
568 IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(vif_##name, bufsz, vif) \
569
570 #define VIF_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
571 debugfs_create_file(alias, mode, parent, vif, \
572 &iwl_dbgfs_vif_##name##_ops)
573 #define VIF_DEBUGFS_ADD_FILE(name, parent, mode) \
574 VIF_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
575
iwl_dbgfs_vif_bf_params_write(struct iwl_mld * mld,char * buf,size_t count,void * data)576 static ssize_t iwl_dbgfs_vif_bf_params_write(struct iwl_mld *mld, char *buf,
577 size_t count, void *data)
578 {
579 struct ieee80211_vif *vif = data;
580 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
581 int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
582 struct ieee80211_bss_conf *link_conf;
583 int val;
584
585 if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
586 if (sscanf(buf + 24, "%d", &val) != 1)
587 return -EINVAL;
588 } else {
589 return -EINVAL;
590 }
591
592 if (val != 0 && val != 1)
593 return -EINVAL;
594
595 link_conf = link_conf_dereference_protected(vif, link_id);
596 if (WARN_ON(!link_conf))
597 return -ENODEV;
598
599 if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
600 return -EIO;
601
602 mld_vif->disable_bf = !val;
603
604 if (val)
605 return iwl_mld_enable_beacon_filter(mld, link_conf,
606 false) ?: count;
607 else
608 return iwl_mld_disable_beacon_filter(mld, vif) ?: count;
609 }
610
iwl_dbgfs_vif_pm_params_write(struct iwl_mld * mld,char * buf,size_t count,void * data)611 static ssize_t iwl_dbgfs_vif_pm_params_write(struct iwl_mld *mld,
612 char *buf,
613 size_t count, void *data)
614 {
615 struct ieee80211_vif *vif = data;
616 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
617 int val;
618
619 if (!strncmp("use_ps_poll=", buf, 12)) {
620 if (sscanf(buf + 12, "%d", &val) != 1)
621 return -EINVAL;
622 } else {
623 return -EINVAL;
624 }
625
626 if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
627 return -EIO;
628
629 mld_vif->use_ps_poll = val;
630
631 return iwl_mld_update_mac_power(mld, vif, false) ?: count;
632 }
633
iwl_dbgfs_vif_low_latency_write(struct iwl_mld * mld,char * buf,size_t count,void * data)634 static ssize_t iwl_dbgfs_vif_low_latency_write(struct iwl_mld *mld,
635 char *buf, size_t count,
636 void *data)
637 {
638 struct ieee80211_vif *vif = data;
639 u8 value;
640 int ret;
641
642 ret = kstrtou8(buf, 0, &value);
643 if (ret)
644 return ret;
645
646 if (value > 1)
647 return -EINVAL;
648
649 iwl_mld_vif_update_low_latency(mld, vif, value, LOW_LATENCY_DEBUGFS);
650
651 return count;
652 }
653
iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif * vif,size_t count,char * buf)654 static ssize_t iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif *vif,
655 size_t count, char *buf)
656 {
657 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
658 char format[] = "traffic=%d\ndbgfs=%d\nvif_type=%d\nactual=%d\n";
659 u8 ll_causes;
660
661 if (WARN_ON(count < sizeof(format)))
662 return -EINVAL;
663
664 ll_causes = READ_ONCE(mld_vif->low_latency_causes);
665
666 /* all values in format are boolean so the size of format is enough
667 * for holding the result string
668 */
669 return scnprintf(buf, count, format,
670 !!(ll_causes & LOW_LATENCY_TRAFFIC),
671 !!(ll_causes & LOW_LATENCY_DEBUGFS),
672 !!(ll_causes & LOW_LATENCY_VIF_TYPE),
673 !!(ll_causes));
674 }
675
676 VIF_DEBUGFS_WRITE_FILE_OPS(pm_params, 32);
677 VIF_DEBUGFS_WRITE_FILE_OPS(bf_params, 32);
678 VIF_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 45);
679
680 static int
_iwl_dbgfs_inject_beacon_ie(struct iwl_mld * mld,struct ieee80211_vif * vif,char * bin,ssize_t len,bool restore)681 _iwl_dbgfs_inject_beacon_ie(struct iwl_mld *mld, struct ieee80211_vif *vif,
682 char *bin, ssize_t len,
683 bool restore)
684 {
685 struct iwl_mld_vif *mld_vif;
686 struct iwl_mld_link *mld_link;
687 struct iwl_mac_beacon_cmd beacon_cmd = {};
688 int n_bytes = len / 2;
689
690 /* Element len should be represented by u8 */
691 if (n_bytes >= U8_MAX)
692 return -EINVAL;
693
694 if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
695 return -EIO;
696
697 if (!vif)
698 return -EINVAL;
699
700 mld_vif = iwl_mld_vif_from_mac80211(vif);
701 mld_vif->beacon_inject_active = true;
702 mld->hw->extra_beacon_tailroom = n_bytes;
703
704 for_each_mld_vif_valid_link(mld_vif, mld_link) {
705 u32 offset;
706 struct ieee80211_tx_info *info;
707 struct ieee80211_bss_conf *link_conf =
708 link_conf_dereference_protected(vif, link_id);
709 struct ieee80211_chanctx_conf *ctx =
710 wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
711 struct sk_buff *beacon =
712 ieee80211_beacon_get_template(mld->hw, vif,
713 NULL, link_id);
714
715 if (!beacon)
716 return -EINVAL;
717
718 if (!restore && (WARN_ON(!n_bytes || !bin) ||
719 hex2bin(skb_put_zero(beacon, n_bytes),
720 bin, n_bytes))) {
721 dev_kfree_skb(beacon);
722 return -EINVAL;
723 }
724
725 info = IEEE80211_SKB_CB(beacon);
726
727 beacon_cmd.flags =
728 cpu_to_le16(iwl_mld_get_rate_flags(mld, info, vif,
729 link_conf,
730 ctx->def.chan->band));
731 beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len);
732 beacon_cmd.link_id =
733 cpu_to_le32(mld_link->fw_id);
734
735 iwl_mld_set_tim_idx(mld, &beacon_cmd.tim_idx,
736 beacon->data, beacon->len);
737
738 offset = iwl_find_ie_offset(beacon->data,
739 WLAN_EID_S1G_TWT,
740 beacon->len);
741
742 beacon_cmd.btwt_offset = cpu_to_le32(offset);
743
744 iwl_mld_send_beacon_template_cmd(mld, beacon, &beacon_cmd);
745 dev_kfree_skb(beacon);
746 }
747
748 if (restore)
749 mld_vif->beacon_inject_active = false;
750
751 return 0;
752 }
753
754 static ssize_t
iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld * mld,char * buf,size_t count,void * data)755 iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld *mld,
756 char *buf, size_t count,
757 void *data)
758 {
759 struct ieee80211_vif *vif = data;
760 int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, buf,
761 count, false);
762
763 mld->hw->extra_beacon_tailroom = 0;
764 return ret ?: count;
765 }
766
767 VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512);
768
769 static ssize_t
iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld * mld,char * buf,size_t count,void * data)770 iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld *mld,
771 char *buf,
772 size_t count,
773 void *data)
774 {
775 struct ieee80211_vif *vif = data;
776 int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, NULL,
777 0, true);
778
779 mld->hw->extra_beacon_tailroom = 0;
780 return ret ?: count;
781 }
782
783 VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512);
784
785 static ssize_t
iwl_dbgfs_vif_twt_setup_write(struct iwl_mld * mld,char * buf,size_t count,void * data)786 iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count,
787 void *data)
788 {
789 struct iwl_host_cmd hcmd = {
790 .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, DEBUG_HOST_COMMAND),
791 };
792 struct ieee80211_vif *vif = data;
793 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
794 struct iwl_dhc_cmd *cmd __free(kfree) = NULL;
795 struct iwl_dhc_twt_operation *dhc_twt_cmd;
796 u64 target_wake_time;
797 u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration;
798 u8 trigger, flow_type, flow_id, protection, tenth_param;
799 u8 twt_request = 1, broadcast = 0;
800 int ret;
801
802 if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
803 return -EIO;
804
805 ret = sscanf(buf, "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu",
806 &twt_operation, &target_wake_time, &interval_exp,
807 &interval_mantissa, &min_wake_duration, &trigger,
808 &flow_type, &flow_id, &protection, &tenth_param);
809
810 /* the new twt_request parameter is optional for station */
811 if ((ret != 9 && ret != 10) ||
812 (ret == 10 && vif->type != NL80211_IFTYPE_STATION &&
813 tenth_param == 1))
814 return -EINVAL;
815
816 /* The 10th parameter:
817 * In STA mode - the TWT type (broadcast or individual)
818 * In AP mode - the role (0 responder, 2 unsolicited)
819 */
820 if (ret == 10) {
821 if (vif->type == NL80211_IFTYPE_STATION)
822 broadcast = tenth_param;
823 else
824 twt_request = tenth_param;
825 }
826
827 cmd = kzalloc(sizeof(*cmd) + sizeof(*dhc_twt_cmd), GFP_KERNEL);
828 if (!cmd)
829 return -ENOMEM;
830
831 dhc_twt_cmd = (void *)cmd->data;
832 dhc_twt_cmd->mac_id = cpu_to_le32(mld_vif->fw_id);
833 dhc_twt_cmd->twt_operation = cpu_to_le32(twt_operation);
834 dhc_twt_cmd->target_wake_time = cpu_to_le64(target_wake_time);
835 dhc_twt_cmd->interval_exp = cpu_to_le32(interval_exp);
836 dhc_twt_cmd->interval_mantissa = cpu_to_le32(interval_mantissa);
837 dhc_twt_cmd->min_wake_duration = cpu_to_le32(min_wake_duration);
838 dhc_twt_cmd->trigger = trigger;
839 dhc_twt_cmd->flow_type = flow_type;
840 dhc_twt_cmd->flow_id = flow_id;
841 dhc_twt_cmd->protection = protection;
842 dhc_twt_cmd->twt_request = twt_request;
843 dhc_twt_cmd->negotiation_type = broadcast ? 3 : 0;
844
845 cmd->length = cpu_to_le32(sizeof(*dhc_twt_cmd) >> 2);
846 cmd->index_and_mask =
847 cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC |
848 DHC_INT_UMAC_TWT_OPERATION);
849
850 hcmd.len[0] = sizeof(*cmd) + sizeof(*dhc_twt_cmd);
851 hcmd.data[0] = cmd;
852
853 ret = iwl_mld_send_cmd(mld, &hcmd);
854
855 return ret ?: count;
856 }
857
858 VIF_DEBUGFS_WRITE_FILE_OPS(twt_setup, 256);
859
860 static ssize_t
iwl_dbgfs_vif_twt_operation_write(struct iwl_mld * mld,char * buf,size_t count,void * data)861 iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count,
862 void *data)
863 {
864 struct ieee80211_vif *vif = data;
865 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
866 struct iwl_twt_operation_cmd twt_cmd = {};
867 int link_id = vif->active_links ? __ffs(vif->active_links) : 0;
868 struct iwl_mld_link *mld_link = iwl_mld_link_dereference_check(mld_vif,
869 link_id);
870 int ret;
871
872 if (WARN_ON(!mld_link))
873 return -ENODEV;
874
875 if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
876 return -EIO;
877
878 if (hweight16(vif->active_links) > 1)
879 return -EOPNOTSUPP;
880
881 ret = sscanf(buf,
882 "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu",
883 &twt_cmd.twt_operation, &twt_cmd.target_wake_time,
884 &twt_cmd.interval_exponent, &twt_cmd.interval_mantissa,
885 &twt_cmd.minimum_wake_duration, &twt_cmd.trigger,
886 &twt_cmd.flow_type, &twt_cmd.flow_id,
887 &twt_cmd.twt_protection, &twt_cmd.ndp_paging_indicator,
888 &twt_cmd.responder_pm_mode, &twt_cmd.negotiation_type,
889 &twt_cmd.twt_request, &twt_cmd.implicit,
890 &twt_cmd.twt_group_assignment, &twt_cmd.twt_channel,
891 &twt_cmd.restricted_info_present, &twt_cmd.dl_bitmap_valid,
892 &twt_cmd.ul_bitmap_valid, &twt_cmd.dl_tid_bitmap,
893 &twt_cmd.ul_tid_bitmap);
894
895 if (ret != 21)
896 return -EINVAL;
897
898 twt_cmd.link_id = cpu_to_le32(mld_link->fw_id);
899
900 ret = iwl_mld_send_cmd_pdu(mld,
901 WIDE_ID(MAC_CONF_GROUP, TWT_OPERATION_CMD),
902 &twt_cmd);
903 return ret ?: count;
904 }
905
906 VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256);
907
iwl_dbgfs_vif_int_mlo_scan_write(struct iwl_mld * mld,char * buf,size_t count,void * data)908 static ssize_t iwl_dbgfs_vif_int_mlo_scan_write(struct iwl_mld *mld, char *buf,
909 size_t count, void *data)
910 {
911 struct ieee80211_vif *vif = data;
912 u32 action;
913 int ret;
914
915 if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif))
916 return -EINVAL;
917
918 if (kstrtou32(buf, 0, &action))
919 return -EINVAL;
920
921 if (action == 0) {
922 ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_INT_MLO, false);
923 } else if (action == 1) {
924 iwl_mld_int_mlo_scan(mld, vif);
925 ret = 0;
926 } else {
927 ret = -EINVAL;
928 }
929
930 return ret ?: count;
931 }
932
933 VIF_DEBUGFS_WRITE_FILE_OPS(int_mlo_scan, 32);
934
iwl_mld_add_vif_debugfs(struct ieee80211_hw * hw,struct ieee80211_vif * vif)935 void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
936 struct ieee80211_vif *vif)
937 {
938 struct dentry *mld_vif_dbgfs =
939 debugfs_create_dir("iwlmld", vif->debugfs_dir);
940 struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
941 struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
942 char target[3 * 3 + 11 + (NL80211_WIPHY_NAME_MAXLEN + 1) +
943 (7 + IFNAMSIZ + 1) + 6 + 1];
944 char name[7 + IFNAMSIZ + 1];
945
946 /* Create symlink for convenience pointing to interface specific
947 * debugfs entries for the driver. For example, under
948 * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmld/
949 * find
950 * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmld/
951 */
952 snprintf(name, sizeof(name), "%pd", vif->debugfs_dir);
953 snprintf(target, sizeof(target), "../../../%pd3/iwlmld",
954 vif->debugfs_dir);
955 if (!mld_vif->dbgfs_slink)
956 mld_vif->dbgfs_slink =
957 debugfs_create_symlink(name, mld->debugfs_dir, target);
958
959 if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
960 vif->type == NL80211_IFTYPE_STATION) {
961 VIF_DEBUGFS_ADD_FILE(pm_params, mld_vif_dbgfs, 0200);
962 VIF_DEBUGFS_ADD_FILE(bf_params, mld_vif_dbgfs, 0200);
963 }
964
965 if (vif->type == NL80211_IFTYPE_AP) {
966 VIF_DEBUGFS_ADD_FILE(inject_beacon_ie, mld_vif_dbgfs, 0200);
967 VIF_DEBUGFS_ADD_FILE(inject_beacon_ie_restore,
968 mld_vif_dbgfs, 0200);
969 }
970
971 VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600);
972 VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200);
973 VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200);
974 VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200);
975 }
976 #define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
977 WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf)
978
979 #define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
980 debugfs_create_file(alias, mode, parent, link_conf, \
981 &iwl_dbgfs_link_##name##_ops)
982 #define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \
983 LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
984
iwl_mld_add_link_debugfs(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf,struct dentry * dir)985 void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
986 struct ieee80211_vif *vif,
987 struct ieee80211_bss_conf *link_conf,
988 struct dentry *dir)
989 {
990 struct dentry *mld_link_dir;
991
992 mld_link_dir = debugfs_lookup("iwlmld", dir);
993
994 /* For non-MLO vifs, the dir of deflink is the same as the vif's one.
995 * so if iwlmld dir already exists, this means that this is deflink.
996 * If not, this is a per-link dir of a MLO vif, add in it the iwlmld
997 * dir.
998 */
999 if (!mld_link_dir)
1000 mld_link_dir = debugfs_create_dir("iwlmld", dir);
1001 }
1002
_iwl_dbgfs_fixed_rate_write(struct iwl_mld * mld,char * buf,size_t count,void * data,bool v3)1003 static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
1004 size_t count, void *data, bool v3)
1005 {
1006 struct ieee80211_link_sta *link_sta = data;
1007 struct iwl_mld_link_sta *mld_link_sta;
1008 u32 rate;
1009 u32 partial = false;
1010 char pretty_rate[100];
1011 int ret;
1012 u8 fw_sta_id;
1013
1014 mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
1015 if (WARN_ON(!mld_link_sta))
1016 return -EINVAL;
1017
1018 fw_sta_id = mld_link_sta->fw_id;
1019
1020 if (sscanf(buf, "%i %i", &rate, &partial) == 0)
1021 return -EINVAL;
1022
1023 if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
1024 return -EIO;
1025
1026 /* input is in FW format (v2 or v3) so convert to v3 */
1027 rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3);
1028 rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3));
1029
1030 ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id,
1031 partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE :
1032 IWL_TLC_DEBUG_FIXED_RATE,
1033 rate);
1034
1035 rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), rate);
1036
1037 IWL_DEBUG_RATE(mld, "sta_id %d rate %s partial: %d, ret:%d\n",
1038 fw_sta_id, pretty_rate, partial, ret);
1039
1040 return ret ? : count;
1041 }
1042
iwl_dbgfs_fixed_rate_write(struct iwl_mld * mld,char * buf,size_t count,void * data)1043 static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
1044 size_t count, void *data)
1045 {
1046 return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false);
1047 }
1048
iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld * mld,char * buf,size_t count,void * data)1049 static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf,
1050 size_t count, void *data)
1051 {
1052 return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true);
1053 }
1054
iwl_dbgfs_tlc_dhc_write(struct iwl_mld * mld,char * buf,size_t count,void * data)1055 static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf,
1056 size_t count, void *data)
1057 {
1058 struct ieee80211_link_sta *link_sta = data;
1059 struct iwl_mld_link_sta *mld_link_sta;
1060 u32 type, value;
1061 int ret;
1062 u8 fw_sta_id;
1063
1064 mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
1065 if (WARN_ON(!mld_link_sta))
1066 return -EINVAL;
1067
1068 fw_sta_id = mld_link_sta->fw_id;
1069
1070 if (sscanf(buf, "%i %i", &type, &value) != 2) {
1071 IWL_DEBUG_RATE(mld, "usage <type> <value>\n");
1072 return -EINVAL;
1073 }
1074
1075 if (iwl_mld_dbgfs_fw_cmd_disabled(mld))
1076 return -EIO;
1077
1078 ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, type, value);
1079
1080 return ret ? : count;
1081 }
1082
1083 #define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
1084 debugfs_create_file(alias, mode, parent, link_sta, \
1085 &iwl_dbgfs_##name##_ops)
1086 #define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \
1087 LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
1088
1089 #define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \
1090 WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta)
1091
1092 LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64);
1093 LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64);
1094 LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64);
1095
iwl_mld_add_link_sta_debugfs(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_link_sta * link_sta,struct dentry * dir)1096 void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
1097 struct ieee80211_vif *vif,
1098 struct ieee80211_link_sta *link_sta,
1099 struct dentry *dir)
1100 {
1101 LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200);
1102 LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200);
1103 LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200);
1104 }
1105