2 ******************************************************************************
4 * @file ecrnx_fw_dump.c
6 * @brief Definition of debug fs entries to process fw dump
8 * Copyright (C) ESWIN 2015-2020
10 ******************************************************************************
14 #include <linux/kmod.h>
15 #include <linux/debugfs.h>
17 #include "ecrnx_defs.h"
18 #include "ecrnx_debugfs.h"
20 static ssize_t ecrnx_dbgfs_rhd_read(struct file *file,
21 char __user *user_buf,
22 size_t count, loff_t *ppos)
24 struct ecrnx_hw *priv = file->private_data;
25 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
28 mutex_lock(&priv->dbgdump_elem.mutex);
29 if (!priv->debugfs.trace_prst) {
30 mutex_unlock(&priv->dbgdump_elem.mutex);
34 read = simple_read_from_buffer(user_buf, count, ppos,
36 dump->dbg_info.rhd_len);
38 mutex_unlock(&priv->dbgdump_elem.mutex);
42 DEBUGFS_READ_FILE_OPS(rhd);
44 static ssize_t ecrnx_dbgfs_rbd_read(struct file *file,
45 char __user *user_buf,
46 size_t count, loff_t *ppos)
48 struct ecrnx_hw *priv = file->private_data;
49 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
52 mutex_lock(&priv->dbgdump_elem.mutex);
53 if (!priv->debugfs.trace_prst) {
54 mutex_unlock(&priv->dbgdump_elem.mutex);
58 read = simple_read_from_buffer(user_buf, count, ppos,
60 dump->dbg_info.rbd_len);
62 mutex_unlock(&priv->dbgdump_elem.mutex);
66 DEBUGFS_READ_FILE_OPS(rbd);
68 static ssize_t ecrnx_dbgfs_thdx_read(struct file *file, char __user *user_buf,
69 size_t count, loff_t *ppos, int idx)
71 struct ecrnx_hw *priv = file->private_data;
72 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
75 mutex_lock(&priv->dbgdump_elem.mutex);
76 if (!priv->debugfs.trace_prst) {
77 mutex_unlock(&priv->dbgdump_elem.mutex);
81 read = simple_read_from_buffer(user_buf, count, ppos,
83 dump->dbg_info.thd_len[idx]);
85 mutex_unlock(&priv->dbgdump_elem.mutex);
89 static ssize_t ecrnx_dbgfs_thd0_read(struct file *file,
90 char __user *user_buf,
91 size_t count, loff_t *ppos)
93 return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 0);
95 DEBUGFS_READ_FILE_OPS(thd0);
97 static ssize_t ecrnx_dbgfs_thd1_read(struct file *file,
98 char __user *user_buf,
99 size_t count, loff_t *ppos)
101 return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 1);
103 DEBUGFS_READ_FILE_OPS(thd1);
105 static ssize_t ecrnx_dbgfs_thd2_read(struct file *file,
106 char __user *user_buf,
107 size_t count, loff_t *ppos)
109 return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 2);
111 DEBUGFS_READ_FILE_OPS(thd2);
113 static ssize_t ecrnx_dbgfs_thd3_read(struct file *file,
114 char __user *user_buf,
115 size_t count, loff_t *ppos)
117 return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 3);
119 DEBUGFS_READ_FILE_OPS(thd3);
121 #if (NX_TXQ_CNT == 5)
122 static ssize_t ecrnx_dbgfs_thd4_read(struct file *file,
123 char __user *user_buf,
124 size_t count, loff_t *ppos)
126 return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 4);
128 DEBUGFS_READ_FILE_OPS(thd4);
131 static ssize_t ecrnx_dbgfs_mactrace_read(struct file *file,
132 char __user *user_buf,
133 size_t count, loff_t *ppos)
135 struct ecrnx_hw *priv = file->private_data;
136 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
139 mutex_lock(&priv->dbgdump_elem.mutex);
140 if (!priv->debugfs.trace_prst) {
142 mutex_unlock(&priv->dbgdump_elem.mutex);
143 scnprintf(msg, sizeof(msg), "Force trigger\n");
144 ecrnx_dbgfs_trigger_fw_dump(priv, msg);
149 read = simple_read_from_buffer(user_buf, count, ppos,
151 dump->dbg_info.la_conf.trace_len);
153 mutex_unlock(&priv->dbgdump_elem.mutex);
157 DEBUGFS_READ_FILE_OPS(mactrace);
159 static ssize_t ecrnx_dbgfs_macdiags_read(struct file *file,
160 char __user *user_buf,
161 size_t count, loff_t *ppos)
163 struct ecrnx_hw *priv = file->private_data;
164 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
167 mutex_lock(&priv->dbgdump_elem.mutex);
168 if (!priv->debugfs.trace_prst) {
169 mutex_unlock(&priv->dbgdump_elem.mutex);
173 read = simple_read_from_buffer(user_buf, count, ppos,
174 dump->dbg_info.diags_mac,
175 DBG_DIAGS_MAC_MAX * 2);
177 mutex_unlock(&priv->dbgdump_elem.mutex);
181 DEBUGFS_READ_FILE_OPS(macdiags);
183 static ssize_t ecrnx_dbgfs_phydiags_read(struct file *file,
184 char __user *user_buf,
185 size_t count, loff_t *ppos)
187 struct ecrnx_hw *priv = file->private_data;
188 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
191 mutex_lock(&priv->dbgdump_elem.mutex);
192 if (!priv->debugfs.trace_prst) {
193 mutex_unlock(&priv->dbgdump_elem.mutex);
197 read = simple_read_from_buffer(user_buf, count, ppos,
198 dump->dbg_info.diags_phy,
199 DBG_DIAGS_PHY_MAX * 2);
201 mutex_unlock(&priv->dbgdump_elem.mutex);
205 DEBUGFS_READ_FILE_OPS(phydiags);
207 static ssize_t ecrnx_dbgfs_hwdiags_read(struct file *file,
208 char __user *user_buf,
209 size_t count, loff_t *ppos)
211 struct ecrnx_hw *priv = file->private_data;
212 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
216 mutex_lock(&priv->dbgdump_elem.mutex);
217 if (!priv->debugfs.trace_prst) {
218 mutex_unlock(&priv->dbgdump_elem.mutex);
222 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
223 "%08X\n", dump->dbg_info.hw_diag);
225 mutex_unlock(&priv->dbgdump_elem.mutex);
226 return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
229 DEBUGFS_READ_FILE_OPS(hwdiags);
231 static ssize_t ecrnx_dbgfs_plfdiags_read(struct file *file,
232 char __user *user_buf,
233 size_t count, loff_t *ppos)
235 struct ecrnx_hw *priv = file->private_data;
236 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
240 mutex_lock(&priv->dbgdump_elem.mutex);
241 if (!priv->debugfs.trace_prst) {
242 mutex_unlock(&priv->dbgdump_elem.mutex);
246 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
247 "%08X\n", dump->dbg_info.la_conf.diag_conf);
249 mutex_unlock(&priv->dbgdump_elem.mutex);
250 return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
253 DEBUGFS_READ_FILE_OPS(plfdiags);
255 static ssize_t ecrnx_dbgfs_swdiags_read(struct file *file,
256 char __user *user_buf,
257 size_t count, loff_t *ppos)
259 struct ecrnx_hw *priv = file->private_data;
260 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
263 mutex_lock(&priv->dbgdump_elem.mutex);
264 if (!priv->debugfs.trace_prst) {
265 mutex_unlock(&priv->dbgdump_elem.mutex);
269 read = simple_read_from_buffer(user_buf, count, ppos,
270 &dump->dbg_info.sw_diag,
271 dump->dbg_info.sw_diag_len);
273 mutex_unlock(&priv->dbgdump_elem.mutex);
277 DEBUGFS_READ_FILE_OPS(swdiags);
279 static ssize_t ecrnx_dbgfs_error_read(struct file *file,
280 char __user *user_buf,
281 size_t count, loff_t *ppos)
283 struct ecrnx_hw *priv = file->private_data;
284 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
287 mutex_lock(&priv->dbgdump_elem.mutex);
288 if (!priv->debugfs.trace_prst) {
289 mutex_unlock(&priv->dbgdump_elem.mutex);
293 read = simple_read_from_buffer(user_buf, count, ppos,
294 dump->dbg_info.error,
295 strlen((char *)dump->dbg_info.error));
297 mutex_unlock(&priv->dbgdump_elem.mutex);
301 DEBUGFS_READ_FILE_OPS(error);
303 static ssize_t ecrnx_dbgfs_rxdesc_read(struct file *file,
304 char __user *user_buf,
305 size_t count, loff_t *ppos)
307 struct ecrnx_hw *priv = file->private_data;
308 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
313 mutex_lock(&priv->dbgdump_elem.mutex);
314 if (!priv->debugfs.trace_prst) {
315 mutex_unlock(&priv->dbgdump_elem.mutex);
319 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
320 "%08X\n%08X\n", dump->dbg_info.rhd,
322 read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
324 mutex_unlock(&priv->dbgdump_elem.mutex);
328 DEBUGFS_READ_FILE_OPS(rxdesc);
330 static ssize_t ecrnx_dbgfs_txdesc_read(struct file *file,
331 char __user *user_buf,
332 size_t count, loff_t *ppos)
334 struct ecrnx_hw *priv = file->private_data;
335 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
340 mutex_lock(&priv->dbgdump_elem.mutex);
341 if (!priv->debugfs.trace_prst) {
342 mutex_unlock(&priv->dbgdump_elem.mutex);
346 for (i = 0; i < NX_TXQ_CNT; i++) {
347 len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
348 "%08X\n", dump->dbg_info.thd[i]);
351 mutex_unlock(&priv->dbgdump_elem.mutex);
352 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
355 DEBUGFS_READ_FILE_OPS(txdesc);
357 static ssize_t ecrnx_dbgfs_macrxptr_read(struct file *file,
358 char __user *user_buf,
359 size_t count, loff_t *ppos)
361 struct ecrnx_hw *priv = file->private_data;
362 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
365 mutex_lock(&priv->dbgdump_elem.mutex);
366 if (!priv->debugfs.trace_prst) {
367 mutex_unlock(&priv->dbgdump_elem.mutex);
371 read = simple_read_from_buffer(user_buf, count, ppos,
372 &dump->dbg_info.rhd_hw_ptr,
373 2 * sizeof(dump->dbg_info.rhd_hw_ptr));
375 mutex_unlock(&priv->dbgdump_elem.mutex);
379 DEBUGFS_READ_FILE_OPS(macrxptr);
381 static ssize_t ecrnx_dbgfs_lamacconf_read(struct file *file,
382 char __user *user_buf,
383 size_t count, loff_t *ppos)
385 struct ecrnx_hw *priv = file->private_data;
386 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
389 mutex_lock(&priv->dbgdump_elem.mutex);
390 if (!priv->debugfs.trace_prst) {
391 mutex_unlock(&priv->dbgdump_elem.mutex);
395 read = simple_read_from_buffer(user_buf, count, ppos,
396 dump->dbg_info.la_conf.conf,
399 mutex_unlock(&priv->dbgdump_elem.mutex);
402 DEBUGFS_READ_FILE_OPS(lamacconf);
404 static ssize_t ecrnx_dbgfs_chaninfo_read(struct file *file,
405 char __user *user_buf,
406 size_t count, loff_t *ppos)
408 struct ecrnx_hw *priv = file->private_data;
409 struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
413 mutex_lock(&priv->dbgdump_elem.mutex);
414 if (!priv->debugfs.trace_prst) {
415 mutex_unlock(&priv->dbgdump_elem.mutex);
419 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
421 "prim20_freq: %d MHz\n"
422 "center1_freq: %d MHz\n"
423 "center2_freq: %d MHz\n",
424 (dump->dbg_info.chan_info.info1 >> 8) & 0xFF,
425 (dump->dbg_info.chan_info.info1 >> 16) & 0xFFFF,
426 (dump->dbg_info.chan_info.info2 >> 0) & 0xFFFF,
427 (dump->dbg_info.chan_info.info2 >> 16) & 0xFFFF);
429 mutex_unlock(&priv->dbgdump_elem.mutex);
430 return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
433 DEBUGFS_READ_FILE_OPS(chaninfo);
435 static ssize_t ecrnx_dbgfs_um_helper_read(struct file *file,
436 char __user *user_buf,
437 size_t count, loff_t *ppos)
439 struct ecrnx_hw *priv = file->private_data;
440 char buf[sizeof(priv->debugfs.helper_cmd)];
443 ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
444 "%s", priv->debugfs.helper_cmd);
446 return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
449 static ssize_t ecrnx_dbgfs_um_helper_write(struct file *file,
450 const char __user *user_buf,
451 size_t count, loff_t *ppos)
453 struct ecrnx_hw *priv = file->private_data;
454 int eobuf = min_t(size_t, sizeof(priv->debugfs.helper_cmd) - 1, count);
456 priv->debugfs.helper_cmd[eobuf] = '\0';
457 if (copy_from_user(priv->debugfs.helper_cmd, user_buf, eobuf))
463 DEBUGFS_READ_WRITE_FILE_OPS(um_helper);
466 * Calls a userspace pgm
468 int ecrnx_um_helper(struct ecrnx_debugfs *ecrnx_debugfs, const char *cmd)
470 struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
472 char *envp[] = { "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
476 if (!ecrnx_debugfs->dir ||
477 !strlen((cmd = cmd ? cmd : ecrnx_debugfs->helper_cmd)))
479 argv = argv_split(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL, cmd, &argc);
481 return PTR_ERR(argv);
483 if ((ret = call_usermodehelper(argv[0], argv, envp,
484 UMH_WAIT_PROC | UMH_KILLABLE)))
485 dev_err(ecrnx_hw->dev, "Failed to call %s (%s returned %d)\n",
492 static void ecrnx_um_helper_work(struct work_struct *ws)
494 struct ecrnx_debugfs *ecrnx_debugfs = container_of(ws, struct ecrnx_debugfs,
496 struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
498 ecrnx_um_helper(ecrnx_debugfs, NULL);
499 if (!ecrnx_debugfs->unregistering)
500 ecrnx_umh_done(ecrnx_hw);
501 ecrnx_debugfs->helper_scheduled = false;
504 int ecrnx_trigger_um_helper(struct ecrnx_debugfs *ecrnx_debugfs)
506 struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
509 if (ecrnx_debugfs->helper_scheduled == true) {
510 dev_err(ecrnx_hw->dev, "%s: Already scheduled\n", __func__);
514 spin_lock_bh(&ecrnx_debugfs->umh_lock);
515 if (ecrnx_debugfs->unregistering) {
516 spin_unlock_bh(&ecrnx_debugfs->umh_lock);
517 dev_err(ecrnx_hw->dev, "%s: unregistering\n", __func__);
520 ecrnx_debugfs->helper_scheduled = true;
521 schedule_work(&ecrnx_debugfs->helper_work);
522 spin_unlock_bh(&ecrnx_debugfs->umh_lock);
526 void ecrnx_wait_um_helper(struct ecrnx_hw *ecrnx_hw)
528 flush_work(&ecrnx_hw->debugfs.helper_work);
531 int ecrnx_dbgfs_register_fw_dump(struct ecrnx_hw *ecrnx_hw,
532 struct dentry *dir_drv,
533 struct dentry *dir_diags)
536 struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
538 BUILD_BUG_ON(sizeof(CONFIG_ECRNX_UM_HELPER_DFLT) >=
539 sizeof(ecrnx_debugfs->helper_cmd));
540 strncpy(ecrnx_debugfs->helper_cmd,
541 CONFIG_ECRNX_UM_HELPER_DFLT, sizeof(ecrnx_debugfs->helper_cmd));
542 INIT_WORK(&ecrnx_debugfs->helper_work, ecrnx_um_helper_work);
543 DEBUGFS_ADD_FILE(um_helper, dir_drv, S_IWUSR | S_IRUSR);
545 ecrnx_debugfs->trace_prst = ecrnx_debugfs->helper_scheduled = false;
546 spin_lock_init(&ecrnx_debugfs->umh_lock);
547 DEBUGFS_ADD_FILE(rhd, dir_diags, S_IRUSR);
548 DEBUGFS_ADD_FILE(rbd, dir_diags, S_IRUSR);
549 DEBUGFS_ADD_FILE(thd0, dir_diags, S_IRUSR);
550 DEBUGFS_ADD_FILE(thd1, dir_diags, S_IRUSR);
551 DEBUGFS_ADD_FILE(thd2, dir_diags, S_IRUSR);
552 DEBUGFS_ADD_FILE(thd3, dir_diags, S_IRUSR);
553 #if (NX_TXQ_CNT == 5)
554 DEBUGFS_ADD_FILE(thd4, dir_diags, S_IRUSR);
556 DEBUGFS_ADD_FILE(mactrace, dir_diags, S_IRUSR);
557 DEBUGFS_ADD_FILE(macdiags, dir_diags, S_IRUSR);
558 DEBUGFS_ADD_FILE(phydiags, dir_diags, S_IRUSR);
559 DEBUGFS_ADD_FILE(plfdiags, dir_diags, S_IRUSR);
560 DEBUGFS_ADD_FILE(hwdiags, dir_diags, S_IRUSR);
561 DEBUGFS_ADD_FILE(swdiags, dir_diags, S_IRUSR);
562 DEBUGFS_ADD_FILE(error, dir_diags, S_IRUSR);
563 DEBUGFS_ADD_FILE(rxdesc, dir_diags, S_IRUSR);
564 DEBUGFS_ADD_FILE(txdesc, dir_diags, S_IRUSR);
565 DEBUGFS_ADD_FILE(macrxptr, dir_diags, S_IRUSR);
566 DEBUGFS_ADD_FILE(lamacconf, dir_diags, S_IRUSR);
567 DEBUGFS_ADD_FILE(chaninfo, dir_diags, S_IRUSR);