diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/net/wwan/t7xx/t7xx_port_trace.c | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/drivers/net/wwan/t7xx/t7xx_port_trace.c b/drivers/net/wwan/t7xx/t7xx_port_trace.c new file mode 100644 index 000000000..6a3f36385 --- /dev/null +++ b/drivers/net/wwan/t7xx/t7xx_port_trace.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Intel Corporation. + */ + +#include <linux/debugfs.h> +#include <linux/relay.h> +#include <linux/skbuff.h> +#include <linux/wwan.h> + +#include "t7xx_port.h" +#include "t7xx_port_proxy.h" +#include "t7xx_state_monitor.h" + +#define T7XX_TRC_SUB_BUFF_SIZE 131072 +#define T7XX_TRC_N_SUB_BUFF 32 + +static struct dentry *t7xx_trace_create_buf_file_handler(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + *is_global = 1; + return debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); +} + +static int t7xx_trace_remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +static int t7xx_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf, + void *prev_subbuf, size_t prev_padding) +{ + if (relay_buf_full(buf)) { + pr_err_ratelimited("Relay_buf full dropping traces"); + return 0; + } + + return 1; +} + +static struct rchan_callbacks relay_callbacks = { + .subbuf_start = t7xx_trace_subbuf_start_handler, + .create_buf_file = t7xx_trace_create_buf_file_handler, + .remove_buf_file = t7xx_trace_remove_buf_file_handler, +}; + +static void t7xx_trace_port_uninit(struct t7xx_port *port) +{ + struct dentry *debugfs_dir = port->t7xx_dev->debugfs_dir; + struct rchan *relaych = port->log.relaych; + + if (!relaych) + return; + + relay_close(relaych); + debugfs_remove_recursive(debugfs_dir); +} + +static int t7xx_trace_port_recv_skb(struct t7xx_port *port, struct sk_buff *skb) +{ + struct rchan *relaych = port->log.relaych; + + if (!relaych) + return -EINVAL; + + relay_write(relaych, skb->data, skb->len); + dev_kfree_skb(skb); + return 0; +} + +static void t7xx_port_trace_md_state_notify(struct t7xx_port *port, unsigned int state) +{ + struct rchan *relaych = port->log.relaych; + struct dentry *debugfs_wwan_dir; + struct dentry *debugfs_dir; + + if (state != MD_STATE_READY || relaych) + return; + + debugfs_wwan_dir = wwan_get_debugfs_dir(port->dev); + if (IS_ERR(debugfs_wwan_dir)) + return; + + debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, debugfs_wwan_dir); + if (IS_ERR_OR_NULL(debugfs_dir)) { + wwan_put_debugfs_dir(debugfs_wwan_dir); + dev_err(port->dev, "Unable to create debugfs for trace"); + return; + } + + relaych = relay_open("relay_ch", debugfs_dir, T7XX_TRC_SUB_BUFF_SIZE, + T7XX_TRC_N_SUB_BUFF, &relay_callbacks, NULL); + if (!relaych) + goto err_rm_debugfs_dir; + + wwan_put_debugfs_dir(debugfs_wwan_dir); + port->log.relaych = relaych; + port->t7xx_dev->debugfs_dir = debugfs_dir; + return; + +err_rm_debugfs_dir: + debugfs_remove_recursive(debugfs_dir); + wwan_put_debugfs_dir(debugfs_wwan_dir); + dev_err(port->dev, "Unable to create trace port %s", port->port_conf->name); +} + +struct port_ops t7xx_trace_port_ops = { + .recv_skb = t7xx_trace_port_recv_skb, + .uninit = t7xx_trace_port_uninit, + .md_state_notify = t7xx_port_trace_md_state_notify, +}; |