xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /******************************************************************************
2*4882a593Smuzhiyun  *
3*4882a593Smuzhiyun  * This file is provided under a dual BSD/GPLv2 license.  When using or
4*4882a593Smuzhiyun  * redistributing this file, you may do so under either license.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * GPL LICENSE SUMMARY
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
9*4882a593Smuzhiyun  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
10*4882a593Smuzhiyun  * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify
13*4882a593Smuzhiyun  * it under the terms of version 2 of the GNU General Public License as
14*4882a593Smuzhiyun  * published by the Free Software Foundation.
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * This program is distributed in the hope that it will be useful, but
17*4882a593Smuzhiyun  * WITHOUT ANY WARRANTY; without even the implied warranty of
18*4882a593Smuzhiyun  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19*4882a593Smuzhiyun  * General Public License for more details.
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * The full GNU General Public License is included in this distribution
22*4882a593Smuzhiyun  * in the file called COPYING.
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * Contact Information:
25*4882a593Smuzhiyun  *  Intel Linux Wireless <linuxwifi@intel.com>
26*4882a593Smuzhiyun  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
27*4882a593Smuzhiyun  *
28*4882a593Smuzhiyun  * BSD LICENSE
29*4882a593Smuzhiyun  *
30*4882a593Smuzhiyun  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
31*4882a593Smuzhiyun  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
32*4882a593Smuzhiyun  * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
33*4882a593Smuzhiyun  * All rights reserved.
34*4882a593Smuzhiyun  *
35*4882a593Smuzhiyun  * Redistribution and use in source and binary forms, with or without
36*4882a593Smuzhiyun  * modification, are permitted provided that the following conditions
37*4882a593Smuzhiyun  * are met:
38*4882a593Smuzhiyun  *
39*4882a593Smuzhiyun  *  * Redistributions of source code must retain the above copyright
40*4882a593Smuzhiyun  *    notice, this list of conditions and the following disclaimer.
41*4882a593Smuzhiyun  *  * Redistributions in binary form must reproduce the above copyright
42*4882a593Smuzhiyun  *    notice, this list of conditions and the following disclaimer in
43*4882a593Smuzhiyun  *    the documentation and/or other materials provided with the
44*4882a593Smuzhiyun  *    distribution.
45*4882a593Smuzhiyun  *  * Neither the name Intel Corporation nor the names of its
46*4882a593Smuzhiyun  *    contributors may be used to endorse or promote products derived
47*4882a593Smuzhiyun  *    from this software without specific prior written permission.
48*4882a593Smuzhiyun  *
49*4882a593Smuzhiyun  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
50*4882a593Smuzhiyun  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
51*4882a593Smuzhiyun  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
52*4882a593Smuzhiyun  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
53*4882a593Smuzhiyun  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
54*4882a593Smuzhiyun  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
55*4882a593Smuzhiyun  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
56*4882a593Smuzhiyun  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
57*4882a593Smuzhiyun  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58*4882a593Smuzhiyun  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
59*4882a593Smuzhiyun  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60*4882a593Smuzhiyun  *
61*4882a593Smuzhiyun  *****************************************************************************/
62*4882a593Smuzhiyun #include <linux/vmalloc.h>
63*4882a593Smuzhiyun #include <linux/ieee80211.h>
64*4882a593Smuzhiyun #include <linux/netdevice.h>
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun #include "mvm.h"
67*4882a593Smuzhiyun #include "sta.h"
68*4882a593Smuzhiyun #include "iwl-io.h"
69*4882a593Smuzhiyun #include "debugfs.h"
70*4882a593Smuzhiyun #include "iwl-modparams.h"
71*4882a593Smuzhiyun #include "fw/error-dump.h"
72*4882a593Smuzhiyun 
iwl_dbgfs_ctdp_budget_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)73*4882a593Smuzhiyun static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file,
74*4882a593Smuzhiyun 					  char __user *user_buf,
75*4882a593Smuzhiyun 					  size_t count, loff_t *ppos)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
78*4882a593Smuzhiyun 	char buf[16];
79*4882a593Smuzhiyun 	int pos, budget;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	if (!iwl_mvm_is_ctdp_supported(mvm))
82*4882a593Smuzhiyun 		return -EOPNOTSUPP;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm) ||
85*4882a593Smuzhiyun 	    mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)
86*4882a593Smuzhiyun 		return -EIO;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
89*4882a593Smuzhiyun 	budget = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_REPORT, 0);
90*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (budget < 0)
93*4882a593Smuzhiyun 		return budget;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	pos = scnprintf(buf, sizeof(buf), "%d\n", budget);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun 
iwl_dbgfs_stop_ctdp_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)100*4882a593Smuzhiyun static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf,
101*4882a593Smuzhiyun 					 size_t count, loff_t *ppos)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	int ret;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	if (!iwl_mvm_is_ctdp_supported(mvm))
106*4882a593Smuzhiyun 		return -EOPNOTSUPP;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm) ||
109*4882a593Smuzhiyun 	    mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)
110*4882a593Smuzhiyun 		return -EIO;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
113*4882a593Smuzhiyun 	ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_STOP, 0);
114*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	return ret ?: count;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun 
iwl_dbgfs_force_ctkill_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)119*4882a593Smuzhiyun static ssize_t iwl_dbgfs_force_ctkill_write(struct iwl_mvm *mvm, char *buf,
120*4882a593Smuzhiyun 					    size_t count, loff_t *ppos)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm) ||
123*4882a593Smuzhiyun 	    mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)
124*4882a593Smuzhiyun 		return -EIO;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	iwl_mvm_enter_ctkill(mvm);
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	return count;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
iwl_dbgfs_tx_flush_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)131*4882a593Smuzhiyun static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf,
132*4882a593Smuzhiyun 					size_t count, loff_t *ppos)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun 	int ret;
135*4882a593Smuzhiyun 	u32 flush_arg;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm) ||
138*4882a593Smuzhiyun 	    mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)
139*4882a593Smuzhiyun 		return -EIO;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if (kstrtou32(buf, 0, &flush_arg))
142*4882a593Smuzhiyun 		return -EINVAL;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	if (iwl_mvm_has_new_tx_api(mvm)) {
145*4882a593Smuzhiyun 		IWL_DEBUG_TX_QUEUES(mvm,
146*4882a593Smuzhiyun 				    "FLUSHING all tids queues on sta_id = %d\n",
147*4882a593Smuzhiyun 				    flush_arg);
148*4882a593Smuzhiyun 		mutex_lock(&mvm->mutex);
149*4882a593Smuzhiyun 		ret = iwl_mvm_flush_sta_tids(mvm, flush_arg, 0xFFFF, 0)
150*4882a593Smuzhiyun 			? : count;
151*4882a593Smuzhiyun 		mutex_unlock(&mvm->mutex);
152*4882a593Smuzhiyun 		return ret;
153*4882a593Smuzhiyun 	}
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	IWL_DEBUG_TX_QUEUES(mvm, "FLUSHING queues mask to flush = 0x%x\n",
156*4882a593Smuzhiyun 			    flush_arg);
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
159*4882a593Smuzhiyun 	ret =  iwl_mvm_flush_tx_path(mvm, flush_arg, 0) ? : count;
160*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	return ret;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
iwl_dbgfs_sta_drain_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)165*4882a593Smuzhiyun static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf,
166*4882a593Smuzhiyun 					 size_t count, loff_t *ppos)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	struct iwl_mvm_sta *mvmsta;
169*4882a593Smuzhiyun 	int sta_id, drain, ret;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm) ||
172*4882a593Smuzhiyun 	    mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR)
173*4882a593Smuzhiyun 		return -EIO;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
176*4882a593Smuzhiyun 		return -EINVAL;
177*4882a593Smuzhiyun 	if (sta_id < 0 || sta_id >= mvm->fw->ucode_capa.num_stations)
178*4882a593Smuzhiyun 		return -EINVAL;
179*4882a593Smuzhiyun 	if (drain < 0 || drain > 1)
180*4882a593Smuzhiyun 		return -EINVAL;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	if (!mvmsta)
187*4882a593Smuzhiyun 		ret = -ENOENT;
188*4882a593Smuzhiyun 	else
189*4882a593Smuzhiyun 		ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	return ret;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun 
iwl_dbgfs_sram_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)196*4882a593Smuzhiyun static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
197*4882a593Smuzhiyun 				   size_t count, loff_t *ppos)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
200*4882a593Smuzhiyun 	const struct fw_img *img;
201*4882a593Smuzhiyun 	unsigned int ofs, len;
202*4882a593Smuzhiyun 	size_t ret;
203*4882a593Smuzhiyun 	u8 *ptr;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
206*4882a593Smuzhiyun 		return -EINVAL;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	/* default is to dump the entire data segment */
209*4882a593Smuzhiyun 	img = &mvm->fw->img[mvm->fwrt.cur_fw_img];
210*4882a593Smuzhiyun 	ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
211*4882a593Smuzhiyun 	len = img->sec[IWL_UCODE_SECTION_DATA].len;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	if (mvm->dbgfs_sram_len) {
214*4882a593Smuzhiyun 		ofs = mvm->dbgfs_sram_offset;
215*4882a593Smuzhiyun 		len = mvm->dbgfs_sram_len;
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	ptr = kzalloc(len, GFP_KERNEL);
219*4882a593Smuzhiyun 	if (!ptr)
220*4882a593Smuzhiyun 		return -ENOMEM;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len);
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	kfree(ptr);
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	return ret;
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun 
iwl_dbgfs_sram_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)231*4882a593Smuzhiyun static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf,
232*4882a593Smuzhiyun 				    size_t count, loff_t *ppos)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun 	const struct fw_img *img;
235*4882a593Smuzhiyun 	u32 offset, len;
236*4882a593Smuzhiyun 	u32 img_offset, img_len;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
239*4882a593Smuzhiyun 		return -EINVAL;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	img = &mvm->fw->img[mvm->fwrt.cur_fw_img];
242*4882a593Smuzhiyun 	img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset;
243*4882a593Smuzhiyun 	img_len = img->sec[IWL_UCODE_SECTION_DATA].len;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
246*4882a593Smuzhiyun 		if ((offset & 0x3) || (len & 0x3))
247*4882a593Smuzhiyun 			return -EINVAL;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 		if (offset + len > img_offset + img_len)
250*4882a593Smuzhiyun 			return -EINVAL;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 		mvm->dbgfs_sram_offset = offset;
253*4882a593Smuzhiyun 		mvm->dbgfs_sram_len = len;
254*4882a593Smuzhiyun 	} else {
255*4882a593Smuzhiyun 		mvm->dbgfs_sram_offset = 0;
256*4882a593Smuzhiyun 		mvm->dbgfs_sram_len = 0;
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	return count;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
iwl_dbgfs_set_nic_temperature_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)262*4882a593Smuzhiyun static ssize_t iwl_dbgfs_set_nic_temperature_read(struct file *file,
263*4882a593Smuzhiyun 						  char __user *user_buf,
264*4882a593Smuzhiyun 						  size_t count, loff_t *ppos)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
267*4882a593Smuzhiyun 	char buf[16];
268*4882a593Smuzhiyun 	int pos;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	if (!mvm->temperature_test)
271*4882a593Smuzhiyun 		pos = scnprintf(buf , sizeof(buf), "disabled\n");
272*4882a593Smuzhiyun 	else
273*4882a593Smuzhiyun 		pos = scnprintf(buf , sizeof(buf), "%d\n", mvm->temperature);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun /*
279*4882a593Smuzhiyun  * Set NIC Temperature
280*4882a593Smuzhiyun  * Cause the driver to ignore the actual NIC temperature reported by the FW
281*4882a593Smuzhiyun  * Enable: any value between IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -
282*4882a593Smuzhiyun  * IWL_MVM_DEBUG_SET_TEMPERATURE_MAX
283*4882a593Smuzhiyun  * Disable: IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE
284*4882a593Smuzhiyun  */
iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)285*4882a593Smuzhiyun static ssize_t iwl_dbgfs_set_nic_temperature_write(struct iwl_mvm *mvm,
286*4882a593Smuzhiyun 						   char *buf, size_t count,
287*4882a593Smuzhiyun 						   loff_t *ppos)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun 	int temperature;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm) && !mvm->temperature_test)
292*4882a593Smuzhiyun 		return -EIO;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	if (kstrtoint(buf, 10, &temperature))
295*4882a593Smuzhiyun 		return -EINVAL;
296*4882a593Smuzhiyun 	/* not a legal temperature */
297*4882a593Smuzhiyun 	if ((temperature > IWL_MVM_DEBUG_SET_TEMPERATURE_MAX &&
298*4882a593Smuzhiyun 	     temperature != IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) ||
299*4882a593Smuzhiyun 	    temperature < IWL_MVM_DEBUG_SET_TEMPERATURE_MIN)
300*4882a593Smuzhiyun 		return -EINVAL;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
303*4882a593Smuzhiyun 	if (temperature == IWL_MVM_DEBUG_SET_TEMPERATURE_DISABLE) {
304*4882a593Smuzhiyun 		if (!mvm->temperature_test)
305*4882a593Smuzhiyun 			goto out;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 		mvm->temperature_test = false;
308*4882a593Smuzhiyun 		/* Since we can't read the temp while awake, just set
309*4882a593Smuzhiyun 		 * it to zero until we get the next RX stats from the
310*4882a593Smuzhiyun 		 * firmware.
311*4882a593Smuzhiyun 		 */
312*4882a593Smuzhiyun 		mvm->temperature = 0;
313*4882a593Smuzhiyun 	} else {
314*4882a593Smuzhiyun 		mvm->temperature_test = true;
315*4882a593Smuzhiyun 		mvm->temperature = temperature;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 	IWL_DEBUG_TEMP(mvm, "%sabling debug set temperature (temp = %d)\n",
318*4882a593Smuzhiyun 		       mvm->temperature_test ? "En" : "Dis" ,
319*4882a593Smuzhiyun 		       mvm->temperature);
320*4882a593Smuzhiyun 	/* handle the temperature change */
321*4882a593Smuzhiyun 	iwl_mvm_tt_handler(mvm);
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun out:
324*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	return count;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
iwl_dbgfs_nic_temp_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)329*4882a593Smuzhiyun static ssize_t iwl_dbgfs_nic_temp_read(struct file *file,
330*4882a593Smuzhiyun 				       char __user *user_buf,
331*4882a593Smuzhiyun 				       size_t count, loff_t *ppos)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
334*4882a593Smuzhiyun 	char buf[16];
335*4882a593Smuzhiyun 	int pos, ret;
336*4882a593Smuzhiyun 	s32 temp;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
339*4882a593Smuzhiyun 		return -EIO;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
342*4882a593Smuzhiyun 	ret = iwl_mvm_get_temp(mvm, &temp);
343*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (ret)
346*4882a593Smuzhiyun 		return -EIO;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	pos = scnprintf(buf , sizeof(buf), "%d\n", temp);
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun #ifdef CONFIG_ACPI
iwl_dbgfs_sar_geo_profile_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)354*4882a593Smuzhiyun static ssize_t iwl_dbgfs_sar_geo_profile_read(struct file *file,
355*4882a593Smuzhiyun 					      char __user *user_buf,
356*4882a593Smuzhiyun 					      size_t count, loff_t *ppos)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
359*4882a593Smuzhiyun 	char buf[256];
360*4882a593Smuzhiyun 	int pos = 0;
361*4882a593Smuzhiyun 	int bufsz = sizeof(buf);
362*4882a593Smuzhiyun 	int tbl_idx;
363*4882a593Smuzhiyun 	u8 *value;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
366*4882a593Smuzhiyun 		return -EIO;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
369*4882a593Smuzhiyun 	tbl_idx = iwl_mvm_get_sar_geo_profile(mvm);
370*4882a593Smuzhiyun 	if (tbl_idx < 0) {
371*4882a593Smuzhiyun 		mutex_unlock(&mvm->mutex);
372*4882a593Smuzhiyun 		return tbl_idx;
373*4882a593Smuzhiyun 	}
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	if (!tbl_idx) {
376*4882a593Smuzhiyun 		pos = scnprintf(buf, bufsz,
377*4882a593Smuzhiyun 				"SAR geographic profile disabled\n");
378*4882a593Smuzhiyun 	} else {
379*4882a593Smuzhiyun 		value = &mvm->fwrt.geo_profiles[tbl_idx - 1].values[0];
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos,
382*4882a593Smuzhiyun 				 "Use geographic profile %d\n", tbl_idx);
383*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos,
384*4882a593Smuzhiyun 				 "2.4GHz:\n\tChain A offset: %hhd dBm\n\tChain B offset: %hhd dBm\n\tmax tx power: %hhd dBm\n",
385*4882a593Smuzhiyun 				 value[1], value[2], value[0]);
386*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos,
387*4882a593Smuzhiyun 				 "5.2GHz:\n\tChain A offset: %hhd dBm\n\tChain B offset: %hhd dBm\n\tmax tx power: %hhd dBm\n",
388*4882a593Smuzhiyun 				 value[4], value[5], value[3]);
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun #endif
395*4882a593Smuzhiyun 
iwl_dbgfs_stations_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)396*4882a593Smuzhiyun static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
397*4882a593Smuzhiyun 				       size_t count, loff_t *ppos)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
400*4882a593Smuzhiyun 	struct ieee80211_sta *sta;
401*4882a593Smuzhiyun 	char buf[400];
402*4882a593Smuzhiyun 	int i, pos = 0, bufsz = sizeof(buf);
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
407*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);
408*4882a593Smuzhiyun 		sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
409*4882a593Smuzhiyun 						lockdep_is_held(&mvm->mutex));
410*4882a593Smuzhiyun 		if (!sta)
411*4882a593Smuzhiyun 			pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");
412*4882a593Smuzhiyun 		else if (IS_ERR(sta))
413*4882a593Smuzhiyun 			pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",
414*4882a593Smuzhiyun 					 PTR_ERR(sta));
415*4882a593Smuzhiyun 		else
416*4882a593Smuzhiyun 			pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
417*4882a593Smuzhiyun 					 sta->addr);
418*4882a593Smuzhiyun 	}
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun 
iwl_dbgfs_rs_data_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)425*4882a593Smuzhiyun static ssize_t iwl_dbgfs_rs_data_read(struct file *file, char __user *user_buf,
426*4882a593Smuzhiyun 				      size_t count, loff_t *ppos)
427*4882a593Smuzhiyun {
428*4882a593Smuzhiyun 	struct ieee80211_sta *sta = file->private_data;
429*4882a593Smuzhiyun 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
430*4882a593Smuzhiyun 	struct iwl_lq_sta_rs_fw *lq_sta = &mvmsta->lq_sta.rs_fw;
431*4882a593Smuzhiyun 	struct iwl_mvm *mvm = lq_sta->pers.drv;
432*4882a593Smuzhiyun 	static const size_t bufsz = 2048;
433*4882a593Smuzhiyun 	char *buff;
434*4882a593Smuzhiyun 	int desc = 0;
435*4882a593Smuzhiyun 	ssize_t ret;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	buff = kmalloc(bufsz, GFP_KERNEL);
438*4882a593Smuzhiyun 	if (!buff)
439*4882a593Smuzhiyun 		return -ENOMEM;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	desc += scnprintf(buff + desc, bufsz - desc, "sta_id %d\n",
444*4882a593Smuzhiyun 			  lq_sta->pers.sta_id);
445*4882a593Smuzhiyun 	desc += scnprintf(buff + desc, bufsz - desc,
446*4882a593Smuzhiyun 			  "fixed rate 0x%X\n",
447*4882a593Smuzhiyun 			  lq_sta->pers.dbg_fixed_rate);
448*4882a593Smuzhiyun 	desc += scnprintf(buff + desc, bufsz - desc,
449*4882a593Smuzhiyun 			  "A-MPDU size limit %d\n",
450*4882a593Smuzhiyun 			  lq_sta->pers.dbg_agg_frame_count_lim);
451*4882a593Smuzhiyun 	desc += scnprintf(buff + desc, bufsz - desc,
452*4882a593Smuzhiyun 			  "valid_tx_ant %s%s%s\n",
453*4882a593Smuzhiyun 		(iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "",
454*4882a593Smuzhiyun 		(iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "",
455*4882a593Smuzhiyun 		(iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : "");
456*4882a593Smuzhiyun 	desc += scnprintf(buff + desc, bufsz - desc,
457*4882a593Smuzhiyun 			  "last tx rate=0x%X ",
458*4882a593Smuzhiyun 			  lq_sta->last_rate_n_flags);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	desc += rs_pretty_print_rate(buff + desc, bufsz - desc,
461*4882a593Smuzhiyun 				     lq_sta->last_rate_n_flags);
462*4882a593Smuzhiyun 	if (desc < bufsz - 1)
463*4882a593Smuzhiyun 		buff[desc++] = '\n';
464*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
467*4882a593Smuzhiyun 	kfree(buff);
468*4882a593Smuzhiyun 	return ret;
469*4882a593Smuzhiyun }
470*4882a593Smuzhiyun 
iwl_dbgfs_amsdu_len_write(struct ieee80211_sta * sta,char * buf,size_t count,loff_t * ppos)471*4882a593Smuzhiyun static ssize_t iwl_dbgfs_amsdu_len_write(struct ieee80211_sta *sta,
472*4882a593Smuzhiyun 					 char *buf, size_t count,
473*4882a593Smuzhiyun 					 loff_t *ppos)
474*4882a593Smuzhiyun {
475*4882a593Smuzhiyun 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
476*4882a593Smuzhiyun 	int i;
477*4882a593Smuzhiyun 	u16 amsdu_len;
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	if (kstrtou16(buf, 0, &amsdu_len))
480*4882a593Smuzhiyun 		return -EINVAL;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	/* only change from debug set <-> debug unset */
483*4882a593Smuzhiyun 	if ((amsdu_len && mvmsta->orig_amsdu_len) ||
484*4882a593Smuzhiyun 	    (!!amsdu_len && mvmsta->orig_amsdu_len))
485*4882a593Smuzhiyun 		return -EBUSY;
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	if (amsdu_len) {
488*4882a593Smuzhiyun 		mvmsta->orig_amsdu_len = sta->max_amsdu_len;
489*4882a593Smuzhiyun 		sta->max_amsdu_len = amsdu_len;
490*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(sta->max_tid_amsdu_len); i++)
491*4882a593Smuzhiyun 			sta->max_tid_amsdu_len[i] = amsdu_len;
492*4882a593Smuzhiyun 	} else {
493*4882a593Smuzhiyun 		sta->max_amsdu_len = mvmsta->orig_amsdu_len;
494*4882a593Smuzhiyun 		mvmsta->orig_amsdu_len = 0;
495*4882a593Smuzhiyun 	}
496*4882a593Smuzhiyun 	return count;
497*4882a593Smuzhiyun }
498*4882a593Smuzhiyun 
iwl_dbgfs_amsdu_len_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)499*4882a593Smuzhiyun static ssize_t iwl_dbgfs_amsdu_len_read(struct file *file,
500*4882a593Smuzhiyun 					char __user *user_buf,
501*4882a593Smuzhiyun 					size_t count, loff_t *ppos)
502*4882a593Smuzhiyun {
503*4882a593Smuzhiyun 	struct ieee80211_sta *sta = file->private_data;
504*4882a593Smuzhiyun 	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun 	char buf[32];
507*4882a593Smuzhiyun 	int pos;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	pos = scnprintf(buf, sizeof(buf), "current %d ", sta->max_amsdu_len);
510*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, sizeof(buf) - pos, "stored %d\n",
511*4882a593Smuzhiyun 			 mvmsta->orig_amsdu_len);
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun 
iwl_dbgfs_disable_power_off_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)516*4882a593Smuzhiyun static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file,
517*4882a593Smuzhiyun 						char __user *user_buf,
518*4882a593Smuzhiyun 						size_t count, loff_t *ppos)
519*4882a593Smuzhiyun {
520*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
521*4882a593Smuzhiyun 	char buf[64];
522*4882a593Smuzhiyun 	int bufsz = sizeof(buf);
523*4882a593Smuzhiyun 	int pos = 0;
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n",
526*4882a593Smuzhiyun 			 mvm->disable_power_off);
527*4882a593Smuzhiyun 	pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n",
528*4882a593Smuzhiyun 			 mvm->disable_power_off_d3);
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun 
iwl_dbgfs_disable_power_off_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)533*4882a593Smuzhiyun static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
534*4882a593Smuzhiyun 						 size_t count, loff_t *ppos)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	int ret, val;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
539*4882a593Smuzhiyun 		return -EIO;
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun 	if (!strncmp("disable_power_off_d0=", buf, 21)) {
542*4882a593Smuzhiyun 		if (sscanf(buf + 21, "%d", &val) != 1)
543*4882a593Smuzhiyun 			return -EINVAL;
544*4882a593Smuzhiyun 		mvm->disable_power_off = val;
545*4882a593Smuzhiyun 	} else if (!strncmp("disable_power_off_d3=", buf, 21)) {
546*4882a593Smuzhiyun 		if (sscanf(buf + 21, "%d", &val) != 1)
547*4882a593Smuzhiyun 			return -EINVAL;
548*4882a593Smuzhiyun 		mvm->disable_power_off_d3 = val;
549*4882a593Smuzhiyun 	} else {
550*4882a593Smuzhiyun 		return -EINVAL;
551*4882a593Smuzhiyun 	}
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
554*4882a593Smuzhiyun 	ret = iwl_mvm_power_update_device(mvm);
555*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	return ret ?: count;
558*4882a593Smuzhiyun }
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun static
iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif * notif,char * buf,int pos,int bufsz)561*4882a593Smuzhiyun int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
562*4882a593Smuzhiyun 			   int pos, int bufsz)
563*4882a593Smuzhiyun {
564*4882a593Smuzhiyun 	pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
567*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_PROF1, false);
568*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_PROF2, false);
569*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
570*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, CHL_SEQ_N, false);
571*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, INBAND_S, false);
572*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
573*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_SCAN, false);
574*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_ADV, false);
575*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
576*4882a593Smuzhiyun 	BT_MBOX_PRINT(0, OPEN_CON_1, true);
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
581*4882a593Smuzhiyun 	BT_MBOX_PRINT(1, IP_SR, false);
582*4882a593Smuzhiyun 	BT_MBOX_PRINT(1, LE_MSTR, false);
583*4882a593Smuzhiyun 	BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
584*4882a593Smuzhiyun 	BT_MBOX_PRINT(1, MSG_TYPE, false);
585*4882a593Smuzhiyun 	BT_MBOX_PRINT(1, SSN, true);
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, SNIFF_ACT, false);
590*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, PAG, false);
591*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, INQUIRY, false);
592*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, CONN, false);
593*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
594*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, DISC, false);
595*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, SCO_TX_ACT, false);
596*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, SCO_RX_ACT, false);
597*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, ESCO_RE_TX, false);
598*4882a593Smuzhiyun 	BT_MBOX_PRINT(2, SCO_DURATION, true);
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, SCO_STATE, false);
603*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, SNIFF_STATE, false);
604*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, A2DP_STATE, false);
605*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, A2DP_SRC, false);
606*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, ACL_STATE, false);
607*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, MSTR_STATE, false);
608*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, OBX_STATE, false);
609*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, OPEN_CON_2, false);
610*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
611*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
612*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, INBAND_P, false);
613*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, MSG_TYPE_2, false);
614*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, SSN_2, false);
615*4882a593Smuzhiyun 	BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 	return pos;
618*4882a593Smuzhiyun }
619*4882a593Smuzhiyun 
iwl_dbgfs_bt_notif_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)620*4882a593Smuzhiyun static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
621*4882a593Smuzhiyun 				       size_t count, loff_t *ppos)
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
624*4882a593Smuzhiyun 	struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
625*4882a593Smuzhiyun 	char *buf;
626*4882a593Smuzhiyun 	int ret, pos = 0, bufsz = sizeof(char) * 1024;
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun 	buf = kmalloc(bufsz, GFP_KERNEL);
629*4882a593Smuzhiyun 	if (!buf)
630*4882a593Smuzhiyun 		return -ENOMEM;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "bt_ci_compliance = %d\n",
637*4882a593Smuzhiyun 			 notif->bt_ci_compliance);
638*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "primary_ch_lut = %d\n",
639*4882a593Smuzhiyun 			 le32_to_cpu(notif->primary_ch_lut));
640*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "secondary_ch_lut = %d\n",
641*4882a593Smuzhiyun 			 le32_to_cpu(notif->secondary_ch_lut));
642*4882a593Smuzhiyun 	pos += scnprintf(buf + pos,
643*4882a593Smuzhiyun 			 bufsz - pos, "bt_activity_grading = %d\n",
644*4882a593Smuzhiyun 			 le32_to_cpu(notif->bt_activity_grading));
645*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "bt_rrc = %d\n",
646*4882a593Smuzhiyun 			 notif->rrc_status & 0xF);
647*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "bt_ttc = %d\n",
648*4882a593Smuzhiyun 			 notif->ttc_status & 0xF);
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "sync_sco = %d\n",
651*4882a593Smuzhiyun 			 IWL_MVM_BT_COEX_SYNC2SCO);
652*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "mplut = %d\n",
653*4882a593Smuzhiyun 			 IWL_MVM_BT_COEX_MPLUT);
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
658*4882a593Smuzhiyun 	kfree(buf);
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	return ret;
661*4882a593Smuzhiyun }
662*4882a593Smuzhiyun #undef BT_MBOX_PRINT
663*4882a593Smuzhiyun 
iwl_dbgfs_bt_cmd_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)664*4882a593Smuzhiyun static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
665*4882a593Smuzhiyun 				     size_t count, loff_t *ppos)
666*4882a593Smuzhiyun {
667*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
668*4882a593Smuzhiyun 	struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
669*4882a593Smuzhiyun 	char buf[256];
670*4882a593Smuzhiyun 	int bufsz = sizeof(buf);
671*4882a593Smuzhiyun 	int pos = 0;
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "Channel inhibition CMD\n");
676*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos,
677*4882a593Smuzhiyun 			 "\tPrimary Channel Bitmap 0x%016llx\n",
678*4882a593Smuzhiyun 			 le64_to_cpu(cmd->bt_primary_ci));
679*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos,
680*4882a593Smuzhiyun 			 "\tSecondary Channel Bitmap 0x%016llx\n",
681*4882a593Smuzhiyun 			 le64_to_cpu(cmd->bt_secondary_ci));
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)689*4882a593Smuzhiyun iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf,
690*4882a593Smuzhiyun 			   size_t count, loff_t *ppos)
691*4882a593Smuzhiyun {
692*4882a593Smuzhiyun 	u32 bt_tx_prio;
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	if (sscanf(buf, "%u", &bt_tx_prio) != 1)
695*4882a593Smuzhiyun 		return -EINVAL;
696*4882a593Smuzhiyun 	if (bt_tx_prio > 4)
697*4882a593Smuzhiyun 		return -EINVAL;
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 	mvm->bt_tx_prio = bt_tx_prio;
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	return count;
702*4882a593Smuzhiyun }
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_bt_force_ant_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)705*4882a593Smuzhiyun iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf,
706*4882a593Smuzhiyun 			     size_t count, loff_t *ppos)
707*4882a593Smuzhiyun {
708*4882a593Smuzhiyun 	static const char * const modes_str[BT_FORCE_ANT_MAX] = {
709*4882a593Smuzhiyun 		[BT_FORCE_ANT_DIS] = "dis",
710*4882a593Smuzhiyun 		[BT_FORCE_ANT_AUTO] = "auto",
711*4882a593Smuzhiyun 		[BT_FORCE_ANT_BT] = "bt",
712*4882a593Smuzhiyun 		[BT_FORCE_ANT_WIFI] = "wifi",
713*4882a593Smuzhiyun 	};
714*4882a593Smuzhiyun 	int ret, bt_force_ant_mode;
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	ret = match_string(modes_str, ARRAY_SIZE(modes_str), buf);
717*4882a593Smuzhiyun 	if (ret < 0)
718*4882a593Smuzhiyun 		return ret;
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	bt_force_ant_mode = ret;
721*4882a593Smuzhiyun 	ret = 0;
722*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
723*4882a593Smuzhiyun 	if (mvm->bt_force_ant_mode == bt_force_ant_mode)
724*4882a593Smuzhiyun 		goto out;
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	mvm->bt_force_ant_mode = bt_force_ant_mode;
727*4882a593Smuzhiyun 	IWL_DEBUG_COEX(mvm, "Force mode: %s\n",
728*4882a593Smuzhiyun 		       modes_str[mvm->bt_force_ant_mode]);
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	if (iwl_mvm_firmware_running(mvm))
731*4882a593Smuzhiyun 		ret = iwl_mvm_send_bt_init_conf(mvm);
732*4882a593Smuzhiyun 	else
733*4882a593Smuzhiyun 		ret = 0;
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun out:
736*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
737*4882a593Smuzhiyun 	return ret ?: count;
738*4882a593Smuzhiyun }
739*4882a593Smuzhiyun 
iwl_dbgfs_fw_ver_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)740*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_ver_read(struct file *file, char __user *user_buf,
741*4882a593Smuzhiyun 				     size_t count, loff_t *ppos)
742*4882a593Smuzhiyun {
743*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
744*4882a593Smuzhiyun 	char *buff, *pos, *endpos;
745*4882a593Smuzhiyun 	static const size_t bufsz = 1024;
746*4882a593Smuzhiyun 	int ret;
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 	buff = kmalloc(bufsz, GFP_KERNEL);
749*4882a593Smuzhiyun 	if (!buff)
750*4882a593Smuzhiyun 		return -ENOMEM;
751*4882a593Smuzhiyun 
752*4882a593Smuzhiyun 	pos = buff;
753*4882a593Smuzhiyun 	endpos = pos + bufsz;
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "FW prefix: %s\n",
756*4882a593Smuzhiyun 			 mvm->trans->cfg->fw_name_pre);
757*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "FW: %s\n",
758*4882a593Smuzhiyun 			 mvm->fwrt.fw->human_readable);
759*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "Device: %s\n",
760*4882a593Smuzhiyun 			 mvm->fwrt.trans->name);
761*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "Bus: %s\n",
762*4882a593Smuzhiyun 			 mvm->fwrt.dev->bus->name);
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
765*4882a593Smuzhiyun 	kfree(buff);
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	return ret;
768*4882a593Smuzhiyun }
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun #define PRINT_STATS_LE32(_struct, _memb)				\
771*4882a593Smuzhiyun 			 pos += scnprintf(buf + pos, bufsz - pos,	\
772*4882a593Smuzhiyun 					  fmt_table, #_memb,		\
773*4882a593Smuzhiyun 					  le32_to_cpu(_struct->_memb))
774*4882a593Smuzhiyun 
iwl_dbgfs_fw_rx_stats_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)775*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
776*4882a593Smuzhiyun 					  char __user *user_buf, size_t count,
777*4882a593Smuzhiyun 					  loff_t *ppos)
778*4882a593Smuzhiyun {
779*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
780*4882a593Smuzhiyun 	static const char *fmt_table = "\t%-30s %10u\n";
781*4882a593Smuzhiyun 	static const char *fmt_header = "%-32s\n";
782*4882a593Smuzhiyun 	int pos = 0;
783*4882a593Smuzhiyun 	char *buf;
784*4882a593Smuzhiyun 	int ret;
785*4882a593Smuzhiyun 	size_t bufsz;
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 	if (iwl_mvm_has_new_rx_stats_api(mvm))
788*4882a593Smuzhiyun 		bufsz = ((sizeof(struct mvm_statistics_rx) /
789*4882a593Smuzhiyun 			  sizeof(__le32)) * 43) + (4 * 33) + 1;
790*4882a593Smuzhiyun 	else
791*4882a593Smuzhiyun 		/* 43 = size of each data line; 33 = size of each header */
792*4882a593Smuzhiyun 		bufsz = ((sizeof(struct mvm_statistics_rx_v3) /
793*4882a593Smuzhiyun 			  sizeof(__le32)) * 43) + (4 * 33) + 1;
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	buf = kzalloc(bufsz, GFP_KERNEL);
796*4882a593Smuzhiyun 	if (!buf)
797*4882a593Smuzhiyun 		return -ENOMEM;
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
800*4882a593Smuzhiyun 
801*4882a593Smuzhiyun 	if (iwl_mvm_firmware_running(mvm))
802*4882a593Smuzhiyun 		iwl_mvm_request_statistics(mvm, false);
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
805*4882a593Smuzhiyun 			 "Statistics_Rx - OFDM");
806*4882a593Smuzhiyun 	if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
807*4882a593Smuzhiyun 		struct mvm_statistics_rx_phy_v2 *ofdm = &mvm->rx_stats_v3.ofdm;
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, ina_cnt);
810*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, fina_cnt);
811*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, plcp_err);
812*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, crc32_err);
813*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, overrun_err);
814*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, early_overrun_err);
815*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, crc32_good);
816*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, false_alarm_cnt);
817*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, fina_sync_err_cnt);
818*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, sfd_timeout);
819*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, fina_timeout);
820*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, unresponded_rts);
821*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun);
822*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, sent_ack_cnt);
823*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, sent_cts_cnt);
824*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt);
825*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, dsp_self_kill);
826*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, mh_format_err);
827*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum);
828*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, reserved);
829*4882a593Smuzhiyun 	} else {
830*4882a593Smuzhiyun 		struct mvm_statistics_rx_phy *ofdm = &mvm->rx_stats.ofdm;
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, unresponded_rts);
833*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun);
834*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt);
835*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, dsp_self_kill);
836*4882a593Smuzhiyun 		PRINT_STATS_LE32(ofdm, reserved);
837*4882a593Smuzhiyun 	}
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
840*4882a593Smuzhiyun 			 "Statistics_Rx - CCK");
841*4882a593Smuzhiyun 	if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
842*4882a593Smuzhiyun 		struct mvm_statistics_rx_phy_v2 *cck = &mvm->rx_stats_v3.cck;
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, ina_cnt);
845*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, fina_cnt);
846*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, plcp_err);
847*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, crc32_err);
848*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, overrun_err);
849*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, early_overrun_err);
850*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, crc32_good);
851*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, false_alarm_cnt);
852*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, fina_sync_err_cnt);
853*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, sfd_timeout);
854*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, fina_timeout);
855*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, unresponded_rts);
856*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun);
857*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, sent_ack_cnt);
858*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, sent_cts_cnt);
859*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, sent_ba_rsp_cnt);
860*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, dsp_self_kill);
861*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, mh_format_err);
862*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, re_acq_main_rssi_sum);
863*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, reserved);
864*4882a593Smuzhiyun 	} else {
865*4882a593Smuzhiyun 		struct mvm_statistics_rx_phy *cck = &mvm->rx_stats.cck;
866*4882a593Smuzhiyun 
867*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, unresponded_rts);
868*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun);
869*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, sent_ba_rsp_cnt);
870*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, dsp_self_kill);
871*4882a593Smuzhiyun 		PRINT_STATS_LE32(cck, reserved);
872*4882a593Smuzhiyun 	}
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
875*4882a593Smuzhiyun 			 "Statistics_Rx - GENERAL");
876*4882a593Smuzhiyun 	if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
877*4882a593Smuzhiyun 		struct mvm_statistics_rx_non_phy_v3 *general =
878*4882a593Smuzhiyun 			&mvm->rx_stats_v3.general;
879*4882a593Smuzhiyun 
880*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, bogus_cts);
881*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, bogus_ack);
882*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, non_bssid_frames);
883*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, filtered_frames);
884*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, non_channel_beacons);
885*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, channel_beacons);
886*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, num_missed_bcon);
887*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, adc_rx_saturation_time);
888*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, ina_detection_search_time);
889*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_silence_rssi_a);
890*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_silence_rssi_b);
891*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_silence_rssi_c);
892*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, interference_data_flag);
893*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, channel_load);
894*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, dsp_false_alarms);
895*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_rssi_a);
896*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_rssi_b);
897*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_rssi_c);
898*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_energy_a);
899*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_energy_b);
900*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_energy_c);
901*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, num_bt_kills);
902*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, mac_id);
903*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, directed_data_mpdu);
904*4882a593Smuzhiyun 	} else {
905*4882a593Smuzhiyun 		struct mvm_statistics_rx_non_phy *general =
906*4882a593Smuzhiyun 			&mvm->rx_stats.general;
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, bogus_cts);
909*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, bogus_ack);
910*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, non_channel_beacons);
911*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, channel_beacons);
912*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, num_missed_bcon);
913*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, adc_rx_saturation_time);
914*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, ina_detection_search_time);
915*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_silence_rssi_a);
916*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_silence_rssi_b);
917*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_silence_rssi_c);
918*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, interference_data_flag);
919*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, channel_load);
920*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_rssi_a);
921*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_rssi_b);
922*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_rssi_c);
923*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_energy_a);
924*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_energy_b);
925*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, beacon_energy_c);
926*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, num_bt_kills);
927*4882a593Smuzhiyun 		PRINT_STATS_LE32(general, mac_id);
928*4882a593Smuzhiyun 	}
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
931*4882a593Smuzhiyun 			 "Statistics_Rx - HT");
932*4882a593Smuzhiyun 	if (!iwl_mvm_has_new_rx_stats_api(mvm)) {
933*4882a593Smuzhiyun 		struct mvm_statistics_rx_ht_phy_v1 *ht =
934*4882a593Smuzhiyun 			&mvm->rx_stats_v3.ofdm_ht;
935*4882a593Smuzhiyun 
936*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, plcp_err);
937*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, overrun_err);
938*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, early_overrun_err);
939*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, crc32_good);
940*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, crc32_err);
941*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, mh_format_err);
942*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, agg_crc32_good);
943*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, agg_mpdu_cnt);
944*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, agg_cnt);
945*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, unsupport_mcs);
946*4882a593Smuzhiyun 	} else {
947*4882a593Smuzhiyun 		struct mvm_statistics_rx_ht_phy *ht =
948*4882a593Smuzhiyun 			&mvm->rx_stats.ofdm_ht;
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, mh_format_err);
951*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, agg_mpdu_cnt);
952*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, agg_cnt);
953*4882a593Smuzhiyun 		PRINT_STATS_LE32(ht, unsupport_mcs);
954*4882a593Smuzhiyun 	}
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
957*4882a593Smuzhiyun 
958*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
959*4882a593Smuzhiyun 	kfree(buf);
960*4882a593Smuzhiyun 
961*4882a593Smuzhiyun 	return ret;
962*4882a593Smuzhiyun }
963*4882a593Smuzhiyun #undef PRINT_STAT_LE32
964*4882a593Smuzhiyun 
iwl_dbgfs_frame_stats_read(struct iwl_mvm * mvm,char __user * user_buf,size_t count,loff_t * ppos,struct iwl_mvm_frame_stats * stats)965*4882a593Smuzhiyun static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm,
966*4882a593Smuzhiyun 					  char __user *user_buf, size_t count,
967*4882a593Smuzhiyun 					  loff_t *ppos,
968*4882a593Smuzhiyun 					  struct iwl_mvm_frame_stats *stats)
969*4882a593Smuzhiyun {
970*4882a593Smuzhiyun 	char *buff, *pos, *endpos;
971*4882a593Smuzhiyun 	int idx, i;
972*4882a593Smuzhiyun 	int ret;
973*4882a593Smuzhiyun 	static const size_t bufsz = 1024;
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	buff = kmalloc(bufsz, GFP_KERNEL);
976*4882a593Smuzhiyun 	if (!buff)
977*4882a593Smuzhiyun 		return -ENOMEM;
978*4882a593Smuzhiyun 
979*4882a593Smuzhiyun 	spin_lock_bh(&mvm->drv_stats_lock);
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	pos = buff;
982*4882a593Smuzhiyun 	endpos = pos + bufsz;
983*4882a593Smuzhiyun 
984*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos,
985*4882a593Smuzhiyun 			 "Legacy/HT/VHT\t:\t%d/%d/%d\n",
986*4882a593Smuzhiyun 			 stats->legacy_frames,
987*4882a593Smuzhiyun 			 stats->ht_frames,
988*4882a593Smuzhiyun 			 stats->vht_frames);
989*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "20/40/80\t:\t%d/%d/%d\n",
990*4882a593Smuzhiyun 			 stats->bw_20_frames,
991*4882a593Smuzhiyun 			 stats->bw_40_frames,
992*4882a593Smuzhiyun 			 stats->bw_80_frames);
993*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n",
994*4882a593Smuzhiyun 			 stats->ngi_frames,
995*4882a593Smuzhiyun 			 stats->sgi_frames);
996*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n",
997*4882a593Smuzhiyun 			 stats->siso_frames,
998*4882a593Smuzhiyun 			 stats->mimo2_frames);
999*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n",
1000*4882a593Smuzhiyun 			 stats->fail_frames,
1001*4882a593Smuzhiyun 			 stats->success_frames);
1002*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n",
1003*4882a593Smuzhiyun 			 stats->agg_frames);
1004*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n",
1005*4882a593Smuzhiyun 			 stats->ampdu_count);
1006*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "Avg MPDUs/A-MPDU:\t%d\n",
1007*4882a593Smuzhiyun 			 stats->ampdu_count > 0 ?
1008*4882a593Smuzhiyun 			 (stats->agg_frames / stats->ampdu_count) : 0);
1009*4882a593Smuzhiyun 
1010*4882a593Smuzhiyun 	pos += scnprintf(pos, endpos - pos, "Last Rates\n");
1011*4882a593Smuzhiyun 
1012*4882a593Smuzhiyun 	idx = stats->last_frame_idx - 1;
1013*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(stats->last_rates); i++) {
1014*4882a593Smuzhiyun 		idx = (idx + 1) % ARRAY_SIZE(stats->last_rates);
1015*4882a593Smuzhiyun 		if (stats->last_rates[idx] == 0)
1016*4882a593Smuzhiyun 			continue;
1017*4882a593Smuzhiyun 		pos += scnprintf(pos, endpos - pos, "Rate[%d]: ",
1018*4882a593Smuzhiyun 				 (int)(ARRAY_SIZE(stats->last_rates) - i));
1019*4882a593Smuzhiyun 		pos += rs_pretty_print_rate(pos, endpos - pos,
1020*4882a593Smuzhiyun 					    stats->last_rates[idx]);
1021*4882a593Smuzhiyun 		if (pos < endpos - 1)
1022*4882a593Smuzhiyun 			*pos++ = '\n';
1023*4882a593Smuzhiyun 	}
1024*4882a593Smuzhiyun 	spin_unlock_bh(&mvm->drv_stats_lock);
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
1027*4882a593Smuzhiyun 	kfree(buff);
1028*4882a593Smuzhiyun 
1029*4882a593Smuzhiyun 	return ret;
1030*4882a593Smuzhiyun }
1031*4882a593Smuzhiyun 
iwl_dbgfs_drv_rx_stats_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1032*4882a593Smuzhiyun static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file,
1033*4882a593Smuzhiyun 					   char __user *user_buf, size_t count,
1034*4882a593Smuzhiyun 					   loff_t *ppos)
1035*4882a593Smuzhiyun {
1036*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1037*4882a593Smuzhiyun 
1038*4882a593Smuzhiyun 	return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos,
1039*4882a593Smuzhiyun 					  &mvm->drv_rx_stats);
1040*4882a593Smuzhiyun }
1041*4882a593Smuzhiyun 
iwl_dbgfs_fw_restart_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1042*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
1043*4882a593Smuzhiyun 					  size_t count, loff_t *ppos)
1044*4882a593Smuzhiyun {
1045*4882a593Smuzhiyun 	int __maybe_unused ret;
1046*4882a593Smuzhiyun 
1047*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1048*4882a593Smuzhiyun 		return -EIO;
1049*4882a593Smuzhiyun 
1050*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1051*4882a593Smuzhiyun 
1052*4882a593Smuzhiyun 	/* allow one more restart that we're provoking here */
1053*4882a593Smuzhiyun 	if (mvm->fw_restart >= 0)
1054*4882a593Smuzhiyun 		mvm->fw_restart++;
1055*4882a593Smuzhiyun 
1056*4882a593Smuzhiyun 	/* take the return value to make compiler happy - it will fail anyway */
1057*4882a593Smuzhiyun 	ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL);
1058*4882a593Smuzhiyun 
1059*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1060*4882a593Smuzhiyun 
1061*4882a593Smuzhiyun 	return count;
1062*4882a593Smuzhiyun }
1063*4882a593Smuzhiyun 
iwl_dbgfs_fw_nmi_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1064*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,
1065*4882a593Smuzhiyun 				      size_t count, loff_t *ppos)
1066*4882a593Smuzhiyun {
1067*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1068*4882a593Smuzhiyun 		return -EIO;
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun 	iwl_force_nmi(mvm->trans);
1071*4882a593Smuzhiyun 
1072*4882a593Smuzhiyun 	return count;
1073*4882a593Smuzhiyun }
1074*4882a593Smuzhiyun 
1075*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_scan_ant_rxchain_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1076*4882a593Smuzhiyun iwl_dbgfs_scan_ant_rxchain_read(struct file *file,
1077*4882a593Smuzhiyun 				char __user *user_buf,
1078*4882a593Smuzhiyun 				size_t count, loff_t *ppos)
1079*4882a593Smuzhiyun {
1080*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1081*4882a593Smuzhiyun 	int pos = 0;
1082*4882a593Smuzhiyun 	char buf[32];
1083*4882a593Smuzhiyun 	const size_t bufsz = sizeof(buf);
1084*4882a593Smuzhiyun 
1085*4882a593Smuzhiyun 	/* print which antennas were set for the scan command by the user */
1086*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: ");
1087*4882a593Smuzhiyun 	if (mvm->scan_rx_ant & ANT_A)
1088*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos, "A");
1089*4882a593Smuzhiyun 	if (mvm->scan_rx_ant & ANT_B)
1090*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos, "B");
1091*4882a593Smuzhiyun 	if (mvm->scan_rx_ant & ANT_C)
1092*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos, "C");
1093*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant);
1094*4882a593Smuzhiyun 
1095*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1096*4882a593Smuzhiyun }
1097*4882a593Smuzhiyun 
1098*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1099*4882a593Smuzhiyun iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
1100*4882a593Smuzhiyun 				 size_t count, loff_t *ppos)
1101*4882a593Smuzhiyun {
1102*4882a593Smuzhiyun 	u8 scan_rx_ant;
1103*4882a593Smuzhiyun 
1104*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1105*4882a593Smuzhiyun 		return -EIO;
1106*4882a593Smuzhiyun 
1107*4882a593Smuzhiyun 	if (sscanf(buf, "%hhx", &scan_rx_ant) != 1)
1108*4882a593Smuzhiyun 		return -EINVAL;
1109*4882a593Smuzhiyun 	if (scan_rx_ant > ANT_ABC)
1110*4882a593Smuzhiyun 		return -EINVAL;
1111*4882a593Smuzhiyun 	if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm)))
1112*4882a593Smuzhiyun 		return -EINVAL;
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun 	if (mvm->scan_rx_ant != scan_rx_ant) {
1115*4882a593Smuzhiyun 		mvm->scan_rx_ant = scan_rx_ant;
1116*4882a593Smuzhiyun 		if (fw_has_capa(&mvm->fw->ucode_capa,
1117*4882a593Smuzhiyun 				IWL_UCODE_TLV_CAPA_UMAC_SCAN))
1118*4882a593Smuzhiyun 			iwl_mvm_config_scan(mvm);
1119*4882a593Smuzhiyun 	}
1120*4882a593Smuzhiyun 
1121*4882a593Smuzhiyun 	return count;
1122*4882a593Smuzhiyun }
1123*4882a593Smuzhiyun 
iwl_dbgfs_indirection_tbl_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1124*4882a593Smuzhiyun static ssize_t iwl_dbgfs_indirection_tbl_write(struct iwl_mvm *mvm,
1125*4882a593Smuzhiyun 					       char *buf, size_t count,
1126*4882a593Smuzhiyun 					       loff_t *ppos)
1127*4882a593Smuzhiyun {
1128*4882a593Smuzhiyun 	struct iwl_rss_config_cmd cmd = {
1129*4882a593Smuzhiyun 		.flags = cpu_to_le32(IWL_RSS_ENABLE),
1130*4882a593Smuzhiyun 		.hash_mask = IWL_RSS_HASH_TYPE_IPV4_TCP |
1131*4882a593Smuzhiyun 			     IWL_RSS_HASH_TYPE_IPV4_UDP |
1132*4882a593Smuzhiyun 			     IWL_RSS_HASH_TYPE_IPV4_PAYLOAD |
1133*4882a593Smuzhiyun 			     IWL_RSS_HASH_TYPE_IPV6_TCP |
1134*4882a593Smuzhiyun 			     IWL_RSS_HASH_TYPE_IPV6_UDP |
1135*4882a593Smuzhiyun 			     IWL_RSS_HASH_TYPE_IPV6_PAYLOAD,
1136*4882a593Smuzhiyun 	};
1137*4882a593Smuzhiyun 	int ret, i, num_repeats, nbytes = count / 2;
1138*4882a593Smuzhiyun 
1139*4882a593Smuzhiyun 	ret = hex2bin(cmd.indirection_table, buf, nbytes);
1140*4882a593Smuzhiyun 	if (ret)
1141*4882a593Smuzhiyun 		return ret;
1142*4882a593Smuzhiyun 
1143*4882a593Smuzhiyun 	/*
1144*4882a593Smuzhiyun 	 * The input is the redirection table, partial or full.
1145*4882a593Smuzhiyun 	 * Repeat the pattern if needed.
1146*4882a593Smuzhiyun 	 * For example, input of 01020F will be repeated 42 times,
1147*4882a593Smuzhiyun 	 * indirecting RSS hash results to queues 1, 2, 15 (skipping
1148*4882a593Smuzhiyun 	 * queues 3 - 14).
1149*4882a593Smuzhiyun 	 */
1150*4882a593Smuzhiyun 	num_repeats = ARRAY_SIZE(cmd.indirection_table) / nbytes;
1151*4882a593Smuzhiyun 	for (i = 1; i < num_repeats; i++)
1152*4882a593Smuzhiyun 		memcpy(&cmd.indirection_table[i * nbytes],
1153*4882a593Smuzhiyun 		       cmd.indirection_table, nbytes);
1154*4882a593Smuzhiyun 	/* handle cut in the middle pattern for the last places */
1155*4882a593Smuzhiyun 	memcpy(&cmd.indirection_table[i * nbytes], cmd.indirection_table,
1156*4882a593Smuzhiyun 	       ARRAY_SIZE(cmd.indirection_table) % nbytes);
1157*4882a593Smuzhiyun 
1158*4882a593Smuzhiyun 	netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key));
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1161*4882a593Smuzhiyun 	if (iwl_mvm_firmware_running(mvm))
1162*4882a593Smuzhiyun 		ret = iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0,
1163*4882a593Smuzhiyun 					   sizeof(cmd), &cmd);
1164*4882a593Smuzhiyun 	else
1165*4882a593Smuzhiyun 		ret = 0;
1166*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1167*4882a593Smuzhiyun 
1168*4882a593Smuzhiyun 	return ret ?: count;
1169*4882a593Smuzhiyun }
1170*4882a593Smuzhiyun 
iwl_dbgfs_inject_packet_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1171*4882a593Smuzhiyun static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mvm *mvm,
1172*4882a593Smuzhiyun 					     char *buf, size_t count,
1173*4882a593Smuzhiyun 					     loff_t *ppos)
1174*4882a593Smuzhiyun {
1175*4882a593Smuzhiyun 	struct iwl_rx_cmd_buffer rxb = {
1176*4882a593Smuzhiyun 		._rx_page_order = 0,
1177*4882a593Smuzhiyun 		.truesize = 0, /* not used */
1178*4882a593Smuzhiyun 		._offset = 0,
1179*4882a593Smuzhiyun 	};
1180*4882a593Smuzhiyun 	struct iwl_rx_packet *pkt;
1181*4882a593Smuzhiyun 	struct iwl_rx_mpdu_desc *desc;
1182*4882a593Smuzhiyun 	int bin_len = count / 2;
1183*4882a593Smuzhiyun 	int ret = -EINVAL;
1184*4882a593Smuzhiyun 	size_t mpdu_cmd_hdr_size = (mvm->trans->trans_cfg->device_family >=
1185*4882a593Smuzhiyun 				    IWL_DEVICE_FAMILY_AX210) ?
1186*4882a593Smuzhiyun 		sizeof(struct iwl_rx_mpdu_desc) :
1187*4882a593Smuzhiyun 		IWL_RX_DESC_SIZE_V1;
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1190*4882a593Smuzhiyun 		return -EIO;
1191*4882a593Smuzhiyun 
1192*4882a593Smuzhiyun 	/* supporting only 9000 descriptor */
1193*4882a593Smuzhiyun 	if (!mvm->trans->trans_cfg->mq_rx_supported)
1194*4882a593Smuzhiyun 		return -ENOTSUPP;
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	rxb._page = alloc_pages(GFP_ATOMIC, 0);
1197*4882a593Smuzhiyun 	if (!rxb._page)
1198*4882a593Smuzhiyun 		return -ENOMEM;
1199*4882a593Smuzhiyun 	pkt = rxb_addr(&rxb);
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun 	ret = hex2bin(page_address(rxb._page), buf, bin_len);
1202*4882a593Smuzhiyun 	if (ret)
1203*4882a593Smuzhiyun 		goto out;
1204*4882a593Smuzhiyun 
1205*4882a593Smuzhiyun 	/* avoid invalid memory access */
1206*4882a593Smuzhiyun 	if (bin_len < sizeof(*pkt) + mpdu_cmd_hdr_size)
1207*4882a593Smuzhiyun 		goto out;
1208*4882a593Smuzhiyun 
1209*4882a593Smuzhiyun 	/* check this is RX packet */
1210*4882a593Smuzhiyun 	if (WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd) !=
1211*4882a593Smuzhiyun 	    WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))
1212*4882a593Smuzhiyun 		goto out;
1213*4882a593Smuzhiyun 
1214*4882a593Smuzhiyun 	/* check the length in metadata matches actual received length */
1215*4882a593Smuzhiyun 	desc = (void *)pkt->data;
1216*4882a593Smuzhiyun 	if (le16_to_cpu(desc->mpdu_len) !=
1217*4882a593Smuzhiyun 	    (bin_len - mpdu_cmd_hdr_size - sizeof(*pkt)))
1218*4882a593Smuzhiyun 		goto out;
1219*4882a593Smuzhiyun 
1220*4882a593Smuzhiyun 	local_bh_disable();
1221*4882a593Smuzhiyun 	iwl_mvm_rx_mpdu_mq(mvm, NULL, &rxb, 0);
1222*4882a593Smuzhiyun 	local_bh_enable();
1223*4882a593Smuzhiyun 	ret = 0;
1224*4882a593Smuzhiyun 
1225*4882a593Smuzhiyun out:
1226*4882a593Smuzhiyun 	iwl_free_rxb(&rxb);
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 	return ret ?: count;
1229*4882a593Smuzhiyun }
1230*4882a593Smuzhiyun 
_iwl_dbgfs_inject_beacon_ie(struct iwl_mvm * mvm,char * bin,int len)1231*4882a593Smuzhiyun static int _iwl_dbgfs_inject_beacon_ie(struct iwl_mvm *mvm, char *bin, int len)
1232*4882a593Smuzhiyun {
1233*4882a593Smuzhiyun 	struct ieee80211_vif *vif;
1234*4882a593Smuzhiyun 	struct iwl_mvm_vif *mvmvif;
1235*4882a593Smuzhiyun 	struct sk_buff *beacon;
1236*4882a593Smuzhiyun 	struct ieee80211_tx_info *info;
1237*4882a593Smuzhiyun 	struct iwl_mac_beacon_cmd beacon_cmd = {};
1238*4882a593Smuzhiyun 	u8 rate;
1239*4882a593Smuzhiyun 	u16 flags;
1240*4882a593Smuzhiyun 	int i;
1241*4882a593Smuzhiyun 
1242*4882a593Smuzhiyun 	len /= 2;
1243*4882a593Smuzhiyun 
1244*4882a593Smuzhiyun 	/* Element len should be represented by u8 */
1245*4882a593Smuzhiyun 	if (len >= U8_MAX)
1246*4882a593Smuzhiyun 		return -EINVAL;
1247*4882a593Smuzhiyun 
1248*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1249*4882a593Smuzhiyun 		return -EIO;
1250*4882a593Smuzhiyun 
1251*4882a593Smuzhiyun 	if (!iwl_mvm_has_new_tx_api(mvm) &&
1252*4882a593Smuzhiyun 	    !fw_has_api(&mvm->fw->ucode_capa,
1253*4882a593Smuzhiyun 			IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE))
1254*4882a593Smuzhiyun 		return -EINVAL;
1255*4882a593Smuzhiyun 
1256*4882a593Smuzhiyun 	rcu_read_lock();
1257*4882a593Smuzhiyun 
1258*4882a593Smuzhiyun 	for (i = 0; i < NUM_MAC_INDEX_DRIVER; i++) {
1259*4882a593Smuzhiyun 		vif = iwl_mvm_rcu_dereference_vif_id(mvm, i, true);
1260*4882a593Smuzhiyun 		if (!vif)
1261*4882a593Smuzhiyun 			continue;
1262*4882a593Smuzhiyun 
1263*4882a593Smuzhiyun 		if (vif->type == NL80211_IFTYPE_AP)
1264*4882a593Smuzhiyun 			break;
1265*4882a593Smuzhiyun 	}
1266*4882a593Smuzhiyun 
1267*4882a593Smuzhiyun 	if (i == NUM_MAC_INDEX_DRIVER || !vif)
1268*4882a593Smuzhiyun 		goto out_err;
1269*4882a593Smuzhiyun 
1270*4882a593Smuzhiyun 	mvm->hw->extra_beacon_tailroom = len;
1271*4882a593Smuzhiyun 
1272*4882a593Smuzhiyun 	beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL);
1273*4882a593Smuzhiyun 	if (!beacon)
1274*4882a593Smuzhiyun 		goto out_err;
1275*4882a593Smuzhiyun 
1276*4882a593Smuzhiyun 	if (len && hex2bin(skb_put_zero(beacon, len), bin, len)) {
1277*4882a593Smuzhiyun 		dev_kfree_skb(beacon);
1278*4882a593Smuzhiyun 		goto out_err;
1279*4882a593Smuzhiyun 	}
1280*4882a593Smuzhiyun 
1281*4882a593Smuzhiyun 	mvm->beacon_inject_active = true;
1282*4882a593Smuzhiyun 
1283*4882a593Smuzhiyun 	mvmvif = iwl_mvm_vif_from_mac80211(vif);
1284*4882a593Smuzhiyun 	info = IEEE80211_SKB_CB(beacon);
1285*4882a593Smuzhiyun 	rate = iwl_mvm_mac_ctxt_get_lowest_rate(info, vif);
1286*4882a593Smuzhiyun 	flags = iwl_mvm_mac80211_idx_to_hwrate(rate);
1287*4882a593Smuzhiyun 
1288*4882a593Smuzhiyun 	if (rate == IWL_FIRST_CCK_RATE)
1289*4882a593Smuzhiyun 		flags |= IWL_MAC_BEACON_CCK;
1290*4882a593Smuzhiyun 
1291*4882a593Smuzhiyun 	beacon_cmd.flags = cpu_to_le16(flags);
1292*4882a593Smuzhiyun 	beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len);
1293*4882a593Smuzhiyun 	beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
1294*4882a593Smuzhiyun 
1295*4882a593Smuzhiyun 	iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd.tim_idx,
1296*4882a593Smuzhiyun 				 &beacon_cmd.tim_size,
1297*4882a593Smuzhiyun 				 beacon->data, beacon->len);
1298*4882a593Smuzhiyun 
1299*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1300*4882a593Smuzhiyun 	iwl_mvm_mac_ctxt_send_beacon_cmd(mvm, beacon, &beacon_cmd,
1301*4882a593Smuzhiyun 					 sizeof(beacon_cmd));
1302*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1303*4882a593Smuzhiyun 
1304*4882a593Smuzhiyun 	dev_kfree_skb(beacon);
1305*4882a593Smuzhiyun 
1306*4882a593Smuzhiyun 	rcu_read_unlock();
1307*4882a593Smuzhiyun 	return 0;
1308*4882a593Smuzhiyun 
1309*4882a593Smuzhiyun out_err:
1310*4882a593Smuzhiyun 	rcu_read_unlock();
1311*4882a593Smuzhiyun 	return -EINVAL;
1312*4882a593Smuzhiyun }
1313*4882a593Smuzhiyun 
iwl_dbgfs_inject_beacon_ie_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1314*4882a593Smuzhiyun static ssize_t iwl_dbgfs_inject_beacon_ie_write(struct iwl_mvm *mvm,
1315*4882a593Smuzhiyun 						char *buf, size_t count,
1316*4882a593Smuzhiyun 						loff_t *ppos)
1317*4882a593Smuzhiyun {
1318*4882a593Smuzhiyun 	int ret = _iwl_dbgfs_inject_beacon_ie(mvm, buf, count);
1319*4882a593Smuzhiyun 
1320*4882a593Smuzhiyun 	mvm->hw->extra_beacon_tailroom = 0;
1321*4882a593Smuzhiyun 	return ret ?: count;
1322*4882a593Smuzhiyun }
1323*4882a593Smuzhiyun 
iwl_dbgfs_inject_beacon_ie_restore_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1324*4882a593Smuzhiyun static ssize_t iwl_dbgfs_inject_beacon_ie_restore_write(struct iwl_mvm *mvm,
1325*4882a593Smuzhiyun 							char *buf,
1326*4882a593Smuzhiyun 							size_t count,
1327*4882a593Smuzhiyun 							loff_t *ppos)
1328*4882a593Smuzhiyun {
1329*4882a593Smuzhiyun 	int ret = _iwl_dbgfs_inject_beacon_ie(mvm, NULL, 0);
1330*4882a593Smuzhiyun 
1331*4882a593Smuzhiyun 	mvm->hw->extra_beacon_tailroom = 0;
1332*4882a593Smuzhiyun 	mvm->beacon_inject_active = false;
1333*4882a593Smuzhiyun 	return ret ?: count;
1334*4882a593Smuzhiyun }
1335*4882a593Smuzhiyun 
iwl_dbgfs_fw_dbg_conf_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1336*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
1337*4882a593Smuzhiyun 					  char __user *user_buf,
1338*4882a593Smuzhiyun 					  size_t count, loff_t *ppos)
1339*4882a593Smuzhiyun {
1340*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1341*4882a593Smuzhiyun 	int conf;
1342*4882a593Smuzhiyun 	char buf[8];
1343*4882a593Smuzhiyun 	const size_t bufsz = sizeof(buf);
1344*4882a593Smuzhiyun 	int pos = 0;
1345*4882a593Smuzhiyun 
1346*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1347*4882a593Smuzhiyun 	conf = mvm->fwrt.dump.conf;
1348*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1349*4882a593Smuzhiyun 
1350*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf);
1351*4882a593Smuzhiyun 
1352*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1353*4882a593Smuzhiyun }
1354*4882a593Smuzhiyun 
iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1355*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
1356*4882a593Smuzhiyun 					   char *buf, size_t count,
1357*4882a593Smuzhiyun 					   loff_t *ppos)
1358*4882a593Smuzhiyun {
1359*4882a593Smuzhiyun 	unsigned int conf_id;
1360*4882a593Smuzhiyun 	int ret;
1361*4882a593Smuzhiyun 
1362*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1363*4882a593Smuzhiyun 		return -EIO;
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 	ret = kstrtouint(buf, 0, &conf_id);
1366*4882a593Smuzhiyun 	if (ret)
1367*4882a593Smuzhiyun 		return ret;
1368*4882a593Smuzhiyun 
1369*4882a593Smuzhiyun 	if (WARN_ON(conf_id >= FW_DBG_CONF_MAX))
1370*4882a593Smuzhiyun 		return -EINVAL;
1371*4882a593Smuzhiyun 
1372*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1373*4882a593Smuzhiyun 	ret = iwl_fw_start_dbg_conf(&mvm->fwrt, conf_id);
1374*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun 	return ret ?: count;
1377*4882a593Smuzhiyun }
1378*4882a593Smuzhiyun 
iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1379*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
1380*4882a593Smuzhiyun 					      char *buf, size_t count,
1381*4882a593Smuzhiyun 					      loff_t *ppos)
1382*4882a593Smuzhiyun {
1383*4882a593Smuzhiyun 	if (count == 0)
1384*4882a593Smuzhiyun 		return 0;
1385*4882a593Smuzhiyun 
1386*4882a593Smuzhiyun 	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_USER_TRIGGER,
1387*4882a593Smuzhiyun 			       NULL);
1388*4882a593Smuzhiyun 
1389*4882a593Smuzhiyun 	iwl_fw_dbg_collect(&mvm->fwrt, FW_DBG_TRIGGER_USER, buf,
1390*4882a593Smuzhiyun 			   (count - 1), NULL);
1391*4882a593Smuzhiyun 
1392*4882a593Smuzhiyun 	return count;
1393*4882a593Smuzhiyun }
1394*4882a593Smuzhiyun 
1395*4882a593Smuzhiyun #define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
1396*4882a593Smuzhiyun #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
iwl_dbgfs_bcast_filters_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1397*4882a593Smuzhiyun static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
1398*4882a593Smuzhiyun 					    char __user *user_buf,
1399*4882a593Smuzhiyun 					    size_t count, loff_t *ppos)
1400*4882a593Smuzhiyun {
1401*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1402*4882a593Smuzhiyun 	struct iwl_bcast_filter_cmd cmd;
1403*4882a593Smuzhiyun 	const struct iwl_fw_bcast_filter *filter;
1404*4882a593Smuzhiyun 	char *buf;
1405*4882a593Smuzhiyun 	int bufsz = 1024;
1406*4882a593Smuzhiyun 	int i, j, pos = 0;
1407*4882a593Smuzhiyun 	ssize_t ret;
1408*4882a593Smuzhiyun 
1409*4882a593Smuzhiyun 	buf = kzalloc(bufsz, GFP_KERNEL);
1410*4882a593Smuzhiyun 	if (!buf)
1411*4882a593Smuzhiyun 		return -ENOMEM;
1412*4882a593Smuzhiyun 
1413*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1414*4882a593Smuzhiyun 	if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
1415*4882a593Smuzhiyun 		ADD_TEXT("None\n");
1416*4882a593Smuzhiyun 		mutex_unlock(&mvm->mutex);
1417*4882a593Smuzhiyun 		goto out;
1418*4882a593Smuzhiyun 	}
1419*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun 	for (i = 0; cmd.filters[i].attrs[0].mask; i++) {
1422*4882a593Smuzhiyun 		filter = &cmd.filters[i];
1423*4882a593Smuzhiyun 
1424*4882a593Smuzhiyun 		ADD_TEXT("Filter [%d]:\n", i);
1425*4882a593Smuzhiyun 		ADD_TEXT("\tDiscard=%d\n", filter->discard);
1426*4882a593Smuzhiyun 		ADD_TEXT("\tFrame Type: %s\n",
1427*4882a593Smuzhiyun 			 filter->frame_type ? "IPv4" : "Generic");
1428*4882a593Smuzhiyun 
1429*4882a593Smuzhiyun 		for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) {
1430*4882a593Smuzhiyun 			const struct iwl_fw_bcast_filter_attr *attr;
1431*4882a593Smuzhiyun 
1432*4882a593Smuzhiyun 			attr = &filter->attrs[j];
1433*4882a593Smuzhiyun 			if (!attr->mask)
1434*4882a593Smuzhiyun 				break;
1435*4882a593Smuzhiyun 
1436*4882a593Smuzhiyun 			ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n",
1437*4882a593Smuzhiyun 				 j, attr->offset,
1438*4882a593Smuzhiyun 				 attr->offset_type ? "IP End" :
1439*4882a593Smuzhiyun 						     "Payload Start",
1440*4882a593Smuzhiyun 				 be32_to_cpu(attr->mask),
1441*4882a593Smuzhiyun 				 be32_to_cpu(attr->val),
1442*4882a593Smuzhiyun 				 le16_to_cpu(attr->reserved1));
1443*4882a593Smuzhiyun 		}
1444*4882a593Smuzhiyun 	}
1445*4882a593Smuzhiyun out:
1446*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1447*4882a593Smuzhiyun 	kfree(buf);
1448*4882a593Smuzhiyun 	return ret;
1449*4882a593Smuzhiyun }
1450*4882a593Smuzhiyun 
iwl_dbgfs_bcast_filters_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1451*4882a593Smuzhiyun static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
1452*4882a593Smuzhiyun 					     size_t count, loff_t *ppos)
1453*4882a593Smuzhiyun {
1454*4882a593Smuzhiyun 	int pos, next_pos;
1455*4882a593Smuzhiyun 	struct iwl_fw_bcast_filter filter = {};
1456*4882a593Smuzhiyun 	struct iwl_bcast_filter_cmd cmd;
1457*4882a593Smuzhiyun 	u32 filter_id, attr_id, mask, value;
1458*4882a593Smuzhiyun 	int err = 0;
1459*4882a593Smuzhiyun 
1460*4882a593Smuzhiyun 	if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard,
1461*4882a593Smuzhiyun 		   &filter.frame_type, &pos) != 3)
1462*4882a593Smuzhiyun 		return -EINVAL;
1463*4882a593Smuzhiyun 
1464*4882a593Smuzhiyun 	if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) ||
1465*4882a593Smuzhiyun 	    filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4)
1466*4882a593Smuzhiyun 		return -EINVAL;
1467*4882a593Smuzhiyun 
1468*4882a593Smuzhiyun 	for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs);
1469*4882a593Smuzhiyun 	     attr_id++) {
1470*4882a593Smuzhiyun 		struct iwl_fw_bcast_filter_attr *attr =
1471*4882a593Smuzhiyun 				&filter.attrs[attr_id];
1472*4882a593Smuzhiyun 
1473*4882a593Smuzhiyun 		if (pos >= count)
1474*4882a593Smuzhiyun 			break;
1475*4882a593Smuzhiyun 
1476*4882a593Smuzhiyun 		if (sscanf(&buf[pos], "%hhi %hhi %i %i %n",
1477*4882a593Smuzhiyun 			   &attr->offset, &attr->offset_type,
1478*4882a593Smuzhiyun 			   &mask, &value, &next_pos) != 4)
1479*4882a593Smuzhiyun 			return -EINVAL;
1480*4882a593Smuzhiyun 
1481*4882a593Smuzhiyun 		attr->mask = cpu_to_be32(mask);
1482*4882a593Smuzhiyun 		attr->val = cpu_to_be32(value);
1483*4882a593Smuzhiyun 		if (mask)
1484*4882a593Smuzhiyun 			filter.num_attrs++;
1485*4882a593Smuzhiyun 
1486*4882a593Smuzhiyun 		pos += next_pos;
1487*4882a593Smuzhiyun 	}
1488*4882a593Smuzhiyun 
1489*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1490*4882a593Smuzhiyun 	memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id],
1491*4882a593Smuzhiyun 	       &filter, sizeof(filter));
1492*4882a593Smuzhiyun 
1493*4882a593Smuzhiyun 	/* send updated bcast filtering configuration */
1494*4882a593Smuzhiyun 	if (iwl_mvm_firmware_running(mvm) &&
1495*4882a593Smuzhiyun 	    mvm->dbgfs_bcast_filtering.override &&
1496*4882a593Smuzhiyun 	    iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
1497*4882a593Smuzhiyun 		err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
1498*4882a593Smuzhiyun 					   sizeof(cmd), &cmd);
1499*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1500*4882a593Smuzhiyun 
1501*4882a593Smuzhiyun 	return err ?: count;
1502*4882a593Smuzhiyun }
1503*4882a593Smuzhiyun 
iwl_dbgfs_bcast_filters_macs_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1504*4882a593Smuzhiyun static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file,
1505*4882a593Smuzhiyun 						 char __user *user_buf,
1506*4882a593Smuzhiyun 						 size_t count, loff_t *ppos)
1507*4882a593Smuzhiyun {
1508*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1509*4882a593Smuzhiyun 	struct iwl_bcast_filter_cmd cmd;
1510*4882a593Smuzhiyun 	char *buf;
1511*4882a593Smuzhiyun 	int bufsz = 1024;
1512*4882a593Smuzhiyun 	int i, pos = 0;
1513*4882a593Smuzhiyun 	ssize_t ret;
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun 	buf = kzalloc(bufsz, GFP_KERNEL);
1516*4882a593Smuzhiyun 	if (!buf)
1517*4882a593Smuzhiyun 		return -ENOMEM;
1518*4882a593Smuzhiyun 
1519*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1520*4882a593Smuzhiyun 	if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
1521*4882a593Smuzhiyun 		ADD_TEXT("None\n");
1522*4882a593Smuzhiyun 		mutex_unlock(&mvm->mutex);
1523*4882a593Smuzhiyun 		goto out;
1524*4882a593Smuzhiyun 	}
1525*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1526*4882a593Smuzhiyun 
1527*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) {
1528*4882a593Smuzhiyun 		const struct iwl_fw_bcast_mac *mac = &cmd.macs[i];
1529*4882a593Smuzhiyun 
1530*4882a593Smuzhiyun 		ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n",
1531*4882a593Smuzhiyun 			 i, mac->default_discard, mac->attached_filters);
1532*4882a593Smuzhiyun 	}
1533*4882a593Smuzhiyun out:
1534*4882a593Smuzhiyun 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1535*4882a593Smuzhiyun 	kfree(buf);
1536*4882a593Smuzhiyun 	return ret;
1537*4882a593Smuzhiyun }
1538*4882a593Smuzhiyun 
iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1539*4882a593Smuzhiyun static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
1540*4882a593Smuzhiyun 						  char *buf, size_t count,
1541*4882a593Smuzhiyun 						  loff_t *ppos)
1542*4882a593Smuzhiyun {
1543*4882a593Smuzhiyun 	struct iwl_bcast_filter_cmd cmd;
1544*4882a593Smuzhiyun 	struct iwl_fw_bcast_mac mac = {};
1545*4882a593Smuzhiyun 	u32 mac_id, attached_filters;
1546*4882a593Smuzhiyun 	int err = 0;
1547*4882a593Smuzhiyun 
1548*4882a593Smuzhiyun 	if (!mvm->bcast_filters)
1549*4882a593Smuzhiyun 		return -ENOENT;
1550*4882a593Smuzhiyun 
1551*4882a593Smuzhiyun 	if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard,
1552*4882a593Smuzhiyun 		   &attached_filters) != 3)
1553*4882a593Smuzhiyun 		return -EINVAL;
1554*4882a593Smuzhiyun 
1555*4882a593Smuzhiyun 	if (mac_id >= ARRAY_SIZE(cmd.macs) ||
1556*4882a593Smuzhiyun 	    mac.default_discard > 1 ||
1557*4882a593Smuzhiyun 	    attached_filters >= BIT(ARRAY_SIZE(cmd.filters)))
1558*4882a593Smuzhiyun 		return -EINVAL;
1559*4882a593Smuzhiyun 
1560*4882a593Smuzhiyun 	mac.attached_filters = cpu_to_le16(attached_filters);
1561*4882a593Smuzhiyun 
1562*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1563*4882a593Smuzhiyun 	memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id],
1564*4882a593Smuzhiyun 	       &mac, sizeof(mac));
1565*4882a593Smuzhiyun 
1566*4882a593Smuzhiyun 	/* send updated bcast filtering configuration */
1567*4882a593Smuzhiyun 	if (iwl_mvm_firmware_running(mvm) &&
1568*4882a593Smuzhiyun 	    mvm->dbgfs_bcast_filtering.override &&
1569*4882a593Smuzhiyun 	    iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
1570*4882a593Smuzhiyun 		err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0,
1571*4882a593Smuzhiyun 					   sizeof(cmd), &cmd);
1572*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1573*4882a593Smuzhiyun 
1574*4882a593Smuzhiyun 	return err ?: count;
1575*4882a593Smuzhiyun }
1576*4882a593Smuzhiyun #endif
1577*4882a593Smuzhiyun 
1578*4882a593Smuzhiyun #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
1579*4882a593Smuzhiyun 	_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
1580*4882a593Smuzhiyun #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
1581*4882a593Smuzhiyun 	_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
1582*4882a593Smuzhiyun #define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do {	\
1583*4882a593Smuzhiyun 		debugfs_create_file(alias, mode, parent, mvm,		\
1584*4882a593Smuzhiyun 				    &iwl_dbgfs_##name##_ops);		\
1585*4882a593Smuzhiyun 	} while (0)
1586*4882a593Smuzhiyun #define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
1587*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
1588*4882a593Smuzhiyun 
1589*4882a593Smuzhiyun #define MVM_DEBUGFS_WRITE_STA_FILE_OPS(name, bufsz) \
1590*4882a593Smuzhiyun 	_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_sta)
1591*4882a593Smuzhiyun #define MVM_DEBUGFS_READ_WRITE_STA_FILE_OPS(name, bufsz) \
1592*4882a593Smuzhiyun 	_MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_sta)
1593*4882a593Smuzhiyun 
1594*4882a593Smuzhiyun #define MVM_DEBUGFS_ADD_STA_FILE_ALIAS(alias, name, parent, mode) do {	\
1595*4882a593Smuzhiyun 		debugfs_create_file(alias, mode, parent, sta,		\
1596*4882a593Smuzhiyun 				    &iwl_dbgfs_##name##_ops);		\
1597*4882a593Smuzhiyun 	} while (0)
1598*4882a593Smuzhiyun #define MVM_DEBUGFS_ADD_STA_FILE(name, parent, mode) \
1599*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_STA_FILE_ALIAS(#name, name, parent, mode)
1600*4882a593Smuzhiyun 
1601*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_prph_reg_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1602*4882a593Smuzhiyun iwl_dbgfs_prph_reg_read(struct file *file,
1603*4882a593Smuzhiyun 			char __user *user_buf,
1604*4882a593Smuzhiyun 			size_t count, loff_t *ppos)
1605*4882a593Smuzhiyun {
1606*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1607*4882a593Smuzhiyun 	int pos = 0;
1608*4882a593Smuzhiyun 	char buf[32];
1609*4882a593Smuzhiyun 	const size_t bufsz = sizeof(buf);
1610*4882a593Smuzhiyun 
1611*4882a593Smuzhiyun 	if (!mvm->dbgfs_prph_reg_addr)
1612*4882a593Smuzhiyun 		return -EINVAL;
1613*4882a593Smuzhiyun 
1614*4882a593Smuzhiyun 	pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n",
1615*4882a593Smuzhiyun 		mvm->dbgfs_prph_reg_addr,
1616*4882a593Smuzhiyun 		iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr));
1617*4882a593Smuzhiyun 
1618*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1619*4882a593Smuzhiyun }
1620*4882a593Smuzhiyun 
1621*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_prph_reg_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1622*4882a593Smuzhiyun iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
1623*4882a593Smuzhiyun 			 size_t count, loff_t *ppos)
1624*4882a593Smuzhiyun {
1625*4882a593Smuzhiyun 	u8 args;
1626*4882a593Smuzhiyun 	u32 value;
1627*4882a593Smuzhiyun 
1628*4882a593Smuzhiyun 	args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value);
1629*4882a593Smuzhiyun 	/* if we only want to set the reg address - nothing more to do */
1630*4882a593Smuzhiyun 	if (args == 1)
1631*4882a593Smuzhiyun 		goto out;
1632*4882a593Smuzhiyun 
1633*4882a593Smuzhiyun 	/* otherwise, make sure we have both address and value */
1634*4882a593Smuzhiyun 	if (args != 2)
1635*4882a593Smuzhiyun 		return -EINVAL;
1636*4882a593Smuzhiyun 
1637*4882a593Smuzhiyun 	iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value);
1638*4882a593Smuzhiyun 
1639*4882a593Smuzhiyun out:
1640*4882a593Smuzhiyun 	return count;
1641*4882a593Smuzhiyun }
1642*4882a593Smuzhiyun 
1643*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1644*4882a593Smuzhiyun iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf,
1645*4882a593Smuzhiyun 			      size_t count, loff_t *ppos)
1646*4882a593Smuzhiyun {
1647*4882a593Smuzhiyun 	int ret;
1648*4882a593Smuzhiyun 
1649*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1650*4882a593Smuzhiyun 		return -EIO;
1651*4882a593Smuzhiyun 
1652*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1653*4882a593Smuzhiyun 	ret = iwl_mvm_send_cmd_pdu(mvm, ECHO_CMD, 0, 0, NULL);
1654*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1655*4882a593Smuzhiyun 
1656*4882a593Smuzhiyun 	return ret ?: count;
1657*4882a593Smuzhiyun }
1658*4882a593Smuzhiyun 
1659*4882a593Smuzhiyun struct iwl_mvm_sniffer_apply {
1660*4882a593Smuzhiyun 	struct iwl_mvm *mvm;
1661*4882a593Smuzhiyun 	u8 *bssid;
1662*4882a593Smuzhiyun 	u16 aid;
1663*4882a593Smuzhiyun };
1664*4882a593Smuzhiyun 
iwl_mvm_sniffer_apply(struct iwl_notif_wait_data * notif_data,struct iwl_rx_packet * pkt,void * data)1665*4882a593Smuzhiyun static bool iwl_mvm_sniffer_apply(struct iwl_notif_wait_data *notif_data,
1666*4882a593Smuzhiyun 				  struct iwl_rx_packet *pkt, void *data)
1667*4882a593Smuzhiyun {
1668*4882a593Smuzhiyun 	struct iwl_mvm_sniffer_apply *apply = data;
1669*4882a593Smuzhiyun 
1670*4882a593Smuzhiyun 	apply->mvm->cur_aid = cpu_to_le16(apply->aid);
1671*4882a593Smuzhiyun 	memcpy(apply->mvm->cur_bssid, apply->bssid,
1672*4882a593Smuzhiyun 	       sizeof(apply->mvm->cur_bssid));
1673*4882a593Smuzhiyun 
1674*4882a593Smuzhiyun 	return true;
1675*4882a593Smuzhiyun }
1676*4882a593Smuzhiyun 
1677*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1678*4882a593Smuzhiyun iwl_dbgfs_he_sniffer_params_write(struct iwl_mvm *mvm, char *buf,
1679*4882a593Smuzhiyun 				  size_t count, loff_t *ppos)
1680*4882a593Smuzhiyun {
1681*4882a593Smuzhiyun 	struct iwl_notification_wait wait;
1682*4882a593Smuzhiyun 	struct iwl_he_monitor_cmd he_mon_cmd = {};
1683*4882a593Smuzhiyun 	struct iwl_mvm_sniffer_apply apply = {
1684*4882a593Smuzhiyun 		.mvm = mvm,
1685*4882a593Smuzhiyun 	};
1686*4882a593Smuzhiyun 	u16 wait_cmds[] = {
1687*4882a593Smuzhiyun 		iwl_cmd_id(HE_AIR_SNIFFER_CONFIG_CMD, DATA_PATH_GROUP, 0),
1688*4882a593Smuzhiyun 	};
1689*4882a593Smuzhiyun 	u32 aid;
1690*4882a593Smuzhiyun 	int ret;
1691*4882a593Smuzhiyun 
1692*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1693*4882a593Smuzhiyun 		return -EIO;
1694*4882a593Smuzhiyun 
1695*4882a593Smuzhiyun 	ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid,
1696*4882a593Smuzhiyun 		     &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1],
1697*4882a593Smuzhiyun 		     &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3],
1698*4882a593Smuzhiyun 		     &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]);
1699*4882a593Smuzhiyun 	if (ret != 7)
1700*4882a593Smuzhiyun 		return -EINVAL;
1701*4882a593Smuzhiyun 
1702*4882a593Smuzhiyun 	he_mon_cmd.aid = cpu_to_le16(aid);
1703*4882a593Smuzhiyun 
1704*4882a593Smuzhiyun 	apply.aid = aid;
1705*4882a593Smuzhiyun 	apply.bssid = (void *)he_mon_cmd.bssid;
1706*4882a593Smuzhiyun 
1707*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1708*4882a593Smuzhiyun 
1709*4882a593Smuzhiyun 	/*
1710*4882a593Smuzhiyun 	 * Use the notification waiter to get our function triggered
1711*4882a593Smuzhiyun 	 * in sequence with other RX. This ensures that frames we get
1712*4882a593Smuzhiyun 	 * on the RX queue _before_ the new configuration is applied
1713*4882a593Smuzhiyun 	 * still have mvm->cur_aid pointing to the old AID, and that
1714*4882a593Smuzhiyun 	 * frames on the RX queue _after_ the firmware processed the
1715*4882a593Smuzhiyun 	 * new configuration (and sent the response, synchronously)
1716*4882a593Smuzhiyun 	 * get mvm->cur_aid correctly set to the new AID.
1717*4882a593Smuzhiyun 	 */
1718*4882a593Smuzhiyun 	iwl_init_notification_wait(&mvm->notif_wait, &wait,
1719*4882a593Smuzhiyun 				   wait_cmds, ARRAY_SIZE(wait_cmds),
1720*4882a593Smuzhiyun 				   iwl_mvm_sniffer_apply, &apply);
1721*4882a593Smuzhiyun 
1722*4882a593Smuzhiyun 	ret = iwl_mvm_send_cmd_pdu(mvm, iwl_cmd_id(HE_AIR_SNIFFER_CONFIG_CMD,
1723*4882a593Smuzhiyun 						   DATA_PATH_GROUP, 0), 0,
1724*4882a593Smuzhiyun 				   sizeof(he_mon_cmd), &he_mon_cmd);
1725*4882a593Smuzhiyun 
1726*4882a593Smuzhiyun 	/* no need to really wait, we already did anyway */
1727*4882a593Smuzhiyun 	iwl_remove_notification(&mvm->notif_wait, &wait);
1728*4882a593Smuzhiyun 
1729*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1730*4882a593Smuzhiyun 
1731*4882a593Smuzhiyun 	return ret ?: count;
1732*4882a593Smuzhiyun }
1733*4882a593Smuzhiyun 
1734*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_he_sniffer_params_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1735*4882a593Smuzhiyun iwl_dbgfs_he_sniffer_params_read(struct file *file, char __user *user_buf,
1736*4882a593Smuzhiyun 				 size_t count, loff_t *ppos)
1737*4882a593Smuzhiyun {
1738*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1739*4882a593Smuzhiyun 	u8 buf[32];
1740*4882a593Smuzhiyun 	int len;
1741*4882a593Smuzhiyun 
1742*4882a593Smuzhiyun 	len = scnprintf(buf, sizeof(buf),
1743*4882a593Smuzhiyun 			"%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
1744*4882a593Smuzhiyun 			le16_to_cpu(mvm->cur_aid), mvm->cur_bssid[0],
1745*4882a593Smuzhiyun 			mvm->cur_bssid[1], mvm->cur_bssid[2], mvm->cur_bssid[3],
1746*4882a593Smuzhiyun 			mvm->cur_bssid[4], mvm->cur_bssid[5]);
1747*4882a593Smuzhiyun 
1748*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1749*4882a593Smuzhiyun }
1750*4882a593Smuzhiyun 
1751*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_uapsd_noagg_bssids_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1752*4882a593Smuzhiyun iwl_dbgfs_uapsd_noagg_bssids_read(struct file *file, char __user *user_buf,
1753*4882a593Smuzhiyun 				  size_t count, loff_t *ppos)
1754*4882a593Smuzhiyun {
1755*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1756*4882a593Smuzhiyun 	u8 buf[IWL_MVM_UAPSD_NOAGG_BSSIDS_NUM * ETH_ALEN * 3 + 1];
1757*4882a593Smuzhiyun 	unsigned int pos = 0;
1758*4882a593Smuzhiyun 	size_t bufsz = sizeof(buf);
1759*4882a593Smuzhiyun 	int i;
1760*4882a593Smuzhiyun 
1761*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1762*4882a593Smuzhiyun 
1763*4882a593Smuzhiyun 	for (i = 0; i < IWL_MVM_UAPSD_NOAGG_LIST_LEN; i++)
1764*4882a593Smuzhiyun 		pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
1765*4882a593Smuzhiyun 				 mvm->uapsd_noagg_bssids[i].addr);
1766*4882a593Smuzhiyun 
1767*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1768*4882a593Smuzhiyun 
1769*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
1770*4882a593Smuzhiyun }
1771*4882a593Smuzhiyun 
1772*4882a593Smuzhiyun static ssize_t
iwl_dbgfs_ltr_config_write(struct iwl_mvm * mvm,char * buf,size_t count,loff_t * ppos)1773*4882a593Smuzhiyun iwl_dbgfs_ltr_config_write(struct iwl_mvm *mvm,
1774*4882a593Smuzhiyun 			   char *buf, size_t count, loff_t *ppos)
1775*4882a593Smuzhiyun {
1776*4882a593Smuzhiyun 	int ret;
1777*4882a593Smuzhiyun 	struct iwl_ltr_config_cmd ltr_config = {0};
1778*4882a593Smuzhiyun 
1779*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1780*4882a593Smuzhiyun 		return -EIO;
1781*4882a593Smuzhiyun 
1782*4882a593Smuzhiyun 	if (sscanf(buf, "%x,%x,%x,%x,%x,%x,%x",
1783*4882a593Smuzhiyun 		   &ltr_config.flags,
1784*4882a593Smuzhiyun 		   &ltr_config.static_long,
1785*4882a593Smuzhiyun 		   &ltr_config.static_short,
1786*4882a593Smuzhiyun 		   &ltr_config.ltr_cfg_values[0],
1787*4882a593Smuzhiyun 		   &ltr_config.ltr_cfg_values[1],
1788*4882a593Smuzhiyun 		   &ltr_config.ltr_cfg_values[2],
1789*4882a593Smuzhiyun 		   &ltr_config.ltr_cfg_values[3]) != 7) {
1790*4882a593Smuzhiyun 		return -EINVAL;
1791*4882a593Smuzhiyun 	}
1792*4882a593Smuzhiyun 
1793*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1794*4882a593Smuzhiyun 	ret = iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, sizeof(ltr_config),
1795*4882a593Smuzhiyun 				   &ltr_config);
1796*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1797*4882a593Smuzhiyun 
1798*4882a593Smuzhiyun 	if (ret)
1799*4882a593Smuzhiyun 		IWL_ERR(mvm, "failed to send ltr configuration cmd\n");
1800*4882a593Smuzhiyun 
1801*4882a593Smuzhiyun 	return ret ?: count;
1802*4882a593Smuzhiyun }
1803*4882a593Smuzhiyun 
1804*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
1805*4882a593Smuzhiyun 
1806*4882a593Smuzhiyun /* Device wide debugfs entries */
1807*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(ctdp_budget);
1808*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(stop_ctdp, 8);
1809*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(force_ctkill, 8);
1810*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
1811*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8);
1812*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8);
1813*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64);
1814*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(set_nic_temperature, 64);
1815*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(nic_temp);
1816*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(stations);
1817*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(rs_data);
1818*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(bt_notif);
1819*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(bt_cmd);
1820*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64);
1821*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats);
1822*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);
1823*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(fw_ver);
1824*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
1825*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
1826*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10);
1827*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
1828*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
1829*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
1830*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64);
1831*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl,
1832*4882a593Smuzhiyun 			   (IWL_RSS_INDIRECTION_TABLE_SIZE * 2));
1833*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(inject_packet, 512);
1834*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512);
1835*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512);
1836*4882a593Smuzhiyun 
1837*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(uapsd_noagg_bssids);
1838*4882a593Smuzhiyun 
1839*4882a593Smuzhiyun #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
1840*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
1841*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
1842*4882a593Smuzhiyun #endif
1843*4882a593Smuzhiyun 
1844*4882a593Smuzhiyun #ifdef CONFIG_ACPI
1845*4882a593Smuzhiyun MVM_DEBUGFS_READ_FILE_OPS(sar_geo_profile);
1846*4882a593Smuzhiyun #endif
1847*4882a593Smuzhiyun 
1848*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_STA_FILE_OPS(amsdu_len, 16);
1849*4882a593Smuzhiyun 
1850*4882a593Smuzhiyun MVM_DEBUGFS_READ_WRITE_FILE_OPS(he_sniffer_params, 32);
1851*4882a593Smuzhiyun 
1852*4882a593Smuzhiyun MVM_DEBUGFS_WRITE_FILE_OPS(ltr_config, 512);
1853*4882a593Smuzhiyun 
iwl_dbgfs_mem_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)1854*4882a593Smuzhiyun static ssize_t iwl_dbgfs_mem_read(struct file *file, char __user *user_buf,
1855*4882a593Smuzhiyun 				  size_t count, loff_t *ppos)
1856*4882a593Smuzhiyun {
1857*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1858*4882a593Smuzhiyun 	struct iwl_dbg_mem_access_cmd cmd = {};
1859*4882a593Smuzhiyun 	struct iwl_dbg_mem_access_rsp *rsp;
1860*4882a593Smuzhiyun 	struct iwl_host_cmd hcmd = {
1861*4882a593Smuzhiyun 		.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
1862*4882a593Smuzhiyun 		.data = { &cmd, },
1863*4882a593Smuzhiyun 		.len = { sizeof(cmd) },
1864*4882a593Smuzhiyun 	};
1865*4882a593Smuzhiyun 	size_t delta;
1866*4882a593Smuzhiyun 	ssize_t ret, len;
1867*4882a593Smuzhiyun 
1868*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1869*4882a593Smuzhiyun 		return -EIO;
1870*4882a593Smuzhiyun 
1871*4882a593Smuzhiyun 	hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR,
1872*4882a593Smuzhiyun 			     DEBUG_GROUP, 0);
1873*4882a593Smuzhiyun 	cmd.op = cpu_to_le32(DEBUG_MEM_OP_READ);
1874*4882a593Smuzhiyun 
1875*4882a593Smuzhiyun 	/* Take care of alignment of both the position and the length */
1876*4882a593Smuzhiyun 	delta = *ppos & 0x3;
1877*4882a593Smuzhiyun 	cmd.addr = cpu_to_le32(*ppos - delta);
1878*4882a593Smuzhiyun 	cmd.len = cpu_to_le32(min(ALIGN(count + delta, 4) / 4,
1879*4882a593Smuzhiyun 				  (size_t)DEBUG_MEM_MAX_SIZE_DWORDS));
1880*4882a593Smuzhiyun 
1881*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1882*4882a593Smuzhiyun 	ret = iwl_mvm_send_cmd(mvm, &hcmd);
1883*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1884*4882a593Smuzhiyun 
1885*4882a593Smuzhiyun 	if (ret < 0)
1886*4882a593Smuzhiyun 		return ret;
1887*4882a593Smuzhiyun 
1888*4882a593Smuzhiyun 	rsp = (void *)hcmd.resp_pkt->data;
1889*4882a593Smuzhiyun 	if (le32_to_cpu(rsp->status) != DEBUG_MEM_STATUS_SUCCESS) {
1890*4882a593Smuzhiyun 		ret = -ENXIO;
1891*4882a593Smuzhiyun 		goto out;
1892*4882a593Smuzhiyun 	}
1893*4882a593Smuzhiyun 
1894*4882a593Smuzhiyun 	len = min((size_t)le32_to_cpu(rsp->len) << 2,
1895*4882a593Smuzhiyun 		  iwl_rx_packet_payload_len(hcmd.resp_pkt) - sizeof(*rsp));
1896*4882a593Smuzhiyun 	len = min(len - delta, count);
1897*4882a593Smuzhiyun 	if (len < 0) {
1898*4882a593Smuzhiyun 		ret = -EFAULT;
1899*4882a593Smuzhiyun 		goto out;
1900*4882a593Smuzhiyun 	}
1901*4882a593Smuzhiyun 
1902*4882a593Smuzhiyun 	ret = len - copy_to_user(user_buf, (void *)rsp->data + delta, len);
1903*4882a593Smuzhiyun 	*ppos += ret;
1904*4882a593Smuzhiyun 
1905*4882a593Smuzhiyun out:
1906*4882a593Smuzhiyun 	iwl_free_resp(&hcmd);
1907*4882a593Smuzhiyun 	return ret;
1908*4882a593Smuzhiyun }
1909*4882a593Smuzhiyun 
iwl_dbgfs_mem_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)1910*4882a593Smuzhiyun static ssize_t iwl_dbgfs_mem_write(struct file *file,
1911*4882a593Smuzhiyun 				   const char __user *user_buf, size_t count,
1912*4882a593Smuzhiyun 				   loff_t *ppos)
1913*4882a593Smuzhiyun {
1914*4882a593Smuzhiyun 	struct iwl_mvm *mvm = file->private_data;
1915*4882a593Smuzhiyun 	struct iwl_dbg_mem_access_cmd *cmd;
1916*4882a593Smuzhiyun 	struct iwl_dbg_mem_access_rsp *rsp;
1917*4882a593Smuzhiyun 	struct iwl_host_cmd hcmd = {};
1918*4882a593Smuzhiyun 	size_t cmd_size;
1919*4882a593Smuzhiyun 	size_t data_size;
1920*4882a593Smuzhiyun 	u32 op, len;
1921*4882a593Smuzhiyun 	ssize_t ret;
1922*4882a593Smuzhiyun 
1923*4882a593Smuzhiyun 	if (!iwl_mvm_firmware_running(mvm))
1924*4882a593Smuzhiyun 		return -EIO;
1925*4882a593Smuzhiyun 
1926*4882a593Smuzhiyun 	hcmd.id = iwl_cmd_id(*ppos >> 24 ? UMAC_RD_WR : LMAC_RD_WR,
1927*4882a593Smuzhiyun 			     DEBUG_GROUP, 0);
1928*4882a593Smuzhiyun 
1929*4882a593Smuzhiyun 	if (*ppos & 0x3 || count < 4) {
1930*4882a593Smuzhiyun 		op = DEBUG_MEM_OP_WRITE_BYTES;
1931*4882a593Smuzhiyun 		len = min(count, (size_t)(4 - (*ppos & 0x3)));
1932*4882a593Smuzhiyun 		data_size = len;
1933*4882a593Smuzhiyun 	} else {
1934*4882a593Smuzhiyun 		op = DEBUG_MEM_OP_WRITE;
1935*4882a593Smuzhiyun 		len = min(count >> 2, (size_t)DEBUG_MEM_MAX_SIZE_DWORDS);
1936*4882a593Smuzhiyun 		data_size = len << 2;
1937*4882a593Smuzhiyun 	}
1938*4882a593Smuzhiyun 
1939*4882a593Smuzhiyun 	cmd_size = sizeof(*cmd) + ALIGN(data_size, 4);
1940*4882a593Smuzhiyun 	cmd = kzalloc(cmd_size, GFP_KERNEL);
1941*4882a593Smuzhiyun 	if (!cmd)
1942*4882a593Smuzhiyun 		return -ENOMEM;
1943*4882a593Smuzhiyun 
1944*4882a593Smuzhiyun 	cmd->op = cpu_to_le32(op);
1945*4882a593Smuzhiyun 	cmd->len = cpu_to_le32(len);
1946*4882a593Smuzhiyun 	cmd->addr = cpu_to_le32(*ppos);
1947*4882a593Smuzhiyun 	if (copy_from_user((void *)cmd->data, user_buf, data_size)) {
1948*4882a593Smuzhiyun 		kfree(cmd);
1949*4882a593Smuzhiyun 		return -EFAULT;
1950*4882a593Smuzhiyun 	}
1951*4882a593Smuzhiyun 
1952*4882a593Smuzhiyun 	hcmd.flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
1953*4882a593Smuzhiyun 	hcmd.data[0] = (void *)cmd;
1954*4882a593Smuzhiyun 	hcmd.len[0] = cmd_size;
1955*4882a593Smuzhiyun 
1956*4882a593Smuzhiyun 	mutex_lock(&mvm->mutex);
1957*4882a593Smuzhiyun 	ret = iwl_mvm_send_cmd(mvm, &hcmd);
1958*4882a593Smuzhiyun 	mutex_unlock(&mvm->mutex);
1959*4882a593Smuzhiyun 
1960*4882a593Smuzhiyun 	kfree(cmd);
1961*4882a593Smuzhiyun 
1962*4882a593Smuzhiyun 	if (ret < 0)
1963*4882a593Smuzhiyun 		return ret;
1964*4882a593Smuzhiyun 
1965*4882a593Smuzhiyun 	rsp = (void *)hcmd.resp_pkt->data;
1966*4882a593Smuzhiyun 	if (rsp->status != DEBUG_MEM_STATUS_SUCCESS) {
1967*4882a593Smuzhiyun 		ret = -ENXIO;
1968*4882a593Smuzhiyun 		goto out;
1969*4882a593Smuzhiyun 	}
1970*4882a593Smuzhiyun 
1971*4882a593Smuzhiyun 	ret = data_size;
1972*4882a593Smuzhiyun 	*ppos += ret;
1973*4882a593Smuzhiyun 
1974*4882a593Smuzhiyun out:
1975*4882a593Smuzhiyun 	iwl_free_resp(&hcmd);
1976*4882a593Smuzhiyun 	return ret;
1977*4882a593Smuzhiyun }
1978*4882a593Smuzhiyun 
1979*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_mem_ops = {
1980*4882a593Smuzhiyun 	.read = iwl_dbgfs_mem_read,
1981*4882a593Smuzhiyun 	.write = iwl_dbgfs_mem_write,
1982*4882a593Smuzhiyun 	.open = simple_open,
1983*4882a593Smuzhiyun 	.llseek = default_llseek,
1984*4882a593Smuzhiyun };
1985*4882a593Smuzhiyun 
iwl_mvm_sta_add_debugfs(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct dentry * dir)1986*4882a593Smuzhiyun void iwl_mvm_sta_add_debugfs(struct ieee80211_hw *hw,
1987*4882a593Smuzhiyun 			     struct ieee80211_vif *vif,
1988*4882a593Smuzhiyun 			     struct ieee80211_sta *sta,
1989*4882a593Smuzhiyun 			     struct dentry *dir)
1990*4882a593Smuzhiyun {
1991*4882a593Smuzhiyun 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
1992*4882a593Smuzhiyun 
1993*4882a593Smuzhiyun 	if (iwl_mvm_has_tlc_offload(mvm)) {
1994*4882a593Smuzhiyun 		MVM_DEBUGFS_ADD_STA_FILE(rs_data, dir, 0400);
1995*4882a593Smuzhiyun 	}
1996*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_STA_FILE(amsdu_len, dir, 0600);
1997*4882a593Smuzhiyun }
1998*4882a593Smuzhiyun 
iwl_mvm_dbgfs_register(struct iwl_mvm * mvm,struct dentry * dbgfs_dir)1999*4882a593Smuzhiyun void iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
2000*4882a593Smuzhiyun {
2001*4882a593Smuzhiyun 	struct dentry *bcast_dir __maybe_unused;
2002*4882a593Smuzhiyun 	char buf[100];
2003*4882a593Smuzhiyun 
2004*4882a593Smuzhiyun 	spin_lock_init(&mvm->drv_stats_lock);
2005*4882a593Smuzhiyun 
2006*4882a593Smuzhiyun 	mvm->debugfs_dir = dbgfs_dir;
2007*4882a593Smuzhiyun 
2008*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, 0200);
2009*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, 0200);
2010*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, 0600);
2011*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir, 0600);
2012*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, 0400);
2013*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(ctdp_budget, dbgfs_dir, 0400);
2014*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(stop_ctdp, dbgfs_dir, 0200);
2015*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(force_ctkill, dbgfs_dir, 0200);
2016*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, 0400);
2017*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, 0400);
2018*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, 0400);
2019*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, 0600);
2020*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(fw_ver, mvm->debugfs_dir, 0400);
2021*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, 0400);
2022*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(drv_rx_stats, mvm->debugfs_dir, 0400);
2023*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, 0200);
2024*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, 0200);
2025*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, 0200);
2026*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, 0200);
2027*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, 0600);
2028*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, 0600);
2029*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, 0600);
2030*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, 0200);
2031*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, 0200);
2032*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, 0200);
2033*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(inject_packet, mvm->debugfs_dir, 0200);
2034*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(inject_beacon_ie, mvm->debugfs_dir, 0200);
2035*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(inject_beacon_ie_restore, mvm->debugfs_dir, 0200);
2036*4882a593Smuzhiyun #ifdef CONFIG_ACPI
2037*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(sar_geo_profile, dbgfs_dir, 0400);
2038*4882a593Smuzhiyun #endif
2039*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(he_sniffer_params, mvm->debugfs_dir, 0600);
2040*4882a593Smuzhiyun 
2041*4882a593Smuzhiyun 	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_LTR_GEN2))
2042*4882a593Smuzhiyun 		MVM_DEBUGFS_ADD_FILE(ltr_config, mvm->debugfs_dir, 0200);
2043*4882a593Smuzhiyun 
2044*4882a593Smuzhiyun 	debugfs_create_bool("enable_scan_iteration_notif", 0600,
2045*4882a593Smuzhiyun 			    mvm->debugfs_dir, &mvm->scan_iter_notif_enabled);
2046*4882a593Smuzhiyun 	debugfs_create_bool("drop_bcn_ap_mode", 0600, mvm->debugfs_dir,
2047*4882a593Smuzhiyun 			    &mvm->drop_bcn_ap_mode);
2048*4882a593Smuzhiyun 
2049*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(uapsd_noagg_bssids, mvm->debugfs_dir, S_IRUSR);
2050*4882a593Smuzhiyun 
2051*4882a593Smuzhiyun #ifdef CONFIG_IWLWIFI_BCAST_FILTERING
2052*4882a593Smuzhiyun 	if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
2053*4882a593Smuzhiyun 		bcast_dir = debugfs_create_dir("bcast_filtering",
2054*4882a593Smuzhiyun 					       mvm->debugfs_dir);
2055*4882a593Smuzhiyun 
2056*4882a593Smuzhiyun 		debugfs_create_bool("override", 0600, bcast_dir,
2057*4882a593Smuzhiyun 				    &mvm->dbgfs_bcast_filtering.override);
2058*4882a593Smuzhiyun 
2059*4882a593Smuzhiyun 		MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
2060*4882a593Smuzhiyun 					   bcast_dir, 0600);
2061*4882a593Smuzhiyun 		MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
2062*4882a593Smuzhiyun 					   bcast_dir, 0600);
2063*4882a593Smuzhiyun 	}
2064*4882a593Smuzhiyun #endif
2065*4882a593Smuzhiyun 
2066*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
2067*4882a593Smuzhiyun 	MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, 0400);
2068*4882a593Smuzhiyun 	debugfs_create_bool("d3_wake_sysassert", 0600, mvm->debugfs_dir,
2069*4882a593Smuzhiyun 			    &mvm->d3_wake_sysassert);
2070*4882a593Smuzhiyun 	debugfs_create_u32("last_netdetect_scans", 0400, mvm->debugfs_dir,
2071*4882a593Smuzhiyun 			   &mvm->last_netdetect_scans);
2072*4882a593Smuzhiyun #endif
2073*4882a593Smuzhiyun 
2074*4882a593Smuzhiyun 	debugfs_create_u8("ps_disabled", 0400, mvm->debugfs_dir,
2075*4882a593Smuzhiyun 			  &mvm->ps_disabled);
2076*4882a593Smuzhiyun 	debugfs_create_blob("nvm_hw", 0400, mvm->debugfs_dir,
2077*4882a593Smuzhiyun 			    &mvm->nvm_hw_blob);
2078*4882a593Smuzhiyun 	debugfs_create_blob("nvm_sw", 0400, mvm->debugfs_dir,
2079*4882a593Smuzhiyun 			    &mvm->nvm_sw_blob);
2080*4882a593Smuzhiyun 	debugfs_create_blob("nvm_calib", 0400, mvm->debugfs_dir,
2081*4882a593Smuzhiyun 			    &mvm->nvm_calib_blob);
2082*4882a593Smuzhiyun 	debugfs_create_blob("nvm_prod", 0400, mvm->debugfs_dir,
2083*4882a593Smuzhiyun 			    &mvm->nvm_prod_blob);
2084*4882a593Smuzhiyun 	debugfs_create_blob("nvm_phy_sku", 0400, mvm->debugfs_dir,
2085*4882a593Smuzhiyun 			    &mvm->nvm_phy_sku_blob);
2086*4882a593Smuzhiyun 	debugfs_create_blob("nvm_reg", S_IRUSR,
2087*4882a593Smuzhiyun 			    mvm->debugfs_dir, &mvm->nvm_reg_blob);
2088*4882a593Smuzhiyun 
2089*4882a593Smuzhiyun 	debugfs_create_file("mem", 0600, dbgfs_dir, mvm, &iwl_dbgfs_mem_ops);
2090*4882a593Smuzhiyun 
2091*4882a593Smuzhiyun 	/*
2092*4882a593Smuzhiyun 	 * Create a symlink with mac80211. It will be removed when mac80211
2093*4882a593Smuzhiyun 	 * exists (before the opmode exists which removes the target.)
2094*4882a593Smuzhiyun 	 */
2095*4882a593Smuzhiyun 	snprintf(buf, 100, "../../%pd2", dbgfs_dir->d_parent);
2096*4882a593Smuzhiyun 	debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf);
2097*4882a593Smuzhiyun }
2098