3e120dd47305728342ae1442b517894cd5cd90d8
[muen/linux.git] / drivers / net / wireless / intel / iwlwifi / fw / debugfs.c
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         if (!debugfs_create_file(alias, mode, parent, fwrt,             \
177                                  &iwl_dbgfs_##name##_ops))              \
178                 goto err;                                               \
179         } while (0)
180 #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \
181         FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
182
183 static int iwl_fw_send_timestamp_marker_cmd(struct iwl_fw_runtime *fwrt)
184 {
185         struct iwl_mvm_marker marker = {
186                 .dw_len = sizeof(struct iwl_mvm_marker) / 4,
187                 .marker_id = MARKER_ID_SYNC_CLOCK,
188
189                 /* the real timestamp is taken from the ftrace clock
190                  * this is for finding the match between fw and kernel logs
191                  */
192                 .timestamp = cpu_to_le64(fwrt->timestamp.seq++),
193         };
194
195         struct iwl_host_cmd hcmd = {
196                 .id = MARKER_CMD,
197                 .flags = CMD_ASYNC,
198                 .data[0] = &marker,
199                 .len[0] = sizeof(marker),
200         };
201
202         return iwl_trans_send_cmd(fwrt->trans, &hcmd);
203 }
204
205 static void iwl_fw_timestamp_marker_wk(struct work_struct *work)
206 {
207         int ret;
208         struct iwl_fw_runtime *fwrt =
209                 container_of(work, struct iwl_fw_runtime, timestamp.wk.work);
210         unsigned long delay = fwrt->timestamp.delay;
211
212         ret = iwl_fw_send_timestamp_marker_cmd(fwrt);
213         if (!ret && delay)
214                 schedule_delayed_work(&fwrt->timestamp.wk,
215                                       round_jiffies_relative(delay));
216         else
217                 IWL_INFO(fwrt,
218                          "stopping timestamp_marker, ret: %d, delay: %u\n",
219                          ret, jiffies_to_msecs(delay) / 1000);
220 }
221
222 void iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay)
223 {
224         IWL_INFO(fwrt,
225                  "starting timestamp_marker trigger with delay: %us\n",
226                  delay);
227
228         iwl_fw_cancel_timestamp(fwrt);
229
230         fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000);
231
232         schedule_delayed_work(&fwrt->timestamp.wk,
233                               round_jiffies_relative(fwrt->timestamp.delay));
234 }
235
236 static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
237                                                 char *buf, size_t count)
238 {
239         int ret;
240         u32 delay;
241
242         ret = kstrtou32(buf, 10, &delay);
243         if (ret < 0)
244                 return ret;
245
246         iwl_fw_trigger_timestamp(fwrt, delay);
247
248         return count;
249 }
250
251 static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt,
252                                                size_t size, char *buf)
253 {
254         u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000;
255
256         return scnprintf(buf, size, "%d\n", delay_secs);
257 }
258
259 FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16);
260
261 struct hcmd_write_data {
262         __be32 cmd_id;
263         __be32 flags;
264         __be16 length;
265         u8 data[0];
266 } __packed;
267
268 static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
269                                          size_t count)
270 {
271         size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2;
272         size_t data_size = (count - 1) / 2;
273         int ret;
274         struct hcmd_write_data *data;
275         struct iwl_host_cmd hcmd = {
276                 .len = { 0, },
277                 .data = { NULL, },
278         };
279
280         if (fwrt->ops && fwrt->ops->fw_running &&
281             !fwrt->ops->fw_running(fwrt->ops_ctx))
282                 return -EIO;
283
284         if (count < header_size + 1 || count > 1024 * 4)
285                 return -EINVAL;
286
287         data = kmalloc(data_size, GFP_KERNEL);
288         if (!data)
289                 return -ENOMEM;
290
291         ret = hex2bin((u8 *)data, buf, data_size);
292         if (ret)
293                 goto out;
294
295         hcmd.id = be32_to_cpu(data->cmd_id);
296         hcmd.flags = be32_to_cpu(data->flags);
297         hcmd.len[0] = be16_to_cpu(data->length);
298         hcmd.data[0] = data->data;
299
300         if (count != header_size + hcmd.len[0] * 2 + 1) {
301                 IWL_ERR(fwrt,
302                         "host command data size does not match header length\n");
303                 ret = -EINVAL;
304                 goto out;
305         }
306
307         if (fwrt->ops && fwrt->ops->send_hcmd)
308                 ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
309         else
310                 ret = -EPERM;
311
312         if (ret < 0)
313                 goto out;
314
315         if (hcmd.flags & CMD_WANT_SKB)
316                 iwl_free_resp(&hcmd);
317 out:
318         kfree(data);
319         return ret ?: count;
320 }
321
322 FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512);
323
324 int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
325                             struct dentry *dbgfs_dir)
326 {
327         INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
328         FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
329         FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200);
330         return 0;
331 err:
332         IWL_ERR(fwrt, "Can't create the fwrt debugfs directory\n");
333         return -ENOMEM;
334 }