0001
0002
0003
0004
0005
0006 #include <linux/wwan.h>
0007 #include "iosm_ipc_trace.h"
0008
0009
0010 #define IOSM_TRC_SUB_BUFF_SIZE 131072
0011 #define IOSM_TRC_N_SUB_BUFF 32
0012
0013 #define IOSM_TRC_FILE_PERM 0600
0014
0015 #define IOSM_TRC_DEBUGFS_TRACE "trace"
0016 #define IOSM_TRC_DEBUGFS_TRACE_CTRL "trace_ctrl"
0017
0018
0019
0020
0021
0022
0023 void ipc_trace_port_rx(struct iosm_imem *ipc_imem, struct sk_buff *skb)
0024 {
0025 struct iosm_trace *ipc_trace = ipc_imem->trace;
0026
0027 if (ipc_trace->ipc_rchan)
0028 relay_write(ipc_trace->ipc_rchan, skb->data, skb->len);
0029
0030 dev_kfree_skb(skb);
0031 }
0032
0033
0034 static struct dentry *
0035 ipc_trace_create_buf_file_handler(const char *filename,
0036 struct dentry *parent,
0037 umode_t mode,
0038 struct rchan_buf *buf,
0039 int *is_global)
0040 {
0041 *is_global = 1;
0042 return debugfs_create_file(filename, mode, parent, buf,
0043 &relay_file_operations);
0044 }
0045
0046
0047 static int ipc_trace_remove_buf_file_handler(struct dentry *dentry)
0048 {
0049 debugfs_remove(dentry);
0050 return 0;
0051 }
0052
0053 static int ipc_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
0054 void *prev_subbuf,
0055 size_t prev_padding)
0056 {
0057 if (relay_buf_full(buf)) {
0058 pr_err_ratelimited("Relay_buf full dropping traces");
0059 return 0;
0060 }
0061
0062 return 1;
0063 }
0064
0065
0066 static struct rchan_callbacks relay_callbacks = {
0067 .subbuf_start = ipc_trace_subbuf_start_handler,
0068 .create_buf_file = ipc_trace_create_buf_file_handler,
0069 .remove_buf_file = ipc_trace_remove_buf_file_handler,
0070 };
0071
0072
0073 static ssize_t ipc_trace_ctrl_file_read(struct file *filp, char __user *buffer,
0074 size_t count, loff_t *ppos)
0075 {
0076 struct iosm_trace *ipc_trace = filp->private_data;
0077 char buf[16];
0078 int len;
0079
0080 mutex_lock(&ipc_trace->trc_mutex);
0081 len = snprintf(buf, sizeof(buf), "%d\n", ipc_trace->mode);
0082 mutex_unlock(&ipc_trace->trc_mutex);
0083
0084 return simple_read_from_buffer(buffer, count, ppos, buf, len);
0085 }
0086
0087
0088 static ssize_t ipc_trace_ctrl_file_write(struct file *filp,
0089 const char __user *buffer,
0090 size_t count, loff_t *ppos)
0091 {
0092 struct iosm_trace *ipc_trace = filp->private_data;
0093 unsigned long val;
0094 int ret;
0095
0096 ret = kstrtoul_from_user(buffer, count, 10, &val);
0097 if (ret)
0098 return ret;
0099
0100 mutex_lock(&ipc_trace->trc_mutex);
0101 if (val == TRACE_ENABLE && ipc_trace->mode != TRACE_ENABLE) {
0102 ipc_trace->channel = ipc_imem_sys_port_open(ipc_trace->ipc_imem,
0103 ipc_trace->chl_id,
0104 IPC_HP_CDEV_OPEN);
0105 if (!ipc_trace->channel) {
0106 ret = -EIO;
0107 goto unlock;
0108 }
0109 ipc_trace->mode = TRACE_ENABLE;
0110 } else if (val == TRACE_DISABLE && ipc_trace->mode != TRACE_DISABLE) {
0111 ipc_trace->mode = TRACE_DISABLE;
0112
0113 ipc_imem_sys_port_close(ipc_trace->ipc_imem,
0114 ipc_trace->channel);
0115 relay_flush(ipc_trace->ipc_rchan);
0116 }
0117 ret = count;
0118 unlock:
0119 mutex_unlock(&ipc_trace->trc_mutex);
0120 return ret;
0121 }
0122
0123 static const struct file_operations ipc_trace_fops = {
0124 .open = simple_open,
0125 .write = ipc_trace_ctrl_file_write,
0126 .read = ipc_trace_ctrl_file_read,
0127 };
0128
0129
0130
0131
0132
0133
0134
0135 struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem)
0136 {
0137 struct ipc_chnl_cfg chnl_cfg = { 0 };
0138 struct iosm_trace *ipc_trace;
0139
0140 ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3);
0141 ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg,
0142 IRQ_MOD_OFF);
0143
0144 ipc_trace = kzalloc(sizeof(*ipc_trace), GFP_KERNEL);
0145 if (!ipc_trace)
0146 return NULL;
0147
0148 ipc_trace->mode = TRACE_DISABLE;
0149 ipc_trace->dev = ipc_imem->dev;
0150 ipc_trace->ipc_imem = ipc_imem;
0151 ipc_trace->chl_id = IPC_MEM_CTRL_CHL_ID_3;
0152
0153 mutex_init(&ipc_trace->trc_mutex);
0154
0155 ipc_trace->ctrl_file = debugfs_create_file(IOSM_TRC_DEBUGFS_TRACE_CTRL,
0156 IOSM_TRC_FILE_PERM,
0157 ipc_imem->debugfs_dir,
0158 ipc_trace, &ipc_trace_fops);
0159
0160 ipc_trace->ipc_rchan = relay_open(IOSM_TRC_DEBUGFS_TRACE,
0161 ipc_imem->debugfs_dir,
0162 IOSM_TRC_SUB_BUFF_SIZE,
0163 IOSM_TRC_N_SUB_BUFF,
0164 &relay_callbacks, NULL);
0165
0166 return ipc_trace;
0167 }
0168
0169
0170
0171
0172
0173 void ipc_trace_deinit(struct iosm_trace *ipc_trace)
0174 {
0175 if (!ipc_trace)
0176 return;
0177
0178 debugfs_remove(ipc_trace->ctrl_file);
0179 relay_close(ipc_trace->ipc_rchan);
0180 mutex_destroy(&ipc_trace->trc_mutex);
0181 kfree(ipc_trace);
0182 }