hc
2024-05-10 23fa18eaa71266feff7ba8d83022d9e1cc83c65a
kernel/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c
....@@ -5,9 +5,9 @@
55 *
66 * GPL LICENSE SUMMARY
77 *
8
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
98 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
109 * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
10
+ * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
1111 *
1212 * This program is free software; you can redistribute it and/or modify
1313 * it under the terms of version 2 of the GNU General Public License as
....@@ -18,9 +18,6 @@
1818 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1919 * General Public License for more details.
2020 *
21
- * You should have received a copy of the GNU General Public License
22
- * along with this program.
23
- *
2421 * The full GNU General Public License is included in this distribution
2522 * in the file called COPYING.
2623 *
....@@ -30,9 +27,9 @@
3027 *
3128 * BSD LICENSE
3229 *
33
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
3430 * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
3531 * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
32
+ * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation
3633 * All rights reserved.
3734 *
3835 * Redistribution and use in source and binary forms, with or without
....@@ -65,56 +62,118 @@
6562 #include "api/commands.h"
6663 #include "debugfs.h"
6764 #include "dbg.h"
65
+#include <linux/seq_file.h>
6866
69
-#define FWRT_DEBUGFS_READ_FILE_OPS(name) \
70
-static ssize_t iwl_dbgfs_##name##_read(struct iwl_fw_runtime *fwrt, \
71
- char *buf, size_t count, \
72
- loff_t *ppos); \
73
-static const struct file_operations iwl_dbgfs_##name##_ops = { \
74
- .read = iwl_dbgfs_##name##_read, \
75
- .open = simple_open, \
76
- .llseek = generic_file_llseek, \
67
+#define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
68
+struct dbgfs_##name##_data { \
69
+ argtype *arg; \
70
+ bool read_done; \
71
+ ssize_t rlen; \
72
+ char rbuf[buflen]; \
73
+}; \
74
+static int _iwl_dbgfs_##name##_open(struct inode *inode, \
75
+ struct file *file) \
76
+{ \
77
+ struct dbgfs_##name##_data *data; \
78
+ \
79
+ data = kzalloc(sizeof(*data), GFP_KERNEL); \
80
+ if (!data) \
81
+ return -ENOMEM; \
82
+ \
83
+ data->read_done = false; \
84
+ data->arg = inode->i_private; \
85
+ file->private_data = data; \
86
+ \
87
+ return 0; \
7788 }
7889
79
-#define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen) \
80
-static ssize_t iwl_dbgfs_##name##_write(struct iwl_fw_runtime *fwrt, \
81
- char *buf, size_t count, \
82
- loff_t *ppos); \
90
+#define FWRT_DEBUGFS_READ_WRAPPER(name) \
91
+static ssize_t _iwl_dbgfs_##name##_read(struct file *file, \
92
+ char __user *user_buf, \
93
+ size_t count, loff_t *ppos) \
94
+{ \
95
+ struct dbgfs_##name##_data *data = file->private_data; \
96
+ \
97
+ if (!data->read_done) { \
98
+ data->read_done = true; \
99
+ data->rlen = iwl_dbgfs_##name##_read(data->arg, \
100
+ sizeof(data->rbuf),\
101
+ data->rbuf); \
102
+ } \
103
+ \
104
+ if (data->rlen < 0) \
105
+ return data->rlen; \
106
+ return simple_read_from_buffer(user_buf, count, ppos, \
107
+ data->rbuf, data->rlen); \
108
+}
109
+
110
+static int _iwl_dbgfs_release(struct inode *inode, struct file *file)
111
+{
112
+ kfree(file->private_data);
113
+
114
+ return 0;
115
+}
116
+
117
+#define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \
118
+FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
119
+FWRT_DEBUGFS_READ_WRAPPER(name) \
120
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
121
+ .read = _iwl_dbgfs_##name##_read, \
122
+ .open = _iwl_dbgfs_##name##_open, \
123
+ .llseek = generic_file_llseek, \
124
+ .release = _iwl_dbgfs_release, \
125
+}
126
+
127
+#define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
83128 static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \
84129 const char __user *user_buf, \
85130 size_t count, loff_t *ppos) \
86131 { \
87
- struct iwl_fw_runtime *fwrt = file->private_data; \
132
+ argtype *arg = \
133
+ ((struct dbgfs_##name##_data *)file->private_data)->arg;\
88134 char buf[buflen] = {}; \
89135 size_t buf_size = min(count, sizeof(buf) - 1); \
90136 \
91137 if (copy_from_user(buf, user_buf, buf_size)) \
92138 return -EFAULT; \
93139 \
94
- return iwl_dbgfs_##name##_write(fwrt, buf, buf_size, ppos); \
140
+ return iwl_dbgfs_##name##_write(arg, buf, buf_size); \
95141 }
96142
97
-#define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen) \
98
-FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen) \
143
+#define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \
144
+FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
145
+FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
146
+FWRT_DEBUGFS_READ_WRAPPER(name) \
99147 static const struct file_operations iwl_dbgfs_##name##_ops = { \
100148 .write = _iwl_dbgfs_##name##_write, \
101
- .read = iwl_dbgfs_##name##_read, \
102
- .open = simple_open, \
149
+ .read = _iwl_dbgfs_##name##_read, \
150
+ .open = _iwl_dbgfs_##name##_open, \
103151 .llseek = generic_file_llseek, \
152
+ .release = _iwl_dbgfs_release, \
104153 }
105154
106
-#define FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen) \
107
-FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen) \
155
+#define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \
156
+FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \
157
+FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \
108158 static const struct file_operations iwl_dbgfs_##name##_ops = { \
109159 .write = _iwl_dbgfs_##name##_write, \
110
- .open = simple_open, \
160
+ .open = _iwl_dbgfs_##name##_open, \
111161 .llseek = generic_file_llseek, \
162
+ .release = _iwl_dbgfs_release, \
112163 }
164
+
165
+#define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz) \
166
+ _FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
167
+
168
+#define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
169
+ _FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
170
+
171
+#define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
172
+ _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime)
113173
114174 #define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \
115
- if (!debugfs_create_file(alias, mode, parent, fwrt, \
116
- &iwl_dbgfs_##name##_ops)) \
117
- goto err; \
175
+ debugfs_create_file(alias, mode, parent, fwrt, \
176
+ &iwl_dbgfs_##name##_ops); \
118177 } while (0)
119178 #define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \
120179 FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
....@@ -173,8 +232,7 @@
173232 }
174233
175234 static ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt,
176
- char *buf, size_t count,
177
- loff_t *ppos)
235
+ char *buf, size_t count)
178236 {
179237 int ret;
180238 u32 delay;
....@@ -188,15 +246,192 @@
188246 return count;
189247 }
190248
191
-FWRT_DEBUGFS_WRITE_FILE_OPS(timestamp_marker, 10);
249
+static ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt,
250
+ size_t size, char *buf)
251
+{
252
+ u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000;
192253
193
-int iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
254
+ return scnprintf(buf, size, "%d\n", delay_secs);
255
+}
256
+
257
+FWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16);
258
+
259
+struct hcmd_write_data {
260
+ __be32 cmd_id;
261
+ __be32 flags;
262
+ __be16 length;
263
+ u8 data[];
264
+} __packed;
265
+
266
+static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
267
+ size_t count)
268
+{
269
+ size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2;
270
+ size_t data_size = (count - 1) / 2;
271
+ int ret;
272
+ struct hcmd_write_data *data;
273
+ struct iwl_host_cmd hcmd = {
274
+ .len = { 0, },
275
+ .data = { NULL, },
276
+ };
277
+
278
+ if (fwrt->ops && fwrt->ops->fw_running &&
279
+ !fwrt->ops->fw_running(fwrt->ops_ctx))
280
+ return -EIO;
281
+
282
+ if (count < header_size + 1 || count > 1024 * 4)
283
+ return -EINVAL;
284
+
285
+ data = kmalloc(data_size, GFP_KERNEL);
286
+ if (!data)
287
+ return -ENOMEM;
288
+
289
+ ret = hex2bin((u8 *)data, buf, data_size);
290
+ if (ret)
291
+ goto out;
292
+
293
+ hcmd.id = be32_to_cpu(data->cmd_id);
294
+ hcmd.flags = be32_to_cpu(data->flags);
295
+ hcmd.len[0] = be16_to_cpu(data->length);
296
+ hcmd.data[0] = data->data;
297
+
298
+ if (count != header_size + hcmd.len[0] * 2 + 1) {
299
+ IWL_ERR(fwrt,
300
+ "host command data size does not match header length\n");
301
+ ret = -EINVAL;
302
+ goto out;
303
+ }
304
+
305
+ if (fwrt->ops && fwrt->ops->send_hcmd)
306
+ ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
307
+ else
308
+ ret = -EPERM;
309
+
310
+ if (ret < 0)
311
+ goto out;
312
+
313
+ if (hcmd.flags & CMD_WANT_SKB)
314
+ iwl_free_resp(&hcmd);
315
+out:
316
+ kfree(data);
317
+ return ret ?: count;
318
+}
319
+
320
+FWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512);
321
+
322
+static ssize_t iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime *fwrt,
323
+ size_t size, char *buf)
324
+{
325
+ return scnprintf(buf, size, "0x%08x\n",
326
+ fwrt->trans->dbg.domains_bitmap);
327
+}
328
+
329
+FWRT_DEBUGFS_READ_FILE_OPS(fw_dbg_domain, 20);
330
+
331
+struct iwl_dbgfs_fw_info_priv {
332
+ struct iwl_fw_runtime *fwrt;
333
+};
334
+
335
+struct iwl_dbgfs_fw_info_state {
336
+ loff_t pos;
337
+};
338
+
339
+static void *iwl_dbgfs_fw_info_seq_next(struct seq_file *seq,
340
+ void *v, loff_t *pos)
341
+{
342
+ struct iwl_dbgfs_fw_info_state *state = v;
343
+ struct iwl_dbgfs_fw_info_priv *priv = seq->private;
344
+ const struct iwl_fw *fw = priv->fwrt->fw;
345
+
346
+ *pos = ++state->pos;
347
+ if (*pos >= fw->ucode_capa.n_cmd_versions) {
348
+ kfree(state);
349
+ return NULL;
350
+ }
351
+
352
+ return state;
353
+}
354
+
355
+static void iwl_dbgfs_fw_info_seq_stop(struct seq_file *seq,
356
+ void *v)
357
+{
358
+ kfree(v);
359
+}
360
+
361
+static void *iwl_dbgfs_fw_info_seq_start(struct seq_file *seq, loff_t *pos)
362
+{
363
+ struct iwl_dbgfs_fw_info_priv *priv = seq->private;
364
+ const struct iwl_fw *fw = priv->fwrt->fw;
365
+ struct iwl_dbgfs_fw_info_state *state;
366
+
367
+ if (*pos >= fw->ucode_capa.n_cmd_versions)
368
+ return NULL;
369
+
370
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
371
+ if (!state)
372
+ return NULL;
373
+ state->pos = *pos;
374
+ return state;
375
+};
376
+
377
+static int iwl_dbgfs_fw_info_seq_show(struct seq_file *seq, void *v)
378
+{
379
+ struct iwl_dbgfs_fw_info_state *state = v;
380
+ struct iwl_dbgfs_fw_info_priv *priv = seq->private;
381
+ const struct iwl_fw *fw = priv->fwrt->fw;
382
+ const struct iwl_fw_cmd_version *ver;
383
+ u32 cmd_id;
384
+
385
+ if (!state->pos)
386
+ seq_puts(seq, "fw_api_ver:\n");
387
+
388
+ ver = &fw->ucode_capa.cmd_versions[state->pos];
389
+
390
+ cmd_id = iwl_cmd_id(ver->cmd, ver->group, 0);
391
+
392
+ seq_printf(seq, " 0x%04x:\n", cmd_id);
393
+ seq_printf(seq, " name: %s\n",
394
+ iwl_get_cmd_string(priv->fwrt->trans, cmd_id));
395
+ seq_printf(seq, " cmd_ver: %d\n", ver->cmd_ver);
396
+ seq_printf(seq, " notif_ver: %d\n", ver->notif_ver);
397
+ return 0;
398
+}
399
+
400
+static const struct seq_operations iwl_dbgfs_info_seq_ops = {
401
+ .start = iwl_dbgfs_fw_info_seq_start,
402
+ .next = iwl_dbgfs_fw_info_seq_next,
403
+ .stop = iwl_dbgfs_fw_info_seq_stop,
404
+ .show = iwl_dbgfs_fw_info_seq_show,
405
+};
406
+
407
+static int iwl_dbgfs_fw_info_open(struct inode *inode, struct file *filp)
408
+{
409
+ struct iwl_dbgfs_fw_info_priv *priv;
410
+
411
+ priv = __seq_open_private(filp, &iwl_dbgfs_info_seq_ops,
412
+ sizeof(*priv));
413
+
414
+ if (!priv)
415
+ return -ENOMEM;
416
+
417
+ priv->fwrt = inode->i_private;
418
+ return 0;
419
+}
420
+
421
+static const struct file_operations iwl_dbgfs_fw_info_ops = {
422
+ .owner = THIS_MODULE,
423
+ .open = iwl_dbgfs_fw_info_open,
424
+ .read = seq_read,
425
+ .llseek = seq_lseek,
426
+ .release = seq_release_private,
427
+};
428
+
429
+void iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt,
194430 struct dentry *dbgfs_dir)
195431 {
196432 INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk);
197433 FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200);
198
- return 0;
199
-err:
200
- IWL_ERR(fwrt, "Can't create the fwrt debugfs directory\n");
201
- return -ENOMEM;
434
+ FWRT_DEBUGFS_ADD_FILE(fw_info, dbgfs_dir, 0200);
435
+ FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200);
436
+ FWRT_DEBUGFS_ADD_FILE(fw_dbg_domain, dbgfs_dir, 0400);
202437 }