1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2020-2021 Intel Corporation.
4 */
5
6 #include <linux/wwan.h>
7 #include "iosm_ipc_trace.h"
8
9 /* sub buffer size and number of sub buffer */
10 #define IOSM_TRC_SUB_BUFF_SIZE 131072
11 #define IOSM_TRC_N_SUB_BUFF 32
12
13 #define IOSM_TRC_FILE_PERM 0600
14
15 #define IOSM_TRC_DEBUGFS_TRACE "trace"
16 #define IOSM_TRC_DEBUGFS_TRACE_CTRL "trace_ctrl"
17
18 /**
19 * ipc_trace_port_rx - Receive trace packet from cp and write to relay buffer
20 * @ipc_imem: Pointer to iosm_imem structure
21 * @skb: Pointer to struct sk_buff
22 */
ipc_trace_port_rx(struct iosm_imem * ipc_imem,struct sk_buff * skb)23 void ipc_trace_port_rx(struct iosm_imem *ipc_imem, struct sk_buff *skb)
24 {
25 struct iosm_trace *ipc_trace = ipc_imem->trace;
26
27 if (ipc_trace->ipc_rchan)
28 relay_write(ipc_trace->ipc_rchan, skb->data, skb->len);
29
30 dev_kfree_skb(skb);
31 }
32
33 /* Creates relay file in debugfs. */
34 static struct dentry *
ipc_trace_create_buf_file_handler(const char * filename,struct dentry * parent,umode_t mode,struct rchan_buf * buf,int * is_global)35 ipc_trace_create_buf_file_handler(const char *filename,
36 struct dentry *parent,
37 umode_t mode,
38 struct rchan_buf *buf,
39 int *is_global)
40 {
41 *is_global = 1;
42 return debugfs_create_file(filename, mode, parent, buf,
43 &relay_file_operations);
44 }
45
46 /* Removes relay file from debugfs. */
ipc_trace_remove_buf_file_handler(struct dentry * dentry)47 static int ipc_trace_remove_buf_file_handler(struct dentry *dentry)
48 {
49 debugfs_remove(dentry);
50 return 0;
51 }
52
ipc_trace_subbuf_start_handler(struct rchan_buf * buf,void * subbuf,void * prev_subbuf)53 static int ipc_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
54 void *prev_subbuf)
55 {
56 if (relay_buf_full(buf)) {
57 pr_err_ratelimited("Relay_buf full dropping traces");
58 return 0;
59 }
60
61 return 1;
62 }
63
64 /* Relay interface callbacks */
65 static struct rchan_callbacks relay_callbacks = {
66 .subbuf_start = ipc_trace_subbuf_start_handler,
67 .create_buf_file = ipc_trace_create_buf_file_handler,
68 .remove_buf_file = ipc_trace_remove_buf_file_handler,
69 };
70
71 /* Copy the trace control mode to user buffer */
ipc_trace_ctrl_file_read(struct file * filp,char __user * buffer,size_t count,loff_t * ppos)72 static ssize_t ipc_trace_ctrl_file_read(struct file *filp, char __user *buffer,
73 size_t count, loff_t *ppos)
74 {
75 struct iosm_trace *ipc_trace = filp->private_data;
76 char buf[16];
77 int len;
78
79 mutex_lock(&ipc_trace->trc_mutex);
80 len = snprintf(buf, sizeof(buf), "%d\n", ipc_trace->mode);
81 mutex_unlock(&ipc_trace->trc_mutex);
82
83 return simple_read_from_buffer(buffer, count, ppos, buf, len);
84 }
85
86 /* Open and close the trace channel depending on user input */
ipc_trace_ctrl_file_write(struct file * filp,const char __user * buffer,size_t count,loff_t * ppos)87 static ssize_t ipc_trace_ctrl_file_write(struct file *filp,
88 const char __user *buffer,
89 size_t count, loff_t *ppos)
90 {
91 struct iosm_trace *ipc_trace = filp->private_data;
92 unsigned long val;
93 int ret;
94
95 ret = kstrtoul_from_user(buffer, count, 10, &val);
96 if (ret)
97 return ret;
98
99 mutex_lock(&ipc_trace->trc_mutex);
100 if (val == TRACE_ENABLE && ipc_trace->mode != TRACE_ENABLE) {
101 ipc_trace->channel = ipc_imem_sys_port_open(ipc_trace->ipc_imem,
102 ipc_trace->chl_id,
103 IPC_HP_CDEV_OPEN);
104 if (!ipc_trace->channel) {
105 ret = -EIO;
106 goto unlock;
107 }
108 ipc_trace->mode = TRACE_ENABLE;
109 } else if (val == TRACE_DISABLE && ipc_trace->mode != TRACE_DISABLE) {
110 ipc_trace->mode = TRACE_DISABLE;
111 /* close trace channel */
112 ipc_imem_sys_port_close(ipc_trace->ipc_imem,
113 ipc_trace->channel);
114 relay_flush(ipc_trace->ipc_rchan);
115 }
116 ret = count;
117 unlock:
118 mutex_unlock(&ipc_trace->trc_mutex);
119 return ret;
120 }
121
122 static const struct file_operations ipc_trace_fops = {
123 .open = simple_open,
124 .write = ipc_trace_ctrl_file_write,
125 .read = ipc_trace_ctrl_file_read,
126 };
127
128 /**
129 * ipc_trace_init - Create trace interface & debugfs entries
130 * @ipc_imem: Pointer to iosm_imem structure
131 *
132 * Returns: Pointer to trace instance on success else NULL
133 */
ipc_trace_init(struct iosm_imem * ipc_imem)134 struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem)
135 {
136 struct ipc_chnl_cfg chnl_cfg = { 0 };
137 struct iosm_trace *ipc_trace;
138
139 ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3);
140 ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg,
141 IRQ_MOD_OFF);
142
143 ipc_trace = kzalloc(sizeof(*ipc_trace), GFP_KERNEL);
144 if (!ipc_trace)
145 return NULL;
146
147 ipc_trace->mode = TRACE_DISABLE;
148 ipc_trace->dev = ipc_imem->dev;
149 ipc_trace->ipc_imem = ipc_imem;
150 ipc_trace->chl_id = IPC_MEM_CTRL_CHL_ID_3;
151
152 mutex_init(&ipc_trace->trc_mutex);
153
154 ipc_trace->ctrl_file = debugfs_create_file(IOSM_TRC_DEBUGFS_TRACE_CTRL,
155 IOSM_TRC_FILE_PERM,
156 ipc_imem->debugfs_dir,
157 ipc_trace, &ipc_trace_fops);
158
159 ipc_trace->ipc_rchan = relay_open(IOSM_TRC_DEBUGFS_TRACE,
160 ipc_imem->debugfs_dir,
161 IOSM_TRC_SUB_BUFF_SIZE,
162 IOSM_TRC_N_SUB_BUFF,
163 &relay_callbacks, NULL);
164
165 return ipc_trace;
166 }
167
168 /**
169 * ipc_trace_deinit - Closing relayfs, removing debugfs entries
170 * @ipc_trace: Pointer to the iosm_trace data struct
171 */
ipc_trace_deinit(struct iosm_trace * ipc_trace)172 void ipc_trace_deinit(struct iosm_trace *ipc_trace)
173 {
174 if (!ipc_trace)
175 return;
176
177 debugfs_remove(ipc_trace->ctrl_file);
178 relay_close(ipc_trace->ipc_rchan);
179 mutex_destroy(&ipc_trace->trc_mutex);
180 kfree(ipc_trace);
181 }
182