xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/intel/iwlwifi/fw/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 "api/commands.h"
63*4882a593Smuzhiyun #include "debugfs.h"
64*4882a593Smuzhiyun #include "dbg.h"
65*4882a593Smuzhiyun #include <linux/seq_file.h>
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun #define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)		\
68*4882a593Smuzhiyun struct dbgfs_##name##_data {						\
69*4882a593Smuzhiyun 	argtype *arg;							\
70*4882a593Smuzhiyun 	bool read_done;							\
71*4882a593Smuzhiyun 	ssize_t rlen;							\
72*4882a593Smuzhiyun 	char rbuf[buflen];						\
73*4882a593Smuzhiyun };									\
74*4882a593Smuzhiyun static int _iwl_dbgfs_##name##_open(struct inode *inode,		\
75*4882a593Smuzhiyun 				    struct file *file)			\
76*4882a593Smuzhiyun {									\
77*4882a593Smuzhiyun 	struct dbgfs_##name##_data *data;				\
78*4882a593Smuzhiyun 									\
79*4882a593Smuzhiyun 	data = kzalloc(sizeof(*data), GFP_KERNEL);			\
80*4882a593Smuzhiyun 	if (!data)							\
81*4882a593Smuzhiyun 		return -ENOMEM;						\
82*4882a593Smuzhiyun 									\
83*4882a593Smuzhiyun 	data->read_done = false;					\
84*4882a593Smuzhiyun 	data->arg = inode->i_private;					\
85*4882a593Smuzhiyun 	file->private_data = data;					\
86*4882a593Smuzhiyun 									\
87*4882a593Smuzhiyun 	return 0;							\
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun #define FWRT_DEBUGFS_READ_WRAPPER(name)					\
91*4882a593Smuzhiyun static ssize_t _iwl_dbgfs_##name##_read(struct file *file,		\
92*4882a593Smuzhiyun 					char __user *user_buf,		\
93*4882a593Smuzhiyun 					size_t count, loff_t *ppos)	\
94*4882a593Smuzhiyun {									\
95*4882a593Smuzhiyun 	struct dbgfs_##name##_data *data = file->private_data;		\
96*4882a593Smuzhiyun 									\
97*4882a593Smuzhiyun 	if (!data->read_done) {						\
98*4882a593Smuzhiyun 		data->read_done = true;					\
99*4882a593Smuzhiyun 		data->rlen = iwl_dbgfs_##name##_read(data->arg,		\
100*4882a593Smuzhiyun 						     sizeof(data->rbuf),\
101*4882a593Smuzhiyun 						     data->rbuf);	\
102*4882a593Smuzhiyun 	}								\
103*4882a593Smuzhiyun 									\
104*4882a593Smuzhiyun 	if (data->rlen < 0)						\
105*4882a593Smuzhiyun 		return data->rlen;					\
106*4882a593Smuzhiyun 	return simple_read_from_buffer(user_buf, count, ppos,		\
107*4882a593Smuzhiyun 				       data->rbuf, data->rlen);		\
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
_iwl_dbgfs_release(struct inode * inode,struct file * file)110*4882a593Smuzhiyun static int _iwl_dbgfs_release(struct inode *inode, struct file *file)
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun 	kfree(file->private_data);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	return 0;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun #define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype)		\
118*4882a593Smuzhiyun FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)			\
119*4882a593Smuzhiyun FWRT_DEBUGFS_READ_WRAPPER(name)						\
120*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_##name##_ops = {		\
121*4882a593Smuzhiyun 	.read = _iwl_dbgfs_##name##_read,				\
122*4882a593Smuzhiyun 	.open = _iwl_dbgfs_##name##_open,				\
123*4882a593Smuzhiyun 	.llseek = generic_file_llseek,					\
124*4882a593Smuzhiyun 	.release = _iwl_dbgfs_release,					\
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun #define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)		\
128*4882a593Smuzhiyun static ssize_t _iwl_dbgfs_##name##_write(struct file *file,		\
129*4882a593Smuzhiyun 					 const char __user *user_buf,	\
130*4882a593Smuzhiyun 					 size_t count, loff_t *ppos)	\
131*4882a593Smuzhiyun {									\
132*4882a593Smuzhiyun 	argtype *arg =							\
133*4882a593Smuzhiyun 		((struct dbgfs_##name##_data *)file->private_data)->arg;\
134*4882a593Smuzhiyun 	char buf[buflen] = {};						\
135*4882a593Smuzhiyun 	size_t buf_size = min(count, sizeof(buf) -  1);			\
136*4882a593Smuzhiyun 									\
137*4882a593Smuzhiyun 	if (copy_from_user(buf, user_buf, buf_size))			\
138*4882a593Smuzhiyun 		return -EFAULT;						\
139*4882a593Smuzhiyun 									\
140*4882a593Smuzhiyun 	return iwl_dbgfs_##name##_write(arg, buf, buf_size);		\
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun #define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype)	\
144*4882a593Smuzhiyun FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)			\
145*4882a593Smuzhiyun FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)			\
146*4882a593Smuzhiyun FWRT_DEBUGFS_READ_WRAPPER(name)						\
147*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_##name##_ops = {		\
148*4882a593Smuzhiyun 	.write = _iwl_dbgfs_##name##_write,				\
149*4882a593Smuzhiyun 	.read = _iwl_dbgfs_##name##_read,				\
150*4882a593Smuzhiyun 	.open = _iwl_dbgfs_##name##_open,				\
151*4882a593Smuzhiyun 	.llseek = generic_file_llseek,					\
152*4882a593Smuzhiyun 	.release = _iwl_dbgfs_release,					\
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun #define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype)		\
156*4882a593Smuzhiyun FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)			\
157*4882a593Smuzhiyun FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)			\
158*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_##name##_ops = {		\
159*4882a593Smuzhiyun 	.write = _iwl_dbgfs_##name##_write,				\
160*4882a593Smuzhiyun 	.open = _iwl_dbgfs_##name##_open,				\
161*4882a593Smuzhiyun 	.llseek = generic_file_llseek,					\
162*4882a593Smuzhiyun 	.release = _iwl_dbgfs_release,					\
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun #define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz)				\
166*4882a593Smuzhiyun 	_FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun #define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz)			\
169*4882a593Smuzhiyun 	_FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun #define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz)			\
172*4882a593Smuzhiyun 	_FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun #define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do {	\
175*4882a593Smuzhiyun 	debugfs_create_file(alias, mode, parent, fwrt,			\
176*4882a593Smuzhiyun 			    &iwl_dbgfs_##name##_ops);			\
177*4882a593Smuzhiyun 	} while (0)
178*4882a593Smuzhiyun #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \
179*4882a593Smuzhiyun 	FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
180*4882a593Smuzhiyun 
iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime * fwrt)181*4882a593Smuzhiyun static int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	struct iwl_mvm_marker marker = {
184*4882a593Smuzhiyun 		.dw_len = sizeof(struct iwl_mvm_marker) / 4,
185*4882a593Smuzhiyun 		.marker_id = MARKER_ID_SYNC_CLOCK,
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 		/* the real timestamp is taken from the ftrace clock
188*4882a593Smuzhiyun 		 * this is for finding the match between fw and kernel logs
189*4882a593Smuzhiyun 		 */
190*4882a593Smuzhiyun 		.timestamp = cpu_to_le64(fwrt->timestamp.seq++),
191*4882a593Smuzhiyun 	};
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	struct iwl_host_cmd hcmd = {
194*4882a593Smuzhiyun 		.id = MARKER_CMD,
195*4882a593Smuzhiyun 		.flags = CMD_ASYNC,
196*4882a593Smuzhiyun 		.data[0] = &marker,
197*4882a593Smuzhiyun 		.len[0] = sizeof(marker),
198*4882a593Smuzhiyun 	};
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	return iwl_trans_send_cmd(fwrt->trans, &hcmd);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun 
iwl_fw_timestamp_marker_wk(struct work_struct * work)203*4882a593Smuzhiyun static void iwl_fw_timestamp_marker_wk(struct work_struct *work)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	int ret;
206*4882a593Smuzhiyun 	struct iwl_fw_runtime *fwrt =
207*4882a593Smuzhiyun 		container_of(work, struct iwl_fw_runtime, timestamp.wk.work);
208*4882a593Smuzhiyun 	unsigned long delay = fwrt->timestamp.delay;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	ret = iwl_fw_send_timestamp_marker_cmd(fwrt);
211*4882a593Smuzhiyun 	if (!ret && delay)
212*4882a593Smuzhiyun 		schedule_delayed_work(&fwrt->timestamp.wk,
213*4882a593Smuzhiyun 				      round_jiffies_relative(delay));
214*4882a593Smuzhiyun 	else
215*4882a593Smuzhiyun 		IWL_INFO(fwrt,
216*4882a593Smuzhiyun 			 "stopping timestamp_marker, ret: %d, delay: %u\n",
217*4882a593Smuzhiyun 			 ret, jiffies_to_msecs(delay) / 1000);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
iwl_fw_trigger_timestamp(struct iwl_fw_runtime * fwrt,u32 delay)220*4882a593Smuzhiyun void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	IWL_INFO(fwrt,
223*4882a593Smuzhiyun 		 "starting timestamp_marker trigger with delay: %us\n",
224*4882a593Smuzhiyun 		 delay);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	iwl_fw_cancel_timestamp(fwrt);
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	schedule_delayed_work(&fwrt->timestamp.wk,
231*4882a593Smuzhiyun 			      round_jiffies_relative(fwrt->timestamp.delay));
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun 
iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime * fwrt,char * buf,size_t count)234*4882a593Smuzhiyun static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
235*4882a593Smuzhiyun 						char *buf, size_t count)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	int ret;
238*4882a593Smuzhiyun 	u32 delay;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	ret = kstrtou32(buf, 10, &delay);
241*4882a593Smuzhiyun 	if (ret < 0)
242*4882a593Smuzhiyun 		return ret;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	iwl_fw_trigger_timestamp(fwrt, delay);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	return count;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun 
iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime * fwrt,size_t size,char * buf)249*4882a593Smuzhiyun static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt,
250*4882a593Smuzhiyun 					       size_t size, char *buf)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun 	u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	return scnprintf(buf, size, "%d\n", delay_secs);
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16);
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun struct hcmd_write_data {
260*4882a593Smuzhiyun 	__be32 cmd_id;
261*4882a593Smuzhiyun 	__be32 flags;
262*4882a593Smuzhiyun 	__be16 length;
263*4882a593Smuzhiyun 	u8 data[];
264*4882a593Smuzhiyun } __packed;
265*4882a593Smuzhiyun 
iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime * fwrt,char * buf,size_t count)266*4882a593Smuzhiyun static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
267*4882a593Smuzhiyun 					 size_t count)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2;
270*4882a593Smuzhiyun 	size_t data_size = (count - 1) / 2;
271*4882a593Smuzhiyun 	int ret;
272*4882a593Smuzhiyun 	struct hcmd_write_data *data;
273*4882a593Smuzhiyun 	struct iwl_host_cmd hcmd = {
274*4882a593Smuzhiyun 		.len = { 0, },
275*4882a593Smuzhiyun 		.data = { NULL, },
276*4882a593Smuzhiyun 	};
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	if (fwrt->ops && fwrt->ops->fw_running &&
279*4882a593Smuzhiyun 	    !fwrt->ops->fw_running(fwrt->ops_ctx))
280*4882a593Smuzhiyun 		return -EIO;
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	if (count < header_size + 1 || count > 1024 * 4)
283*4882a593Smuzhiyun 		return -EINVAL;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	data = kmalloc(data_size, GFP_KERNEL);
286*4882a593Smuzhiyun 	if (!data)
287*4882a593Smuzhiyun 		return -ENOMEM;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	ret = hex2bin((u8 *)data, buf, data_size);
290*4882a593Smuzhiyun 	if (ret)
291*4882a593Smuzhiyun 		goto out;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	hcmd.id = be32_to_cpu(data->cmd_id);
294*4882a593Smuzhiyun 	hcmd.flags = be32_to_cpu(data->flags);
295*4882a593Smuzhiyun 	hcmd.len[0] = be16_to_cpu(data->length);
296*4882a593Smuzhiyun 	hcmd.data[0] = data->data;
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	if (count != header_size + hcmd.len[0] * 2 + 1) {
299*4882a593Smuzhiyun 		IWL_ERR(fwrt,
300*4882a593Smuzhiyun 			"host command data size does not match header length\n");
301*4882a593Smuzhiyun 		ret = -EINVAL;
302*4882a593Smuzhiyun 		goto out;
303*4882a593Smuzhiyun 	}
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	if (fwrt->ops && fwrt->ops->send_hcmd)
306*4882a593Smuzhiyun 		ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
307*4882a593Smuzhiyun 	else
308*4882a593Smuzhiyun 		ret = -EPERM;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (ret < 0)
311*4882a593Smuzhiyun 		goto out;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	if (hcmd.flags & CMD_WANT_SKB)
314*4882a593Smuzhiyun 		iwl_free_resp(&hcmd);
315*4882a593Smuzhiyun out:
316*4882a593Smuzhiyun 	kfree(data);
317*4882a593Smuzhiyun 	return ret ?: count;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512);
321*4882a593Smuzhiyun 
iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime * fwrt,size_t size,char * buf)322*4882a593Smuzhiyun static ssize_t iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime *fwrt,
323*4882a593Smuzhiyun 					    size_t size, char *buf)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	return scnprintf(buf, size, "0x%08x\n",
326*4882a593Smuzhiyun 			 fwrt->trans->dbg.domains_bitmap);
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun FWRT_DEBUGFS_READ_FILE_OPS(fw_dbg_domain, 20);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_priv {
332*4882a593Smuzhiyun 	struct iwl_fw_runtime *fwrt;
333*4882a593Smuzhiyun };
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun struct iwl_dbgfs_fw_info_state {
336*4882a593Smuzhiyun 	loff_t pos;
337*4882a593Smuzhiyun };
338*4882a593Smuzhiyun 
iwl_dbgfs_fw_info_seq_next(struct seq_file * seq,void * v,loff_t * pos)339*4882a593Smuzhiyun static void *iwl_dbgfs_fw_info_seq_next(struct seq_file *seq,
340*4882a593Smuzhiyun 					void *v, loff_t *pos)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun 	struct iwl_dbgfs_fw_info_state *state = v;
343*4882a593Smuzhiyun 	struct iwl_dbgfs_fw_info_priv *priv = seq->private;
344*4882a593Smuzhiyun 	const struct iwl_fw *fw = priv->fwrt->fw;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	*pos = ++state->pos;
347*4882a593Smuzhiyun 	if (*pos >= fw->ucode_capa.n_cmd_versions)
348*4882a593Smuzhiyun 		return NULL;
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	return state;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun 
iwl_dbgfs_fw_info_seq_stop(struct seq_file * seq,void * v)353*4882a593Smuzhiyun static void iwl_dbgfs_fw_info_seq_stop(struct seq_file *seq,
354*4882a593Smuzhiyun 				       void *v)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	kfree(v);
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun 
iwl_dbgfs_fw_info_seq_start(struct seq_file * seq,loff_t * pos)359*4882a593Smuzhiyun static void *iwl_dbgfs_fw_info_seq_start(struct seq_file *seq, loff_t *pos)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	struct iwl_dbgfs_fw_info_priv *priv = seq->private;
362*4882a593Smuzhiyun 	const struct iwl_fw *fw = priv->fwrt->fw;
363*4882a593Smuzhiyun 	struct iwl_dbgfs_fw_info_state *state;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	if (*pos >= fw->ucode_capa.n_cmd_versions)
366*4882a593Smuzhiyun 		return NULL;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	state = kzalloc(sizeof(*state), GFP_KERNEL);
369*4882a593Smuzhiyun 	if (!state)
370*4882a593Smuzhiyun 		return NULL;
371*4882a593Smuzhiyun 	state->pos = *pos;
372*4882a593Smuzhiyun 	return state;
373*4882a593Smuzhiyun };
374*4882a593Smuzhiyun 
iwl_dbgfs_fw_info_seq_show(struct seq_file * seq,void * v)375*4882a593Smuzhiyun static int iwl_dbgfs_fw_info_seq_show(struct seq_file *seq, void *v)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun 	struct iwl_dbgfs_fw_info_state *state = v;
378*4882a593Smuzhiyun 	struct iwl_dbgfs_fw_info_priv *priv = seq->private;
379*4882a593Smuzhiyun 	const struct iwl_fw *fw = priv->fwrt->fw;
380*4882a593Smuzhiyun 	const struct iwl_fw_cmd_version *ver;
381*4882a593Smuzhiyun 	u32 cmd_id;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	if (!state->pos)
384*4882a593Smuzhiyun 		seq_puts(seq, "fw_api_ver:\n");
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	ver = &fw->ucode_capa.cmd_versions[state->pos];
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	cmd_id = iwl_cmd_id(ver->cmd, ver->group, 0);
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	seq_printf(seq, "  0x%04x:\n", cmd_id);
391*4882a593Smuzhiyun 	seq_printf(seq, "    name: %s\n",
392*4882a593Smuzhiyun 		   iwl_get_cmd_string(priv->fwrt->trans, cmd_id));
393*4882a593Smuzhiyun 	seq_printf(seq, "    cmd_ver: %d\n", ver->cmd_ver);
394*4882a593Smuzhiyun 	seq_printf(seq, "    notif_ver: %d\n", ver->notif_ver);
395*4882a593Smuzhiyun 	return 0;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun static const struct seq_operations iwl_dbgfs_info_seq_ops = {
399*4882a593Smuzhiyun 	.start = iwl_dbgfs_fw_info_seq_start,
400*4882a593Smuzhiyun 	.next = iwl_dbgfs_fw_info_seq_next,
401*4882a593Smuzhiyun 	.stop = iwl_dbgfs_fw_info_seq_stop,
402*4882a593Smuzhiyun 	.show = iwl_dbgfs_fw_info_seq_show,
403*4882a593Smuzhiyun };
404*4882a593Smuzhiyun 
iwl_dbgfs_fw_info_open(struct inode * inode,struct file * filp)405*4882a593Smuzhiyun static int iwl_dbgfs_fw_info_open(struct inode *inode, struct file *filp)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	struct iwl_dbgfs_fw_info_priv *priv;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	priv = __seq_open_private(filp, &iwl_dbgfs_info_seq_ops,
410*4882a593Smuzhiyun 				  sizeof(*priv));
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	if (!priv)
413*4882a593Smuzhiyun 		return -ENOMEM;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	priv->fwrt = inode->i_private;
416*4882a593Smuzhiyun 	return 0;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun static const struct file_operations iwl_dbgfs_fw_info_ops = {
420*4882a593Smuzhiyun 	.owner = THIS_MODULE,
421*4882a593Smuzhiyun 	.open = iwl_dbgfs_fw_info_open,
422*4882a593Smuzhiyun 	.read = seq_read,
423*4882a593Smuzhiyun 	.llseek = seq_lseek,
424*4882a593Smuzhiyun 	.release = seq_release_private,
425*4882a593Smuzhiyun };
426*4882a593Smuzhiyun 
iwl_fwrt_dbgfs_register(struct iwl_fw_runtime * fwrt,struct dentry * dbgfs_dir)427*4882a593Smuzhiyun void iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
428*4882a593Smuzhiyun 			    struct dentry *dbgfs_dir)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun 	INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
431*4882a593Smuzhiyun 	FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
432*4882a593Smuzhiyun 	FWRT_DEBUGFS_ADD_FILE(fw_info, dbgfs_dir, 0200);
433*4882a593Smuzhiyun 	FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200);
434*4882a593Smuzhiyun 	FWRT_DEBUGFS_ADD_FILE(fw_dbg_domain, dbgfs_dir, 0400);
435*4882a593Smuzhiyun }
436