Search Linux Wireless

[RFC v1 055/256] cl8k: add debugfs.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Viktor Barna <viktor.barna@xxxxxxxxxx>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx>
---
 drivers/net/wireless/celeno/cl8k/debugfs.c | 957 +++++++++++++++++++++
 1 file changed, 957 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/debugfs.c

diff --git a/drivers/net/wireless/celeno/cl8k/debugfs.c b/drivers/net/wireless/celeno/cl8k/debugfs.c
new file mode 100644
index 000000000000..79854dfd85ea
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/debugfs.c
@@ -0,0 +1,957 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/list.h>
+
+#include "debugfs.h"
+#include "chip.h"
+#include "fw/msg_tx.h"
+#include "fw/msg_cfm.h"
+#include "tx/tx.h"
+#include "rate_ctrl.h"
+#include "utils/utils.h"
+#include "coredump.h"
+#include "fw/fw_dbg.h"
+#include "dbgfile.h"
+
+#define DEBUGFS_ADD_FILE(name, parent, mode)                                          \
+do {                                                                                  \
+       if (!debugfs_create_file(#name, mode, parent, cl_hw, &cl_dbgfs_##name##_ops)) \
+               goto err;                                                             \
+} while (0)
+
+/* File operation */
+#define DEBUGFS_READ_FUNC(name)                                  \
+       static ssize_t cl_dbgfs_##name##_read(struct file *file, \
+                       char __user *user_buf,                   \
+                       size_t count, loff_t *ppos)
+
+#define DEBUGFS_WRITE_FUNC(name)                                  \
+       static ssize_t cl_dbgfs_##name##_write(struct file *file, \
+                       const char __user *user_buf,              \
+                       size_t count, loff_t *ppos)
+
+#define DEBUGFS_READ_FILE_OPS(name)                                   \
+       DEBUGFS_READ_FUNC(name);                                      \
+       static const struct file_operations cl_dbgfs_##name##_ops = { \
+               .read   = cl_dbgfs_##name##_read,                     \
+               .open   = simple_open,                                \
+               .llseek = generic_file_llseek,                        \
+       }
+
+#define DEBUGFS_WRITE_FILE_OPS(name)                                  \
+       DEBUGFS_WRITE_FUNC(name);                                     \
+       static const struct file_operations cl_dbgfs_##name##_ops = { \
+               .write  = cl_dbgfs_##name##_write,                    \
+               .open   = simple_open,                                \
+               .llseek = generic_file_llseek,                        \
+       }
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name)                             \
+       DEBUGFS_READ_FUNC(name);                                      \
+       DEBUGFS_WRITE_FUNC(name);                                     \
+       static const struct file_operations cl_dbgfs_##name##_ops = { \
+               .write  = cl_dbgfs_##name##_write,                    \
+               .read   = cl_dbgfs_##name##_read,                     \
+               .open   = simple_open,                                \
+               .llseek = generic_file_llseek,                        \
+       }
+
+static ssize_t cl_dbgfs_set_debug_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count,
+                                       loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       struct cl_tcv_conf *conf = cl_hw->conf;
+       char buf[32];
+       char *usage_str = "Usage:\n"
+                         "echo [mod_filter] > set_debug\n";
+       unsigned long long mod_filter;
+       int ret;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf))
+               return -EFAULT;
+
+       ret = kstrtoull(buf, 0, &mod_filter);
+       if (ret) {
+               cl_dbg_verbose(cl_hw, "%s", usage_str);
+               return ret;
+       }
+
+       cl_dbg_verbose(cl_hw, "Send to FW: dbg_module=0x%x, dbg_severity=%u, mod_filter=0x%x\n",
+                      conf->ci_fw_dbg_module, conf->ci_fw_dbg_severity, (u32)mod_filter);
+
+       ret = cl_msg_tx_dbg_set_ce_mod_filter(cl_hw, conf->ci_fw_dbg_module);
+       if (ret)
+               return ret;
+
+       ret = cl_msg_tx_dbg_set_sev_filter(cl_hw, conf->ci_fw_dbg_severity);
+       if (ret)
+               return ret;
+
+       ret = cl_msg_tx_dbg_set_mod_filter(cl_hw, mod_filter);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+DEBUGFS_WRITE_FILE_OPS(set_debug);
+
+static ssize_t cl_dbgfs_tx_trace_debug_flag_write(struct file *file,
+                                                 const char __user *user_buf,
+                                                 size_t count,
+                                                 loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[32];
+       int ret;
+       unsigned long long result;
+       u8 read_write_cmd;
+       char *sptr, *token;
+       char *usage_str = "Usage: echo  [w\\r] [value] > tx_trace_debug_flag\n";
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf)) {
+               cl_dbg_err(cl_hw, "illegal input\n %s", usage_str);
+               return -EFAULT;
+       }
+       sptr = buf;
+       token = strsep(&sptr, " ");
+       if (!token) {
+               cl_dbg_err(cl_hw, "illegal input\n %s", usage_str);
+               return -EINVAL;
+       }
+
+       if (!strncasecmp(token, "w", strlen("w"))) {
+               read_write_cmd = 1;
+               ret = kstrtoull(sptr, 0, &result);
+               if (ret) {
+                       cl_dbg_err(cl_hw, "illegal input\n %s", usage_str);
+                       return ret;
+               }
+               cl_dbg_trace(cl_hw, "(write value=0x%x)\n", (u32)result);
+       } else if (!strncasecmp(token, "r", strlen("r"))) {
+               read_write_cmd = 0;
+               result = 0;
+               cl_dbg_trace(cl_hw, "(read)\n");
+       } else {
+               cl_dbg_err(cl_hw, "illegal input\n %s", usage_str);
+               return -EFAULT;
+       }
+
+       ret = cl_msg_tx_dbg_tx_trace_debug_flag(cl_hw, (u32)result, (u8)read_write_cmd);
+
+       return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(tx_trace_debug_flag);
+
+static ssize_t cl_dbgfs_test_mode_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count,
+                                       loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[32];
+       int ret;
+       unsigned long long params[TEST_MODE_PARAM_MAX + 1] = {0};
+       u32 test_params[TEST_MODE_PARAM_MAX + 1] = {0};
+       char *usage_str = "Usage:\n"
+                         "debugfsh <chip> <tcv> test_mode <command> <params> <params> ..\n";
+       char *token, *sptr;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+       int i = 0;
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf)) {
+               cl_dbg_err(cl_hw, "string %s count %zu eobuf %d\n", buf, count, eobuf);
+               return -EFAULT;
+       }
+       sptr = buf;
+       token = strsep(&sptr, " ");
+
+       for (i = 0; i < TEST_MODE_PARAM_MAX + 1; i++) {
+               if (token) {
+                       ret = kstrtoull(token, 0, &params[i]);
+                       if (ret) {
+                               cl_dbg_trace(cl_hw, "Token %s illegal convert %s\n",
+                                            token, usage_str);
+                               return ret;
+                       }
+                       test_params[i] = (u32)params[i];
+                       cl_dbg_trace(cl_hw, "param[%d]=%llu\n", i, params[i]);
+               } else {
+                       break;
+               }
+               token = strsep(&sptr, " ");
+       }
+
+       ret = cl_msg_tx_dbg_test_mode(cl_hw, test_params);
+
+       return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(test_mode);
+
+static ssize_t cl_dbgfs_fixed_rate_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count,
+                                        loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[32];
+       int ret;
+       unsigned long long mcs = 0, bw = 0, gi = 0, format = 0, ltf_field = 0,
+               pre_type_or_stbc = 0, sta_idx = 0xff;
+       char *sptr, *token1, *token2, *token3, *token4, *token5, *token6, *token7;
+       char *usage_str = "Usage:\n"
+                         "echo [mcs] [bw] [gi] [format] [ltf_field] [pre_type_or_stbc(opt)] "
+                         "[sta_idx(opt)] > fixed_rate\n";
+       u8 op_mode = RATE_OP_MODE_FIXED;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+       union cl_rate_ctrl_info rate_ctrl;
+       union cl_rate_ctrl_info_he rate_ctrl_he;
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf)) {
+               cl_dbg_err(cl_hw, "illegal input\n %s", usage_str);
+               return -EFAULT;
+       }
+
+       sptr = buf;
+
+       /* Token1 - mcs */
+       token1 = strsep(&sptr, " ");
+       if (!token1) {
+               cl_dbg_err(cl_hw, "token1 illegal %s\n", usage_str);
+               return -EINVAL;
+       }
+
+       ret = kstrtoull(token1, 0, &mcs);
+       if (ret) {
+               cl_dbg_err(cl_hw, "token1 %s illegal convert %s\n", token1, usage_str);
+               return ret;
+       }
+
+       /* Token2 - bw */
+       token2 = strsep(&sptr, " ");
+       if (!token2) {
+               cl_dbg_err(cl_hw, "token2 illegal %s\n", usage_str);
+               return -EINVAL;
+       }
+
+       ret = kstrtoull(token2, 0, &bw);
+       if (ret) {
+               cl_dbg_err(cl_hw, "token2 %s illegal convert %s\n", token2, usage_str);
+               return ret;
+       }
+
+       /* Token3 - gi */
+       token3 = strsep(&sptr, " ");
+       if (!token3) {
+               cl_dbg_err(cl_hw, "token3 illegal %s\n", usage_str);
+               return -EINVAL;
+       }
+
+       ret = kstrtoull(token3, 0, &gi);
+       if (ret) {
+               cl_dbg_err(cl_hw, "token3 %s illegal convert %s\n", token3, usage_str);
+               return ret;
+       }
+
+       /* Token4 - format */
+       token4 = strsep(&sptr, " ");
+       if (!token4) {
+               cl_dbg_err(cl_hw, "token4 illegal %s\n", usage_str);
+               return -EINVAL;
+       }
+
+       ret = kstrtoull(token4, 0, &format);
+       if (ret) {
+               cl_dbg_err(cl_hw, "token4 %s illegal convert %s\n", token4, usage_str);
+               return ret;
+       }
+
+       /* Token5 - ltf_field */
+       token5 = strsep(&sptr, " ");
+       if (!token5) {
+               cl_dbg_err(cl_hw, "token5 illegal %s\n", usage_str);
+               return -EINVAL;
+       }
+
+       ret = kstrtoull(token5, 0, &ltf_field);
+       if (ret) {
+               cl_dbg_err(cl_hw, "token5 %s illegal convert %s\n", token5, usage_str);
+               return ret;
+       }
+
+       /* Token6 - pre_type_or_stbc (optional) */
+       token6 = strsep(&sptr, " ");
+       if (token6) {
+               ret = kstrtoull(token6, 0, &pre_type_or_stbc);
+               if (ret) {
+                       cl_dbg_err(cl_hw, "token6 %s illegal convert %s\n", token6, usage_str);
+                       return ret;
+               }
+       }
+
+       /* Token7 - sta_idx (optional) */
+       token7 = strsep(&sptr, " ");
+       if (token7) {
+               ret = kstrtoull(token7, 0, &sta_idx);
+               if (ret) {
+                       cl_dbg_err(cl_hw, "token7 %s illegal convert %s\n", token7, usage_str);
+                       return ret;
+               }
+       }
+
+       /* Sanity tests */
+       if (sta_idx != STA_IDX_INVALID && !cl_sta_is_assoc(cl_hw, sta_idx)) {
+               cl_dbg_err(cl_hw, "Invalid station index [%llu]\n", sta_idx);
+               return -EINVAL;
+       }
+
+       if (format > FORMATMOD_HE_SU) {
+               cl_dbg_err(cl_hw, "Invalid format [%llu]\n", format);
+               return -EINVAL;
+       }
+
+       if (format < FORMATMOD_HE_SU)
+               ltf_field = 0;
+
+       /* Build rate_ctrl and rate_ctrl_he */
+       if (mcs == 0xff || bw == 0xff || gi == 0xff || format == 0xff) {
+               cl_hw->entry_fixed_rate = false;
+               cl_dbg_trace(cl_hw, "Fixed rate turned off\n");
+       } else {
+               rate_ctrl.word = 0;
+               rate_ctrl.field.mcs_index = mcs;
+               rate_ctrl.field.bw = bw;
+               rate_ctrl.field.gi = gi;
+               rate_ctrl.field.format_mod = format;
+
+               if (rate_ctrl.field.format_mod != FORMATMOD_NON_HT)
+                       rate_ctrl.field.pre_type_or_stbc = pre_type_or_stbc;
+
+               /* rate_ctrl_he is relevant only for HE format mode. */
+               if (format > FORMATMOD_VHT)
+                       rate_ctrl_he.field.spatial_conf = RATE_CNTRL_HE_SPATIAL_CONF_DEF;
+               else
+                       rate_ctrl_he.word = 0;
+
+               cl_dbg_trace(cl_hw, "mcs=%llu bw=%llu gi=%llu format=%llu --> rate_ctrl 0x%x\n",
+                            mcs, bw, gi, format, rate_ctrl.word);
+
+               /* Set entry_fixed_rate to true only if there is no specific station */
+               cl_hw->entry_fixed_rate = (sta_idx == 0xff);
+
+               if (!cl_rate_ctrl_set_dbgfs(cl_hw, sta_idx, rate_ctrl.word, rate_ctrl_he.word,
+                                           op_mode, bw, ltf_field))
+                       return -EFAULT;
+       }
+
+       return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(fixed_rate);
+
+static ssize_t cl_dbgfs_mactrace_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count,
+                                     loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       ssize_t read;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (cl_hw->debugfs.trace_prst) {
+               read = simple_read_from_buffer(user_buf, count, ppos,
+                                              &cl_hw->dbginfo.buf->u.dump.la_mem[0],
+                                              ARRAY_SIZE(cl_hw->dbginfo.buf->u.dump.la_mem[0]));
+       } else {
+               pr_debug("No dump!\n");
+               read = 0;
+       }
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+
+       return read;
+}
+
+static ssize_t cl_dbgfs_mactrace_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count,
+                                      loff_t *ppos)
+{
+       /* Write "1" to diags/mactrace triggers dump, type 0 (recoverable). */
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[32];
+       unsigned long v;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+
+       if (cl_hw->debugfs.unregistering)
+               return -EFAULT;
+
+       if (copy_from_user(&buf, user_buf, eobuf))
+               return -EFAULT;
+
+       buf[eobuf] = '\0';
+       if (kstrtoul(buf, 0, &v) || v != 1) {
+               pr_debug("Usage: echo 1 > mactrace : trigger firmware dump\n");
+               return -EFAULT;
+       }
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (cl_hw->debugfs.trace_prst) {
+               pr_debug("Dump already waiting\n");
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return -EFAULT;
+       }
+
+       scnprintf(buf, sizeof(buf), "Force trigger\n");
+       cl_msg_tx_dbg_trigger(cl_hw, buf);
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+
+       return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(mactrace);
+
+static ssize_t cl_dbgfs_phytrace_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count,
+                                     loff_t *ppos)
+{
+#if LA_CNT < 2
+       return -EFAULT; /* La_mem[1] does not exist */
+#else
+       struct cl_hw *cl_hw = file->private_data;
+       ssize_t read;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       read = simple_read_from_buffer(user_buf, count, ppos,
+                                      &cl_hw->dbginfo.buf->u.dump.la_mem[1],
+                                      ARRAY_SIZE(cl_hw->dbginfo.buf->u.dump.la_mem[1]));
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+       return read;
+#endif
+}
+
+DEBUGFS_READ_FILE_OPS(phytrace);
+
+static ssize_t cl_dbgfs_macdiags_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count,
+                                     loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       ssize_t read;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       read = simple_read_from_buffer(user_buf, count, ppos,
+                                      cl_hw->dbginfo.buf->u.dump.general_data.diags_mac,
+                                      DBG_DIAGS_MAC_MAX * 2); // FIXME: Why * 2?? BUG?
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+
+       return read;
+}
+
+DEBUGFS_READ_FILE_OPS(macdiags);
+
+static ssize_t cl_dbgfs_hwdiags_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count,
+                                    loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[16];
+       int ret;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                       "%08X\n", cl_hw->dbginfo.buf->u.dump.general_data.hw_diag);
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(hwdiags);
+
+static ssize_t cl_dbgfs_swdiags_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count,
+                                    loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       ssize_t read;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       read = simple_read_from_buffer(user_buf, count, ppos,
+                                      &cl_hw->dbginfo.buf->u.dump.general_data.sw_diag,
+                                      cl_hw->dbginfo.buf->u.dump.general_data.sw_diag_len);
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+       return read;
+}
+
+DEBUGFS_READ_FILE_OPS(swdiags);
+
+static ssize_t cl_dbgfs_lamacconf_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count,
+                                      loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       ssize_t read;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       read = simple_read_from_buffer(user_buf, count, ppos,
+                                      &cl_hw->dbginfo.buf->u.dump.general_data.la_conf[0],
+                                      sizeof(struct la_conf_tag));
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+       return read;
+}
+
+DEBUGFS_READ_FILE_OPS(lamacconf);
+
+static ssize_t cl_dbgfs_laphyconf_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count,
+                                      loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       ssize_t read;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       read = simple_read_from_buffer(user_buf, count, ppos,
+                                      &cl_hw->dbginfo.buf->u.dump.general_data.la_conf[1],
+                                      sizeof(struct la_conf_tag));
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+       return read;
+}
+
+DEBUGFS_READ_FILE_OPS(laphyconf);
+
+static ssize_t cl_dbgfs_chaninfo_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count,
+                                     loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       struct phy_channel_info *chan_info = &cl_hw->dbginfo.buf->u.dump.general_data.chan_info;
+       char buf[4 * 32];
+       int ret;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                       "type:         %u\n"
+                       "prim20_freq:  %u MHz\n"
+                       "center1_freq: %u MHz\n"
+                       "center2_freq: %u MHz\n",
+                       (chan_info->info1 >> 8) & 0xFF,
+                       (chan_info->info1 >> 16) & 0xFFFF,
+                       (chan_info->info2 >> 0) & 0xFFFF,
+                       (chan_info->info2 >> 16) & 0xFFFF);
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(chaninfo);
+
+#define MAX_BUF_SIZE 512
+
+static ssize_t cl_dbgfs_error_read(struct file *file,
+                                  char __user *user_buf,
+                                  size_t count,
+                                  loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       struct dbg_print_ind *ind =
+               &cl_hw->dbginfo.buf->u.dump.fw_dump.common_info.error_info;
+       ssize_t read;
+
+       mutex_lock(&cl_hw->dbginfo.mutex);
+       if (!cl_hw->debugfs.trace_prst) {
+               mutex_unlock(&cl_hw->dbginfo.mutex);
+               return 0;
+       }
+
+       /* If an assert message, search for assert string */
+       if (ind->file_id && ind->line) {
+               u16 file_id = le16_to_cpu(ind->file_id);
+               u16 line = le16_to_cpu(ind->line);
+               const char *assert_string = cl_dbgfile_get_msg_txt(&cl_hw->dbg_data, file_id, line);
+
+               /* If string not found. use FW error string. */
+               if (!assert_string)
+                       assert_string = cl_hw->dbginfo.buf->u.dump.general_data.error;
+
+               read = simple_read_from_buffer(user_buf, count, ppos, assert_string,
+                                              strnlen(assert_string, MAX_BUF_SIZE));
+       } else {
+               char *error = cl_hw->dbginfo.buf->u.dump.general_data.error;
+
+               read = simple_read_from_buffer(user_buf, count, ppos, error,
+                                              strnlen(error, DBG_ERROR_TRACE_SIZE));
+       }
+
+       mutex_unlock(&cl_hw->dbginfo.mutex);
+       return read;
+}
+
+DEBUGFS_READ_FILE_OPS(error);
+
+static ssize_t cl_dbgfs_mpifmask_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count,
+                                      loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[32];
+       int ret;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+       unsigned long long result;
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf))
+               return -EFAULT;
+
+       ret = kstrtoull(buf, 0, &result);
+       if (ret)
+               return ret;
+
+       cl_dbg_trace(cl_hw, "Set LA mpif mask = 0x%08x\n", (u32)result);
+
+       cl_msg_tx_dbg_set_la_mpif_mask(cl_hw, result);
+
+       return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(mpifmask);
+
+static ssize_t cl_dbgfs_trigpoint_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count,
+                                       loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[32];
+       int ret;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+       unsigned long long result;
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf))
+               return -EFAULT;
+
+       ret = kstrtoull(buf, 0, &result);
+       if (ret)
+               return ret;
+
+       cl_dbg_trace(cl_hw, "Set LA trigger point = 0x%x\n", (u32)result);
+
+       cl_msg_tx_dbg_set_la_trig_point(cl_hw, result);
+
+       return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(trigpoint);
+
+enum {
+       CL_LA_MPIF_DEBUG_MODE_GEN, /* Generic MPIF sampling mask */
+       CL_LA_MPIF_DEBUG_MODE_TX,  /* Tx oriented MPIF sampling mask */
+       CL_LA_MPIF_DEBUG_MODE_RX,  /* Rx oriented MPIF sampling mask */
+
+       CL_LA_MPIF_DEBUG_MODE_NUM
+};
+
+const char *ce_la_mpif_debug_mode_str[CL_LA_MPIF_DEBUG_MODE_NUM] = {
+       [CL_LA_MPIF_DEBUG_MODE_GEN] = "LA_MPIF_DEBUG_MODE_GEN",
+       [CL_LA_MPIF_DEBUG_MODE_TX]  = "LA_MPIF_DEBUG_MODE_TX",
+       [CL_LA_MPIF_DEBUG_MODE_RX]  = "LA_MPIF_DEBUG_MODE_RX"
+};
+
+static ssize_t cl_dbgfs_mpif_debug_mode_write(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count,
+                                             loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[32];
+       int ret;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+       unsigned long long result;
+       u8 mode;
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf))
+               return -EFAULT;
+
+       ret = kstrtoull(buf, 0, &result);
+       if (ret)
+               return ret;
+
+       mode = (u8)result;
+       if (mode >= CL_LA_MPIF_DEBUG_MODE_NUM)
+               return -EINVAL;
+
+       cl_dbg_trace(cl_hw, "Set LA MPIF mode = %u (%s)\n", mode, ce_la_mpif_debug_mode_str[mode]);
+
+       cl_msg_tx_dbg_set_la_mpif_debug_mode(cl_hw, result);
+
+       return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(mpif_debug_mode);
+
+char *la_trig_oper_str[LA_TRIG_OPER_MAX] = {
+       [LA_TRIG_OPER_EQ] = "eq",
+       [LA_TRIG_OPER_NOT_EQ] = "neq",
+       [LA_TRIG_OPER_GT] = "gt",
+       [LA_TRIG_OPER_GT_EQ] = "gte",
+       [LA_TRIG_OPER_LT] = "lt",
+       [LA_TRIG_OPER_LT_EQ] = "lte"
+};
+
+static ssize_t cl_dbgfs_trig_rule_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count,
+                                       loff_t *ppos)
+{
+       struct cl_hw *cl_hw = file->private_data;
+       char buf[100];
+       int ret = 0;
+       int eobuf = min_t(size_t, sizeof(buf) - 1, count);
+       char *sptr = NULL, *token = NULL;
+       unsigned long val;
+       u32 address = 0, value = 0, mask = 0, duration = 0;
+       u8 idx = 0, oper = 0;
+       bool enable = false;
+       char *usage_str = "Usage:\n"
+                         "echo  <rule-idx> <enable {1|0}> <address> <oper {eq|neq|gt|gte|lt|lte}>"
+                         " <value> <mask> [duration] > trig_rule\n";
+
+       buf[eobuf] = '\0';
+       if (copy_from_user(&buf, user_buf, eobuf))
+               return -EFAULT;
+
+       sptr = buf;
+
+       token = strsep(&sptr, " ");
+       if (!token) {
+               pr_err("Illegal token.\n%s", usage_str);
+               return -EINVAL;
+       }
+
+       ret = kstrtoul(token, 0, &val);
+       if (ret) {
+               pr_err("<rule-idx> illegal value (%s)\n%s", token, usage_str);
+               return -EINVAL;
+       }
+
+       idx = (u8)val;
+
+       token = strsep(&sptr, " ");
+       if (!token) {
+               pr_err("Illegal token.\n%s", usage_str);
+               return -EINVAL;
+       }
+
+       ret = kstrtoul(token, 0, &val);
+       if (ret) {
+               pr_err("<enable> illegal value (%s)\n%s", token, usage_str);
+               return -EINVAL;
+       }
+
+       enable = (bool)val;
+
+       if (enable) {
+               /* Address token */
+               token = strsep(&sptr, " ");
+               if (!token) {
+                       pr_err("Illegal token.\n%s", usage_str);
+                       return -EINVAL;
+               }
+
+               ret = kstrtoul(token, 0, &val);
+               if (ret) {
+                       pr_err("<address> illegal value (%s)\n%s", token, usage_str);
+                       return -EINVAL;
+               }
+
+               address = val;
+
+               /* Comparison Operator token */
+               token = strsep(&sptr, " ");
+               if (!token) {
+                       pr_err("Illegal token.\n%s", usage_str);
+                       return -EINVAL;
+               }
+
+               for (oper = LA_TRIG_OPER_EQ; oper < LA_TRIG_OPER_MAX; ++oper)
+                       if (!strcmp(token, la_trig_oper_str[oper]))
+                               break;
+
+               if (oper == LA_TRIG_OPER_MAX) {
+                       pr_err("<oper> illegal value (%s)\n%s", token, usage_str);
+                       return -EINVAL;
+               }
+
+               /* Value token */
+               token = strsep(&sptr, " ");
+               if (!token) {
+                       pr_err("Illegal token.\n%s", usage_str);
+                       return -EINVAL;
+               }
+
+               ret = kstrtoul(token, 0, &val);
+               if (ret) {
+                       pr_err("<value> illegal value (%s)\n%s", token, usage_str);
+                       return -EINVAL;
+               }
+
+               value = val;
+
+               /* Mask token */
+               token = strsep(&sptr, " ");
+               if (!token) {
+                       pr_err("Illegal token.\n%s", usage_str);
+                       return -EINVAL;
+               }
+
+               ret = kstrtoul(token, 0, &val);
+               if (ret) {
+                       pr_err("<mask> illegal value (%s)\n%s", token, usage_str);
+                       return -EINVAL;
+               }
+
+               mask = val;
+
+               /* Duration token (optional) */
+               token = strsep(&sptr, " ");
+               if (token) {
+                       ret = kstrtoul(token, 0, &val);
+                       if (ret) {
+                               pr_err("<duration> illegal value (%s)\n%s", token, usage_str);
+                               return -EINVAL;
+                       }
+
+                       duration = val;
+               }
+       }
+
+       if (enable)
+               pr_debug("Set trigger rule: idx=%u, addr=0x%08x, oper=%s, value=0x%08x, "
+                        "mask=0x%08x, duration=%u\n",
+                        idx, address, la_trig_oper_str[oper], value, mask, duration);
+       else
+               pr_debug("Disable trigger rule: idx=%u\n", idx);
+
+       if (cl_msg_tx_dbg_set_la_trig_rule(cl_hw, idx, enable, address, oper,
+                                          value, mask, duration))
+               return -EFAULT;
+
+       return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(trig_rule);
+
+int cl_dbgfs_register(struct cl_hw *cl_hw, const char *name)
+{
+       struct dentry *phyd = cl_hw->hw->wiphy->debugfsdir;
+       struct dentry *dir_drv, *dir_diags, *dir_cl;
+
+       cl_dbg_trace(cl_hw, "/sys/kernel/debug/%s/%s/%s.\n",
+                    phyd->d_parent->d_name.name, phyd->d_name.name, name);
+
+       dir_drv = debugfs_create_dir(name, phyd);
+       if (!dir_drv)
+               return -ENOMEM;
+
+       dir_diags = debugfs_create_dir("diags", dir_drv);
+       if (!dir_diags)
+               goto err;
+
+       dir_cl = debugfs_create_dir("cl8k", dir_drv);
+       if (!dir_cl) {
+               cl_dbg_err(cl_hw, "%s\n", name);
+               goto err;
+       }
+
+       cl_coredump_init(cl_hw, dir_drv);
+
+       DEBUGFS_ADD_FILE(mactrace,            dir_diags, 0400);
+       DEBUGFS_ADD_FILE(phytrace,            dir_diags, 0400);
+       DEBUGFS_ADD_FILE(macdiags,            dir_diags, 0400);
+       DEBUGFS_ADD_FILE(hwdiags,             dir_diags, 0400);
+       DEBUGFS_ADD_FILE(swdiags,             dir_diags, 0400);
+       DEBUGFS_ADD_FILE(error,               dir_diags, 0400);
+       DEBUGFS_ADD_FILE(lamacconf,           dir_diags, 0400);
+       DEBUGFS_ADD_FILE(laphyconf,           dir_diags, 0400);
+       DEBUGFS_ADD_FILE(chaninfo,            dir_diags, 0400);
+       DEBUGFS_ADD_FILE(trigpoint,           dir_diags, 0200);
+       DEBUGFS_ADD_FILE(mpifmask,            dir_diags, 0200);
+       DEBUGFS_ADD_FILE(mpif_debug_mode,     dir_diags, 0200);
+       DEBUGFS_ADD_FILE(set_debug,           dir_cl,    0200);
+       DEBUGFS_ADD_FILE(test_mode,           dir_cl,    0200);
+       DEBUGFS_ADD_FILE(fixed_rate,          dir_cl,    0200);
+       DEBUGFS_ADD_FILE(tx_trace_debug_flag, dir_cl,    0200);
+       DEBUGFS_ADD_FILE(trig_rule,           dir_cl,    0200);
+       return 0;
+
+err:
+       cl_dbgfs_unregister(cl_hw);
+       return -ENOMEM;
+}
+
+void cl_dbgfs_unregister(struct cl_hw *cl_hw)
+{
+       cl_coredump_close(cl_hw);
+}
--
2.30.0

________________________________
The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any retransmission, dissemination, copying or other use of, or taking of any action in reliance upon this information is prohibited. If you received this in error, please contact the sender and delete the material from any computer. Nothing contained herein shall be deemed as a representation, warranty or a commitment by Celeno. No warranties are expressed or implied, including, but not limited to, any implied warranties of non-infringement, merchantability and fitness for a particular purpose.
________________________________





[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux