Linux-libre 5.3.12-gnu
[librecmc/linux-libre.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         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 }