// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2020-2021 Intel Corporation. */ #include #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); }