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