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