| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 |
|
| 8 | #include <linux/auxiliary_bus.h>
|
| 9 | #include <linux/debugfs.h>
|
| 10 | #include <linux/module.h>
|
| 11 | #include <linux/pm_runtime.h>
|
| 12 | #include <sound/sof/header.h>
|
| 13 |
|
| 14 | #include "sof-client.h"
|
| 15 |
|
| 16 | #define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000
|
| 17 |
|
| 18 | struct sof_msg_inject_priv {
|
| 19 | struct dentry *kernel_dfs_file;
|
| 20 | size_t max_msg_size;
|
| 21 |
|
| 22 | void *kernel_buffer;
|
| 23 | };
|
| 24 |
|
| 25 | static int sof_msg_inject_dfs_open(struct inode *inode, struct file *file)
|
| 26 | {
|
| 27 | int ret = debugfs_file_get(file->f_path.dentry);
|
| 28 |
|
| 29 | if (unlikely(ret))
|
| 30 | return ret;
|
| 31 |
|
| 32 | ret = simple_open(inode, file);
|
| 33 | if (ret)
|
| 34 | debugfs_file_put(file->f_path.dentry);
|
| 35 |
|
| 36 | return ret;
|
| 37 | }
|
| 38 |
|
| 39 | static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __user *buffer,
|
| 40 | size_t count, loff_t *ppos)
|
| 41 | {
|
| 42 | struct sof_client_dev *cdev = file->private_data;
|
| 43 | struct sof_msg_inject_priv *priv = cdev->data;
|
| 44 | struct sof_ipc_cmd_hdr *hdr = priv->kernel_buffer;
|
| 45 | struct device *dev = &cdev->auxdev.dev;
|
| 46 | ssize_t size;
|
| 47 | int ret;
|
| 48 |
|
| 49 | if (*ppos)
|
| 50 | return 0;
|
| 51 |
|
| 52 | size = simple_write_to_buffer(priv->kernel_buffer, priv->max_msg_size,
|
| 53 | ppos, buffer, count);
|
| 54 | if (size < 0)
|
| 55 | return size;
|
| 56 | if (size != count)
|
| 57 | return -EFAULT;
|
| 58 |
|
| 59 | ret = pm_runtime_resume_and_get(dev);
|
| 60 | if (ret < 0 && ret != -EACCES) {
|
| 61 | dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
|
| 62 | return ret;
|
| 63 | }
|
| 64 |
|
| 65 | ret = sof_client_boot_dsp(cdev);
|
| 66 | if (!ret)
|
| 67 | sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer);
|
| 68 |
|
| 69 | ret = pm_runtime_put_autosuspend(dev);
|
| 70 | if (ret < 0)
|
| 71 | dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", ret);
|
| 72 |
|
| 73 | return count;
|
| 74 | };
|
| 75 |
|
| 76 | static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file)
|
| 77 | {
|
| 78 | debugfs_file_put(file->f_path.dentry);
|
| 79 |
|
| 80 | return 0;
|
| 81 | }
|
| 82 |
|
| 83 | static const struct file_operations sof_kernel_msg_inject_fops = {
|
| 84 | .open = sof_msg_inject_dfs_open,
|
| 85 | .write = sof_kernel_msg_inject_dfs_write,
|
| 86 | .release = sof_msg_inject_dfs_release,
|
| 87 |
|
| 88 | .owner = THIS_MODULE,
|
| 89 | };
|
| 90 |
|
| 91 | static int sof_msg_inject_probe(struct auxiliary_device *auxdev,
|
| 92 | const struct auxiliary_device_id *id)
|
| 93 | {
|
| 94 | struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
|
| 95 | struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev);
|
| 96 | struct device *dev = &auxdev->dev;
|
| 97 | struct sof_msg_inject_priv *priv;
|
| 98 | size_t alloc_size;
|
| 99 |
|
| 100 |
|
| 101 | priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
|
| 102 | if (!priv)
|
| 103 | return -ENOMEM;
|
| 104 |
|
| 105 | priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
|
| 106 | alloc_size = priv->max_msg_size;
|
| 107 | priv->kernel_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL);
|
| 108 |
|
| 109 | if (!priv->kernel_buffer)
|
| 110 | return -ENOMEM;
|
| 111 |
|
| 112 | cdev->data = priv;
|
| 113 |
|
| 114 | priv->kernel_dfs_file = debugfs_create_file("kernel_ipc_msg_inject", 0644,
|
| 115 | debugfs_root, cdev,
|
| 116 | &sof_kernel_msg_inject_fops);
|
| 117 |
|
| 118 |
|
| 119 | pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS);
|
| 120 | pm_runtime_use_autosuspend(dev);
|
| 121 | pm_runtime_set_active(dev);
|
| 122 | pm_runtime_enable(dev);
|
| 123 | pm_runtime_mark_last_busy(dev);
|
| 124 | pm_runtime_idle(dev);
|
| 125 |
|
| 126 | return 0;
|
| 127 | }
|
| 128 |
|
| 129 | static void sof_msg_inject_remove(struct auxiliary_device *auxdev)
|
| 130 | {
|
| 131 | struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
|
| 132 | struct sof_msg_inject_priv *priv = cdev->data;
|
| 133 |
|
| 134 | pm_runtime_disable(&auxdev->dev);
|
| 135 |
|
| 136 | debugfs_remove(priv->kernel_dfs_file);
|
| 137 | }
|
| 138 |
|
| 139 | static const struct auxiliary_device_id sof_msg_inject_client_id_table[] = {
|
| 140 | { .name = "snd_sof.kernel_injector" },
|
| 141 | {},
|
| 142 | };
|
| 143 | MODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table);
|
| 144 |
|
| 145 | |
| 146 | * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus
|
| 147 | * type are enough to ensure that the parent SOF device resumes to bring the DSP
|
| 148 | * back to D0.
|
| 149 | * Driver name will be set based on KBUILD_MODNAME.
|
| 150 |
|
| 151 | static struct auxiliary_driver sof_msg_inject_client_drv = {
|
| 152 | .probe = sof_msg_inject_probe,
|
| 153 | .remove = sof_msg_inject_remove,
|
| 154 |
|
| 155 | .id_table = sof_msg_inject_client_id_table,
|
| 156 | };
|
| 157 |
|
| 158 | module_auxiliary_driver(sof_msg_inject_client_drv);
|
| 159 |
|
| 160 | MODULE_LICENSE("GPL");
|
| 161 | MODULE_DESCRIPTION("SOF IPC Kernel Injector Client Driver");
|
| 162 | MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT");
|