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