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 */ 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 * 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. */ 47 static int ipc_trace_remove_buf_file_handler(struct dentry *dentry) 48 { 49 debugfs_remove(dentry); 50 return 0; 51 } 52 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 */ 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 */ 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 */ 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 */ 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