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 size_t prev_padding) 56 { 57 if (relay_buf_full(buf)) { 58 pr_err_ratelimited("Relay_buf full dropping traces"); 59 return 0; 60 } 61 62 return 1; 63 } 64 65 /* Relay interface callbacks */ 66 static struct rchan_callbacks relay_callbacks = { 67 .subbuf_start = ipc_trace_subbuf_start_handler, 68 .create_buf_file = ipc_trace_create_buf_file_handler, 69 .remove_buf_file = ipc_trace_remove_buf_file_handler, 70 }; 71 72 /* Copy the trace control mode to user buffer */ 73 static ssize_t ipc_trace_ctrl_file_read(struct file *filp, char __user *buffer, 74 size_t count, loff_t *ppos) 75 { 76 struct iosm_trace *ipc_trace = filp->private_data; 77 char buf[16]; 78 int len; 79 80 mutex_lock(&ipc_trace->trc_mutex); 81 len = snprintf(buf, sizeof(buf), "%d\n", ipc_trace->mode); 82 mutex_unlock(&ipc_trace->trc_mutex); 83 84 return simple_read_from_buffer(buffer, count, ppos, buf, len); 85 } 86 87 /* Open and close the trace channel depending on user input */ 88 static ssize_t ipc_trace_ctrl_file_write(struct file *filp, 89 const char __user *buffer, 90 size_t count, loff_t *ppos) 91 { 92 struct iosm_trace *ipc_trace = filp->private_data; 93 unsigned long val; 94 int ret; 95 96 ret = kstrtoul_from_user(buffer, count, 10, &val); 97 if (ret) 98 return ret; 99 100 mutex_lock(&ipc_trace->trc_mutex); 101 if (val == TRACE_ENABLE && ipc_trace->mode != TRACE_ENABLE) { 102 ipc_trace->channel = ipc_imem_sys_port_open(ipc_trace->ipc_imem, 103 ipc_trace->chl_id, 104 IPC_HP_CDEV_OPEN); 105 if (!ipc_trace->channel) { 106 ret = -EIO; 107 goto unlock; 108 } 109 ipc_trace->mode = TRACE_ENABLE; 110 } else if (val == TRACE_DISABLE && ipc_trace->mode != TRACE_DISABLE) { 111 ipc_trace->mode = TRACE_DISABLE; 112 /* close trace channel */ 113 ipc_imem_sys_port_close(ipc_trace->ipc_imem, 114 ipc_trace->channel); 115 relay_flush(ipc_trace->ipc_rchan); 116 } 117 ret = count; 118 unlock: 119 mutex_unlock(&ipc_trace->trc_mutex); 120 return ret; 121 } 122 123 static const struct file_operations ipc_trace_fops = { 124 .open = simple_open, 125 .write = ipc_trace_ctrl_file_write, 126 .read = ipc_trace_ctrl_file_read, 127 }; 128 129 /** 130 * ipc_trace_init - Create trace interface & debugfs entries 131 * @ipc_imem: Pointer to iosm_imem structure 132 * 133 * Returns: Pointer to trace instance on success else NULL 134 */ 135 struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem) 136 { 137 struct ipc_chnl_cfg chnl_cfg = { 0 }; 138 struct iosm_trace *ipc_trace; 139 140 ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3); 141 ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg, 142 IRQ_MOD_OFF); 143 144 ipc_trace = kzalloc(sizeof(*ipc_trace), GFP_KERNEL); 145 if (!ipc_trace) 146 return NULL; 147 148 ipc_trace->mode = TRACE_DISABLE; 149 ipc_trace->dev = ipc_imem->dev; 150 ipc_trace->ipc_imem = ipc_imem; 151 ipc_trace->chl_id = IPC_MEM_CTRL_CHL_ID_3; 152 153 mutex_init(&ipc_trace->trc_mutex); 154 155 ipc_trace->ctrl_file = debugfs_create_file(IOSM_TRC_DEBUGFS_TRACE_CTRL, 156 IOSM_TRC_FILE_PERM, 157 ipc_imem->debugfs_dir, 158 ipc_trace, &ipc_trace_fops); 159 160 ipc_trace->ipc_rchan = relay_open(IOSM_TRC_DEBUGFS_TRACE, 161 ipc_imem->debugfs_dir, 162 IOSM_TRC_SUB_BUFF_SIZE, 163 IOSM_TRC_N_SUB_BUFF, 164 &relay_callbacks, NULL); 165 166 return ipc_trace; 167 } 168 169 /** 170 * ipc_trace_deinit - Closing relayfs, removing debugfs entries 171 * @ipc_trace: Pointer to the iosm_trace data struct 172 */ 173 void ipc_trace_deinit(struct iosm_trace *ipc_trace) 174 { 175 if (!ipc_trace) 176 return; 177 178 debugfs_remove(ipc_trace->ctrl_file); 179 relay_close(ipc_trace->ipc_rchan); 180 mutex_destroy(&ipc_trace->trc_mutex); 181 kfree(ipc_trace); 182 } 183