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