183 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only
 | |
| /*
 | |
|  * Copyright (C) 2020-2021 Intel Corporation.
 | |
|  */
 | |
| 
 | |
| #include <linux/wwan.h>
 | |
| #include "iosm_ipc_trace.h"
 | |
| 
 | |
| /* sub buffer size and number of sub buffer */
 | |
| #define IOSM_TRC_SUB_BUFF_SIZE 131072
 | |
| #define IOSM_TRC_N_SUB_BUFF 32
 | |
| 
 | |
| #define IOSM_TRC_FILE_PERM 0600
 | |
| 
 | |
| #define IOSM_TRC_DEBUGFS_TRACE "trace"
 | |
| #define IOSM_TRC_DEBUGFS_TRACE_CTRL "trace_ctrl"
 | |
| 
 | |
| /**
 | |
|  * ipc_trace_port_rx - Receive trace packet from cp and write to relay buffer
 | |
|  * @ipc_imem:   Pointer to iosm_imem structure
 | |
|  * @skb:        Pointer to struct sk_buff
 | |
|  */
 | |
| void ipc_trace_port_rx(struct iosm_imem *ipc_imem, struct sk_buff *skb)
 | |
| {
 | |
| 	struct iosm_trace *ipc_trace = ipc_imem->trace;
 | |
| 
 | |
| 	if (ipc_trace->ipc_rchan)
 | |
| 		relay_write(ipc_trace->ipc_rchan, skb->data, skb->len);
 | |
| 
 | |
| 	dev_kfree_skb(skb);
 | |
| }
 | |
| 
 | |
| /* Creates relay file in debugfs. */
 | |
| 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)
 | |
| {
 | |
| 	*is_global = 1;
 | |
| 	return debugfs_create_file(filename, mode, parent, buf,
 | |
| 				   &relay_file_operations);
 | |
| }
 | |
| 
 | |
| /* Removes relay file from debugfs. */
 | |
| static int ipc_trace_remove_buf_file_handler(struct dentry *dentry)
 | |
| {
 | |
| 	debugfs_remove(dentry);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ipc_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
 | |
| 					  void *prev_subbuf,
 | |
| 					  size_t prev_padding)
 | |
| {
 | |
| 	if (relay_buf_full(buf)) {
 | |
| 		pr_err_ratelimited("Relay_buf full dropping traces");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /* Relay interface callbacks */
 | |
| static struct rchan_callbacks relay_callbacks = {
 | |
| 	.subbuf_start = ipc_trace_subbuf_start_handler,
 | |
| 	.create_buf_file = ipc_trace_create_buf_file_handler,
 | |
| 	.remove_buf_file = ipc_trace_remove_buf_file_handler,
 | |
| };
 | |
| 
 | |
| /* Copy the trace control mode to user buffer */
 | |
| static ssize_t ipc_trace_ctrl_file_read(struct file *filp, char __user *buffer,
 | |
| 					size_t count, loff_t *ppos)
 | |
| {
 | |
| 	struct iosm_trace *ipc_trace = filp->private_data;
 | |
| 	char buf[16];
 | |
| 	int len;
 | |
| 
 | |
| 	mutex_lock(&ipc_trace->trc_mutex);
 | |
| 	len = snprintf(buf, sizeof(buf), "%d\n", ipc_trace->mode);
 | |
| 	mutex_unlock(&ipc_trace->trc_mutex);
 | |
| 
 | |
| 	return simple_read_from_buffer(buffer, count, ppos, buf, len);
 | |
| }
 | |
| 
 | |
| /* Open and close the trace channel depending on user input */
 | |
| static ssize_t ipc_trace_ctrl_file_write(struct file *filp,
 | |
| 					 const char __user *buffer,
 | |
| 					 size_t count, loff_t *ppos)
 | |
| {
 | |
| 	struct iosm_trace *ipc_trace = filp->private_data;
 | |
| 	unsigned long val;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = kstrtoul_from_user(buffer, count, 10, &val);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	mutex_lock(&ipc_trace->trc_mutex);
 | |
| 	if (val == TRACE_ENABLE && ipc_trace->mode != TRACE_ENABLE) {
 | |
| 		ipc_trace->channel = ipc_imem_sys_port_open(ipc_trace->ipc_imem,
 | |
| 							    ipc_trace->chl_id,
 | |
| 							    IPC_HP_CDEV_OPEN);
 | |
| 		if (!ipc_trace->channel) {
 | |
| 			ret = -EIO;
 | |
| 			goto unlock;
 | |
| 		}
 | |
| 		ipc_trace->mode = TRACE_ENABLE;
 | |
| 	} else if (val == TRACE_DISABLE && ipc_trace->mode != TRACE_DISABLE) {
 | |
| 		ipc_trace->mode = TRACE_DISABLE;
 | |
| 		/* close trace channel */
 | |
| 		ipc_imem_sys_port_close(ipc_trace->ipc_imem,
 | |
| 					ipc_trace->channel);
 | |
| 		relay_flush(ipc_trace->ipc_rchan);
 | |
| 	}
 | |
| 	ret = count;
 | |
| unlock:
 | |
| 	mutex_unlock(&ipc_trace->trc_mutex);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static const struct file_operations ipc_trace_fops = {
 | |
| 	.open = simple_open,
 | |
| 	.write = ipc_trace_ctrl_file_write,
 | |
| 	.read  = ipc_trace_ctrl_file_read,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * ipc_trace_init - Create trace interface & debugfs entries
 | |
|  * @ipc_imem:   Pointer to iosm_imem structure
 | |
|  *
 | |
|  * Returns: Pointer to trace instance on success else NULL
 | |
|  */
 | |
| struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem)
 | |
| {
 | |
| 	struct ipc_chnl_cfg chnl_cfg = { 0 };
 | |
| 	struct iosm_trace *ipc_trace;
 | |
| 
 | |
| 	ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3);
 | |
| 	ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg,
 | |
| 			      IRQ_MOD_OFF);
 | |
| 
 | |
| 	ipc_trace = kzalloc(sizeof(*ipc_trace), GFP_KERNEL);
 | |
| 	if (!ipc_trace)
 | |
| 		return NULL;
 | |
| 
 | |
| 	ipc_trace->mode = TRACE_DISABLE;
 | |
| 	ipc_trace->dev = ipc_imem->dev;
 | |
| 	ipc_trace->ipc_imem = ipc_imem;
 | |
| 	ipc_trace->chl_id = IPC_MEM_CTRL_CHL_ID_3;
 | |
| 
 | |
| 	mutex_init(&ipc_trace->trc_mutex);
 | |
| 
 | |
| 	ipc_trace->ctrl_file = debugfs_create_file(IOSM_TRC_DEBUGFS_TRACE_CTRL,
 | |
| 						   IOSM_TRC_FILE_PERM,
 | |
| 						   ipc_imem->debugfs_dir,
 | |
| 						   ipc_trace, &ipc_trace_fops);
 | |
| 
 | |
| 	ipc_trace->ipc_rchan = relay_open(IOSM_TRC_DEBUGFS_TRACE,
 | |
| 					  ipc_imem->debugfs_dir,
 | |
| 					  IOSM_TRC_SUB_BUFF_SIZE,
 | |
| 					  IOSM_TRC_N_SUB_BUFF,
 | |
| 					  &relay_callbacks, NULL);
 | |
| 
 | |
| 	return ipc_trace;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ipc_trace_deinit - Closing relayfs, removing debugfs entries
 | |
|  * @ipc_trace: Pointer to the iosm_trace data struct
 | |
|  */
 | |
| void ipc_trace_deinit(struct iosm_trace *ipc_trace)
 | |
| {
 | |
| 	if (!ipc_trace)
 | |
| 		return;
 | |
| 
 | |
| 	debugfs_remove(ipc_trace->ctrl_file);
 | |
| 	relay_close(ipc_trace->ipc_rchan);
 | |
| 	mutex_destroy(&ipc_trace->trc_mutex);
 | |
| 	kfree(ipc_trace);
 | |
| }
 |