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