1 /******************************************************************************
2  *
3  * This file is provided under a dual BSD/GPLv2 license.  When using or
4  * redistributing this file, you may do so under either license.
5  *
6  * GPL LICENSE SUMMARY
7  *
8  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
9  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
10  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
11  * Copyright (C) 2018 Intel Corporation
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of version 2 of the GNU General Public License as
15  * published by the Free Software Foundation.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * The full GNU General Public License is included in this distribution
23  * in the file called COPYING.
24  *
25  * Contact Information:
26  *  Intel Linux Wireless <linuxwifi@intel.com>
27  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
28  *
29  * BSD LICENSE
30  *
31  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
32  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
33  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
34  * Copyright (C) 2018 Intel Corporation
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  *
41  *  * Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  *  * Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in
45  *    the documentation and/or other materials provided with the
46  *    distribution.
47  *  * Neither the name Intel Corporation nor the names of its
48  *    contributors may be used to endorse or promote products derived
49  *    from this software without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
52  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
53  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
54  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
55  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
56  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
57  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
58  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
59  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
61  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  *
63  *****************************************************************************/
64 #include "api/commands.h"
65 #include "debugfs.h"
66 #include "dbg.h"
67 
68 #define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)		\
69 struct dbgfs_##name##_data {						\
70 	argtype *arg;							\
71 	bool read_done;							\
72 	ssize_t rlen;							\
73 	char rbuf[buflen];						\
74 };									\
75 static int _iwl_dbgfs_##name##_open(struct inode *inode,		\
76 				    struct file *file)			\
77 {									\
78 	struct dbgfs_##name##_data *data;				\
79 									\
80 	data = kzalloc(sizeof(*data), GFP_KERNEL);			\
81 	if (!data)							\
82 		return -ENOMEM;						\
83 									\
84 	data->read_done = false;					\
85 	data->arg = inode->i_private;					\
86 	file->private_data = data;					\
87 									\
88 	return 0;							\
89 }
90 
91 #define FWRT_DEBUGFS_READ_WRAPPER(name)					\
92 static ssize_t _iwl_dbgfs_##name##_read(struct file *file,		\
93 					char __user *user_buf,		\
94 					size_t count, loff_t *ppos)	\
95 {									\
96 	struct dbgfs_##name##_data *data = file->private_data;		\
97 									\
98 	if (!data->read_done) {						\
99 		data->read_done = true;					\
100 		data->rlen = iwl_dbgfs_##name##_read(data->arg,		\
101 						     sizeof(data->rbuf),\
102 						     data->rbuf);	\
103 	}								\
104 									\
105 	if (data->rlen < 0)						\
106 		return data->rlen;					\
107 	return simple_read_from_buffer(user_buf, count, ppos,		\
108 				       data->rbuf, data->rlen);		\
109 }
110 
111 static int _iwl_dbgfs_release(struct inode *inode, struct file *file)
112 {
113 	kfree(file->private_data);
114 
115 	return 0;
116 }
117 
118 #define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype)		\
119 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)			\
120 FWRT_DEBUGFS_READ_WRAPPER(name)						\
121 static const struct file_operations iwl_dbgfs_##name##_ops = {		\
122 	.read = _iwl_dbgfs_##name##_read,				\
123 	.open = _iwl_dbgfs_##name##_open,				\
124 	.llseek = generic_file_llseek,					\
125 	.release = _iwl_dbgfs_release,					\
126 }
127 
128 #define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)		\
129 static ssize_t _iwl_dbgfs_##name##_write(struct file *file,		\
130 					 const char __user *user_buf,	\
131 					 size_t count, loff_t *ppos)	\
132 {									\
133 	argtype *arg =							\
134 		((struct dbgfs_##name##_data *)file->private_data)->arg;\
135 	char buf[buflen] = {};						\
136 	size_t buf_size = min(count, sizeof(buf) -  1);			\
137 									\
138 	if (copy_from_user(buf, user_buf, buf_size))			\
139 		return -EFAULT;						\
140 									\
141 	return iwl_dbgfs_##name##_write(arg, buf, buf_size);		\
142 }
143 
144 #define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype)	\
145 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)			\
146 FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)			\
147 FWRT_DEBUGFS_READ_WRAPPER(name)						\
148 static const struct file_operations iwl_dbgfs_##name##_ops = {		\
149 	.write = _iwl_dbgfs_##name##_write,				\
150 	.read = _iwl_dbgfs_##name##_read,				\
151 	.open = _iwl_dbgfs_##name##_open,				\
152 	.llseek = generic_file_llseek,					\
153 	.release = _iwl_dbgfs_release,					\
154 }
155 
156 #define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype)		\
157 FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype)			\
158 FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype)			\
159 static const struct file_operations iwl_dbgfs_##name##_ops = {		\
160 	.write = _iwl_dbgfs_##name##_write,				\
161 	.open = _iwl_dbgfs_##name##_open,				\
162 	.llseek = generic_file_llseek,					\
163 	.release = _iwl_dbgfs_release,					\
164 }
165 
166 #define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz)				\
167 	_FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
168 
169 #define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz)			\
170 	_FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
171 
172 #define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz)			\
173 	_FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
174 
175 #define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do {	\
176 	debugfs_create_file(alias, mode, parent, fwrt,			\
177 			    &iwl_dbgfs_##name##_ops);			\
178 	} while (0)
179 #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \
180 	FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
181 
182 static int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt)
183 {
184 	struct iwl_mvm_marker marker = {
185 		.dw_len = sizeof(struct iwl_mvm_marker) / 4,
186 		.marker_id = MARKER_ID_SYNC_CLOCK,
187 
188 		/* the real timestamp is taken from the ftrace clock
189 		 * this is for finding the match between fw and kernel logs
190 		 */
191 		.timestamp = cpu_to_le64(fwrt->timestamp.seq++),
192 	};
193 
194 	struct iwl_host_cmd hcmd = {
195 		.id = MARKER_CMD,
196 		.flags = CMD_ASYNC,
197 		.data[0] = &marker,
198 		.len[0] = sizeof(marker),
199 	};
200 
201 	return iwl_trans_send_cmd(fwrt->trans, &hcmd);
202 }
203 
204 static void iwl_fw_timestamp_marker_wk(struct work_struct *work)
205 {
206 	int ret;
207 	struct iwl_fw_runtime *fwrt =
208 		container_of(work, struct iwl_fw_runtime, timestamp.wk.work);
209 	unsigned long delay = fwrt->timestamp.delay;
210 
211 	ret = iwl_fw_send_timestamp_marker_cmd(fwrt);
212 	if (!ret && delay)
213 		schedule_delayed_work(&fwrt->timestamp.wk,
214 				      round_jiffies_relative(delay));
215 	else
216 		IWL_INFO(fwrt,
217 			 "stopping timestamp_marker, ret: %d, delay: %u\n",
218 			 ret, jiffies_to_msecs(delay) / 1000);
219 }
220 
221 void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
222 {
223 	IWL_INFO(fwrt,
224 		 "starting timestamp_marker trigger with delay: %us\n",
225 		 delay);
226 
227 	iwl_fw_cancel_timestamp(fwrt);
228 
229 	fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
230 
231 	schedule_delayed_work(&fwrt->timestamp.wk,
232 			      round_jiffies_relative(fwrt->timestamp.delay));
233 }
234 
235 static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
236 						char *buf, size_t count)
237 {
238 	int ret;
239 	u32 delay;
240 
241 	ret = kstrtou32(buf, 10, &delay);
242 	if (ret < 0)
243 		return ret;
244 
245 	iwl_fw_trigger_timestamp(fwrt, delay);
246 
247 	return count;
248 }
249 
250 static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt,
251 					       size_t size, char *buf)
252 {
253 	u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000;
254 
255 	return scnprintf(buf, size, "%d\n", delay_secs);
256 }
257 
258 FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16);
259 
260 struct hcmd_write_data {
261 	__be32 cmd_id;
262 	__be32 flags;
263 	__be16 length;
264 	u8 data[0];
265 } __packed;
266 
267 static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
268 					 size_t count)
269 {
270 	size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2;
271 	size_t data_size = (count - 1) / 2;
272 	int ret;
273 	struct hcmd_write_data *data;
274 	struct iwl_host_cmd hcmd = {
275 		.len = { 0, },
276 		.data = { NULL, },
277 	};
278 
279 	if (fwrt->ops && fwrt->ops->fw_running &&
280 	    !fwrt->ops->fw_running(fwrt->ops_ctx))
281 		return -EIO;
282 
283 	if (count < header_size + 1 || count > 1024 * 4)
284 		return -EINVAL;
285 
286 	data = kmalloc(data_size, GFP_KERNEL);
287 	if (!data)
288 		return -ENOMEM;
289 
290 	ret = hex2bin((u8 *)data, buf, data_size);
291 	if (ret)
292 		goto out;
293 
294 	hcmd.id = be32_to_cpu(data->cmd_id);
295 	hcmd.flags = be32_to_cpu(data->flags);
296 	hcmd.len[0] = be16_to_cpu(data->length);
297 	hcmd.data[0] = data->data;
298 
299 	if (count != header_size + hcmd.len[0] * 2 + 1) {
300 		IWL_ERR(fwrt,
301 			"host command data size does not match header length\n");
302 		ret = -EINVAL;
303 		goto out;
304 	}
305 
306 	if (fwrt->ops && fwrt->ops->send_hcmd)
307 		ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
308 	else
309 		ret = -EPERM;
310 
311 	if (ret < 0)
312 		goto out;
313 
314 	if (hcmd.flags & CMD_WANT_SKB)
315 		iwl_free_resp(&hcmd);
316 out:
317 	kfree(data);
318 	return ret ?: count;
319 }
320 
321 FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512);
322 
323 void iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
324 			    struct dentry *dbgfs_dir)
325 {
326 	INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
327 	FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
328 	FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200);
329 }
330