1235783Skib// SPDX-License-Identifier: GPL-2.0-only
2235783Skib//
3235783Skib// Copyright(c) 2022 Intel Corporation. All rights reserved.
4235783Skib//
5235783Skib// Author: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
6235783Skib//
7235783Skib
8235783Skib#include <linux/auxiliary_bus.h>
9235783Skib#include <linux/completion.h>
10235783Skib#include <linux/debugfs.h>
11235783Skib#include <linux/ktime.h>
12235783Skib#include <linux/mod_devicetable.h>
13235783Skib#include <linux/module.h>
14235783Skib#include <linux/pm_runtime.h>
15235783Skib#include <linux/slab.h>
16235783Skib#include <linux/uaccess.h>
17235783Skib#include <sound/sof/header.h>
18235783Skib#include <sound/sof/ipc4/header.h>
19235783Skib
20235783Skib#include "sof-client.h"
21235783Skib
22235783Skib#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS	3000
23235783Skib
24235783Skibstruct sof_msg_inject_priv {
25235783Skib	struct dentry *dfs_file;
26235783Skib	size_t max_msg_size;
27296548Sdumbbell	enum sof_ipc_type ipc_type;
28296548Sdumbbell
29296548Sdumbbell	void *tx_buffer;
30296548Sdumbbell	void *rx_buffer;
31235783Skib};
32235783Skib
33235783Skibstatic int sof_msg_inject_dfs_open(struct inode *inode, struct file *file)
34235783Skib{
35235783Skib	struct sof_client_dev *cdev = inode->i_private;
36235783Skib	int ret;
37235783Skib
38235783Skib	if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
39235783Skib		return -ENODEV;
40235783Skib
41235783Skib	ret = debugfs_file_get(file->f_path.dentry);
42235783Skib	if (unlikely(ret))
43235783Skib		return ret;
44235783Skib
45235783Skib	ret = simple_open(inode, file);
46235783Skib	if (ret)
47235783Skib		debugfs_file_put(file->f_path.dentry);
48235783Skib
49235783Skib	return ret;
50235783Skib}
51235783Skib
52235783Skibstatic ssize_t sof_msg_inject_dfs_read(struct file *file, char __user *buffer,
53235783Skib				       size_t count, loff_t *ppos)
54235783Skib{
55235783Skib	struct sof_client_dev *cdev = file->private_data;
56235783Skib	struct sof_msg_inject_priv *priv = cdev->data;
57235783Skib	struct sof_ipc_reply *rhdr = priv->rx_buffer;
58235783Skib
59235783Skib	if (!rhdr->hdr.size || !count || *ppos)
60235783Skib		return 0;
61235783Skib
62235783Skib	if (count > rhdr->hdr.size)
63235783Skib		count = rhdr->hdr.size;
64235783Skib
65235783Skib	if (copy_to_user(buffer, priv->rx_buffer, count))
66235783Skib		return -EFAULT;
67235783Skib
68235783Skib	*ppos += count;
69235783Skib	return count;
70235783Skib}
71235783Skib
72235783Skibstatic ssize_t sof_msg_inject_ipc4_dfs_read(struct file *file,
73235783Skib					    char __user *buffer,
74235783Skib					    size_t count, loff_t *ppos)
75235783Skib{
76235783Skib	struct sof_client_dev *cdev = file->private_data;
77235783Skib	struct sof_msg_inject_priv *priv = cdev->data;
78235783Skib	struct sof_ipc4_msg *ipc4_msg = priv->rx_buffer;
79235783Skib	size_t header_size = sizeof(ipc4_msg->header_u64);
80235783Skib	size_t remaining;
81235783Skib
82235783Skib	if (!ipc4_msg->header_u64 || !count || *ppos)
83235783Skib		return 0;
84235783Skib
85235783Skib	/* we need space for the header at minimum (u64) */
86235783Skib	if (count < header_size)
87235783Skib		return -ENOSPC;
88235783Skib
89235783Skib	remaining = header_size;
90235783Skib
91235783Skib	/* Only get large config have payload */
92235783Skib	if (SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_msg->primary) &&
93235783Skib	    (SOF_IPC4_MSG_TYPE_GET(ipc4_msg->primary) == SOF_IPC4_MOD_LARGE_CONFIG_GET))
94235783Skib		remaining += ipc4_msg->data_size;
95235783Skib
96235783Skib	if (count > remaining)
97235783Skib		count = remaining;
98235783Skib	else if (count < remaining)
99235783Skib		remaining = count;
100235783Skib
101235783Skib	/* copy the header first */
102235783Skib	if (copy_to_user(buffer, &ipc4_msg->header_u64, header_size))
103235783Skib		return -EFAULT;
104235783Skib
105235783Skib	*ppos += header_size;
106235783Skib	remaining -= header_size;
107235783Skib
108235783Skib	if (!remaining)
109235783Skib		return count;
110235783Skib
111235783Skib	if (remaining > ipc4_msg->data_size)
112235783Skib		remaining = ipc4_msg->data_size;
113235783Skib
114235783Skib	/* Copy the payload */
115235783Skib	if (copy_to_user(buffer + *ppos, ipc4_msg->data_ptr, remaining))
116235783Skib		return -EFAULT;
117235783Skib
118235783Skib	*ppos += remaining;
119235783Skib	return count;
120235783Skib}
121235783Skib
122235783Skibstatic int sof_msg_inject_send_message(struct sof_client_dev *cdev)
123235783Skib{
124235783Skib	struct sof_msg_inject_priv *priv = cdev->data;
125235783Skib	struct device *dev = &cdev->auxdev.dev;
126235783Skib	int ret, err;
127235783Skib
128235783Skib	ret = pm_runtime_resume_and_get(dev);
129235783Skib	if (ret < 0 && ret != -EACCES) {
130235783Skib		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
131235783Skib		return ret;
132235783Skib	}
133235783Skib
134235783Skib	/* send the message */
135235783Skib	ret = sof_client_ipc_tx_message(cdev, priv->tx_buffer, priv->rx_buffer,
136235783Skib					priv->max_msg_size);
137235783Skib	if (ret)
138235783Skib		dev_err(dev, "IPC message send failed: %d\n", ret);
139235783Skib
140235783Skib	pm_runtime_mark_last_busy(dev);
141235783Skib	err = pm_runtime_put_autosuspend(dev);
142235783Skib	if (err < 0)
143235783Skib		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
144235783Skib
145235783Skib	return ret;
146235783Skib}
147235783Skib
148235783Skibstatic ssize_t sof_msg_inject_dfs_write(struct file *file, const char __user *buffer,
149235783Skib					size_t count, loff_t *ppos)
150235783Skib{
151235783Skib	struct sof_client_dev *cdev = file->private_data;
152235783Skib	struct sof_msg_inject_priv *priv = cdev->data;
153235783Skib	ssize_t size;
154235783Skib	int ret;
155235783Skib
156235783Skib	if (*ppos)
157235783Skib		return 0;
158235783Skib
159235783Skib	size = simple_write_to_buffer(priv->tx_buffer, priv->max_msg_size,
160235783Skib				      ppos, buffer, count);
161235783Skib	if (size < 0)
162235783Skib		return size;
163235783Skib	if (size != count)
164235783Skib		return -EFAULT;
165235783Skib
166235783Skib	memset(priv->rx_buffer, 0, priv->max_msg_size);
167235783Skib
168235783Skib	ret = sof_msg_inject_send_message(cdev);
169235783Skib
170235783Skib	/* return the error code if test failed */
171235783Skib	if (ret < 0)
172235783Skib		size = ret;
173235783Skib
174296548Sdumbbell	return size;
175235783Skib};
176235783Skib
177235783Skibstatic ssize_t sof_msg_inject_ipc4_dfs_write(struct file *file,
178235783Skib					     const char __user *buffer,
179277487Skib					     size_t count, loff_t *ppos)
180277487Skib{
181277487Skib	struct sof_client_dev *cdev = file->private_data;
182277487Skib	struct sof_msg_inject_priv *priv = cdev->data;
183277487Skib	struct sof_ipc4_msg *ipc4_msg = priv->tx_buffer;
184277487Skib	size_t data_size;
185277487Skib	int ret;
186277487Skib
187277487Skib	if (*ppos)
188277487Skib		return 0;
189277487Skib
190277487Skib	if (count < sizeof(ipc4_msg->header_u64))
191277487Skib		return -EINVAL;
192296548Sdumbbell
193277487Skib	/* copy the header first */
194277487Skib	if (copy_from_user(&ipc4_msg->header_u64, buffer,
195277487Skib			   sizeof(ipc4_msg->header_u64)))
196277487Skib		return -EFAULT;
197277487Skib
198277487Skib	data_size = count - sizeof(ipc4_msg->header_u64);
199277487Skib	if (data_size > priv->max_msg_size)
200277487Skib		return -EINVAL;
201235783Skib
202235783Skib	/* Copy the payload */
203235783Skib	if (copy_from_user(ipc4_msg->data_ptr,
204235783Skib			   buffer + sizeof(ipc4_msg->header_u64), data_size))
205235783Skib		return -EFAULT;
206235783Skib
207235783Skib	ipc4_msg->data_size = data_size;
208235783Skib
209235783Skib	/* Initialize the reply storage */
210277487Skib	ipc4_msg = priv->rx_buffer;
211235783Skib	ipc4_msg->header_u64 = 0;
212235783Skib	ipc4_msg->data_size = priv->max_msg_size;
213235783Skib	memset(ipc4_msg->data_ptr, 0, priv->max_msg_size);
214235783Skib
215235783Skib	ret = sof_msg_inject_send_message(cdev);
216235783Skib
217235783Skib	/* return the error code if test failed */
218235783Skib	if (ret < 0)
219235783Skib		return ret;
220235783Skib
221235783Skib	return count;
222235783Skib};
223235783Skib
224235783Skibstatic int sof_msg_inject_dfs_release(struct inode *inode, struct file *file)
225235783Skib{
226235783Skib	debugfs_file_put(file->f_path.dentry);
227235783Skib
228235783Skib	return 0;
229235783Skib}
230235783Skib
231235783Skibstatic const struct file_operations sof_msg_inject_fops = {
232235783Skib	.open = sof_msg_inject_dfs_open,
233235783Skib	.read = sof_msg_inject_dfs_read,
234235783Skib	.write = sof_msg_inject_dfs_write,
235235783Skib	.llseek = default_llseek,
236235783Skib	.release = sof_msg_inject_dfs_release,
237235783Skib
238296548Sdumbbell	.owner = THIS_MODULE,
239296548Sdumbbell};
240296548Sdumbbell
241235783Skibstatic const struct file_operations sof_msg_inject_ipc4_fops = {
242235783Skib	.open = sof_msg_inject_dfs_open,
243235783Skib	.read = sof_msg_inject_ipc4_dfs_read,
244235783Skib	.write = sof_msg_inject_ipc4_dfs_write,
245235783Skib	.llseek = default_llseek,
246235783Skib	.release = sof_msg_inject_dfs_release,
247235783Skib
248235783Skib	.owner = THIS_MODULE,
249235783Skib};
250235783Skib
251235783Skibstatic int sof_msg_inject_probe(struct auxiliary_device *auxdev,
252235783Skib				const struct auxiliary_device_id *id)
253235783Skib{
254235783Skib	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
255235783Skib	struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev);
256235783Skib	static const struct file_operations *fops;
257235783Skib	struct device *dev = &auxdev->dev;
258235783Skib	struct sof_msg_inject_priv *priv;
259235783Skib	size_t alloc_size;
260235783Skib
261235783Skib	/* allocate memory for client data */
262235783Skib	priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
263235783Skib	if (!priv)
264235783Skib		return -ENOMEM;
265235783Skib
266235783Skib	priv->ipc_type = sof_client_get_ipc_type(cdev);
267235783Skib	priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
268296548Sdumbbell	alloc_size = priv->max_msg_size;
269235783Skib
270296548Sdumbbell	if (priv->ipc_type == SOF_IPC_TYPE_4)
271235783Skib		alloc_size += sizeof(struct sof_ipc4_msg);
272277487Skib
273277487Skib	priv->tx_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL);
274277487Skib	priv->rx_buffer = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
275277487Skib	if (!priv->tx_buffer || !priv->rx_buffer)
276277487Skib		return -ENOMEM;
277277487Skib
278277487Skib	if (priv->ipc_type == SOF_IPC_TYPE_4) {
279277487Skib		struct sof_ipc4_msg *ipc4_msg;
280277487Skib
281277487Skib		ipc4_msg = priv->tx_buffer;
282277487Skib		ipc4_msg->data_ptr = priv->tx_buffer + sizeof(struct sof_ipc4_msg);
283277487Skib
284277487Skib		ipc4_msg = priv->rx_buffer;
285235783Skib		ipc4_msg->data_ptr = priv->rx_buffer + sizeof(struct sof_ipc4_msg);
286235783Skib
287235783Skib		fops = &sof_msg_inject_ipc4_fops;
288235783Skib	} else {
289235783Skib		fops = &sof_msg_inject_fops;
290235783Skib	}
291235783Skib
292235783Skib	cdev->data = priv;
293235783Skib
294235783Skib	priv->dfs_file = debugfs_create_file("ipc_msg_inject", 0644, debugfs_root,
295235783Skib					     cdev, fops);
296235783Skib
297277487Skib	/* enable runtime PM */
298277487Skib	pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
299277487Skib	pm_runtime_use_autosuspend(dev);
300277487Skib	pm_runtime_enable(dev);
301277487Skib	pm_runtime_mark_last_busy(dev);
302235783Skib	pm_runtime_idle(dev);
303235783Skib
304235783Skib	return 0;
305235783Skib}
306235783Skib
307235783Skibstatic void sof_msg_inject_remove(struct auxiliary_device *auxdev)
308235783Skib{
309235783Skib	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
310235783Skib	struct sof_msg_inject_priv *priv = cdev->data;
311235783Skib
312235783Skib	pm_runtime_disable(&auxdev->dev);
313235783Skib
314235783Skib	debugfs_remove(priv->dfs_file);
315235783Skib}
316296548Sdumbbell
317296548Sdumbbellstatic const struct auxiliary_device_id sof_msg_inject_client_id_table[] = {
318296548Sdumbbell	{ .name = "snd_sof.msg_injector" },
319235783Skib	{},
320235783Skib};
321235783SkibMODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table);
322235783Skib
323235783Skib/*
324235783Skib * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus
325235783Skib * type are enough to ensure that the parent SOF device resumes to bring the DSP
326235783Skib * back to D0.
327235783Skib * Driver name will be set based on KBUILD_MODNAME.
328235783Skib */
329235783Skibstatic struct auxiliary_driver sof_msg_inject_client_drv = {
330235783Skib	.probe = sof_msg_inject_probe,
331235783Skib	.remove = sof_msg_inject_remove,
332235783Skib
333235783Skib	.id_table = sof_msg_inject_client_id_table,
334235783Skib};
335235783Skib
336235783Skibmodule_auxiliary_driver(sof_msg_inject_client_drv);
337235783Skib
338235783SkibMODULE_DESCRIPTION("SOF IPC Message Injector Client Driver");
339235783SkibMODULE_LICENSE("GPL");
340235783SkibMODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);
341235783Skib