cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sof-client-probes.c (21728B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// Copyright(c) 2019-2022 Intel Corporation. All rights reserved.
      4//
      5// Author: Cezary Rojewski <cezary.rojewski@intel.com>
      6//
      7// SOF client support:
      8//  Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
      9//  Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
     10//
     11
     12#include <linux/debugfs.h>
     13#include <linux/module.h>
     14#include <linux/pm_runtime.h>
     15#include <sound/soc.h>
     16#include <sound/sof/header.h>
     17#include "sof-client.h"
     18#include "sof-client-probes.h"
     19
     20#define SOF_PROBES_SUSPEND_DELAY_MS 3000
     21/* only extraction supported for now */
     22#define SOF_PROBES_NUM_DAI_LINKS 1
     23
     24#define SOF_PROBES_INVALID_NODE_ID UINT_MAX
     25
     26static bool __read_mostly sof_probes_enabled;
     27module_param_named(enable, sof_probes_enabled, bool, 0444);
     28MODULE_PARM_DESC(enable, "Enable SOF probes support");
     29
     30struct sof_probes_priv {
     31	struct dentry *dfs_points;
     32	struct dentry *dfs_points_remove;
     33	u32 extractor_stream_tag;
     34	struct snd_soc_card card;
     35
     36	const struct sof_probes_host_ops *host_ops;
     37};
     38
     39struct sof_probe_point_desc {
     40	unsigned int buffer_id;
     41	unsigned int purpose;
     42	unsigned int stream_tag;
     43} __packed;
     44
     45struct sof_probe_dma {
     46	unsigned int stream_tag;
     47	unsigned int dma_buffer_size;
     48} __packed;
     49
     50struct sof_ipc_probe_dma_add_params {
     51	struct sof_ipc_cmd_hdr hdr;
     52	unsigned int num_elems;
     53	struct sof_probe_dma dma[];
     54} __packed;
     55
     56struct sof_ipc_probe_info_params {
     57	struct sof_ipc_reply rhdr;
     58	unsigned int num_elems;
     59	union {
     60		struct sof_probe_dma dma[0];
     61		struct sof_probe_point_desc desc[0];
     62	};
     63} __packed;
     64
     65struct sof_ipc_probe_point_add_params {
     66	struct sof_ipc_cmd_hdr hdr;
     67	unsigned int num_elems;
     68	struct sof_probe_point_desc desc[];
     69} __packed;
     70
     71struct sof_ipc_probe_point_remove_params {
     72	struct sof_ipc_cmd_hdr hdr;
     73	unsigned int num_elems;
     74	unsigned int buffer_id[];
     75} __packed;
     76
     77/**
     78 * sof_probes_init - initialize data probing
     79 * @cdev:		SOF client device
     80 * @stream_tag:		Extractor stream tag
     81 * @buffer_size:	DMA buffer size to set for extractor
     82 *
     83 * Host chooses whether extraction is supported or not by providing
     84 * valid stream tag to DSP. Once specified, stream described by that
     85 * tag will be tied to DSP for extraction for the entire lifetime of
     86 * probe.
     87 *
     88 * Probing is initialized only once and each INIT request must be
     89 * matched by DEINIT call.
     90 */
     91static int sof_probes_init(struct sof_client_dev *cdev, u32 stream_tag,
     92			   size_t buffer_size)
     93{
     94	struct sof_ipc_probe_dma_add_params *msg;
     95	size_t size = struct_size(msg, dma, 1);
     96	struct sof_ipc_reply reply;
     97	int ret;
     98
     99	msg = kmalloc(size, GFP_KERNEL);
    100	if (!msg)
    101		return -ENOMEM;
    102	msg->hdr.size = size;
    103	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT;
    104	msg->num_elems = 1;
    105	msg->dma[0].stream_tag = stream_tag;
    106	msg->dma[0].dma_buffer_size = buffer_size;
    107
    108	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
    109	kfree(msg);
    110	return ret;
    111}
    112
    113/**
    114 * sof_probes_deinit - cleanup after data probing
    115 * @cdev:		SOF client device
    116 *
    117 * Host sends DEINIT request to free previously initialized probe
    118 * on DSP side once it is no longer needed. DEINIT only when there
    119 * are no probes connected and with all injectors detached.
    120 */
    121static int sof_probes_deinit(struct sof_client_dev *cdev)
    122{
    123	struct sof_ipc_cmd_hdr msg;
    124	struct sof_ipc_reply reply;
    125
    126	msg.size = sizeof(msg);
    127	msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT;
    128
    129	return sof_client_ipc_tx_message(cdev, &msg, &reply, sizeof(reply));
    130}
    131
    132static int sof_probes_info(struct sof_client_dev *cdev, unsigned int cmd,
    133			   void **params, size_t *num_params)
    134{
    135	size_t max_msg_size = sof_client_get_ipc_max_payload_size(cdev);
    136	struct sof_ipc_probe_info_params msg = {{{0}}};
    137	struct sof_ipc_probe_info_params *reply;
    138	size_t bytes;
    139	int ret;
    140
    141	*params = NULL;
    142	*num_params = 0;
    143
    144	reply = kzalloc(max_msg_size, GFP_KERNEL);
    145	if (!reply)
    146		return -ENOMEM;
    147	msg.rhdr.hdr.size = sizeof(msg);
    148	msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd;
    149
    150	ret = sof_client_ipc_tx_message(cdev, &msg, reply, max_msg_size);
    151	if (ret < 0 || reply->rhdr.error < 0)
    152		goto exit;
    153
    154	if (!reply->num_elems)
    155		goto exit;
    156
    157	if (cmd == SOF_IPC_PROBE_DMA_INFO)
    158		bytes = sizeof(reply->dma[0]);
    159	else
    160		bytes = sizeof(reply->desc[0]);
    161	bytes *= reply->num_elems;
    162	*params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL);
    163	if (!*params) {
    164		ret = -ENOMEM;
    165		goto exit;
    166	}
    167	*num_params = reply->num_elems;
    168
    169exit:
    170	kfree(reply);
    171	return ret;
    172}
    173
    174/**
    175 * sof_probes_points_info - retrieve list of active probe points
    176 * @cdev:		SOF client device
    177 * @desc:	Returned list of active probes
    178 * @num_desc:	Returned count of active probes
    179 *
    180 * Host sends PROBE_POINT_INFO request to obtain list of active probe
    181 * points, valid for disconnection when given probe is no longer
    182 * required.
    183 */
    184static int sof_probes_points_info(struct sof_client_dev *cdev,
    185				  struct sof_probe_point_desc **desc,
    186				  size_t *num_desc)
    187{
    188	return sof_probes_info(cdev, SOF_IPC_PROBE_POINT_INFO,
    189			       (void **)desc, num_desc);
    190}
    191
    192/**
    193 * sof_probes_points_add - connect specified probes
    194 * @cdev:		SOF client device
    195 * @desc:	List of probe points to connect
    196 * @num_desc:	Number of elements in @desc
    197 *
    198 * Dynamically connects to provided set of endpoints. Immediately
    199 * after connection is established, host must be prepared to
    200 * transfer data from or to target stream given the probing purpose.
    201 *
    202 * Each probe point should be removed using PROBE_POINT_REMOVE
    203 * request when no longer needed.
    204 */
    205static int sof_probes_points_add(struct sof_client_dev *cdev,
    206				 struct sof_probe_point_desc *desc,
    207				 size_t num_desc)
    208{
    209	struct sof_ipc_probe_point_add_params *msg;
    210	size_t size = struct_size(msg, desc, num_desc);
    211	struct sof_ipc_reply reply;
    212	int ret;
    213
    214	msg = kmalloc(size, GFP_KERNEL);
    215	if (!msg)
    216		return -ENOMEM;
    217	msg->hdr.size = size;
    218	msg->num_elems = num_desc;
    219	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD;
    220	memcpy(&msg->desc[0], desc, size - sizeof(*msg));
    221
    222	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
    223	kfree(msg);
    224	return ret;
    225}
    226
    227/**
    228 * sof_probes_points_remove - disconnect specified probes
    229 * @cdev:		SOF client device
    230 * @buffer_id:		List of probe points to disconnect
    231 * @num_buffer_id:	Number of elements in @desc
    232 *
    233 * Removes previously connected probes from list of active probe
    234 * points and frees all resources on DSP side.
    235 */
    236static int sof_probes_points_remove(struct sof_client_dev *cdev,
    237				    unsigned int *buffer_id, size_t num_buffer_id)
    238{
    239	struct sof_ipc_probe_point_remove_params *msg;
    240	size_t size = struct_size(msg, buffer_id, num_buffer_id);
    241	struct sof_ipc_reply reply;
    242	int ret;
    243
    244	msg = kmalloc(size, GFP_KERNEL);
    245	if (!msg)
    246		return -ENOMEM;
    247	msg->hdr.size = size;
    248	msg->num_elems = num_buffer_id;
    249	msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE;
    250	memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg));
    251
    252	ret = sof_client_ipc_tx_message(cdev, msg, &reply, sizeof(reply));
    253	kfree(msg);
    254	return ret;
    255}
    256
    257static int sof_probes_compr_startup(struct snd_compr_stream *cstream,
    258				    struct snd_soc_dai *dai)
    259{
    260	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
    261	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
    262	struct sof_probes_priv *priv = cdev->data;
    263	const struct sof_probes_host_ops *ops = priv->host_ops;
    264	int ret;
    265
    266	if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED)
    267		return -ENODEV;
    268
    269	ret = sof_client_core_module_get(cdev);
    270	if (ret)
    271		return ret;
    272
    273	ret = ops->assign(cdev, cstream, dai, &priv->extractor_stream_tag);
    274	if (ret) {
    275		dev_err(dai->dev, "Failed to assign probe stream: %d\n", ret);
    276		priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
    277		sof_client_core_module_put(cdev);
    278	}
    279
    280	return ret;
    281}
    282
    283static int sof_probes_compr_shutdown(struct snd_compr_stream *cstream,
    284				     struct snd_soc_dai *dai)
    285{
    286	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
    287	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
    288	struct sof_probes_priv *priv = cdev->data;
    289	const struct sof_probes_host_ops *ops = priv->host_ops;
    290	struct sof_probe_point_desc *desc;
    291	size_t num_desc;
    292	int i, ret;
    293
    294	/* disconnect all probe points */
    295	ret = sof_probes_points_info(cdev, &desc, &num_desc);
    296	if (ret < 0) {
    297		dev_err(dai->dev, "Failed to get probe points: %d\n", ret);
    298		goto exit;
    299	}
    300
    301	for (i = 0; i < num_desc; i++)
    302		sof_probes_points_remove(cdev, &desc[i].buffer_id, 1);
    303	kfree(desc);
    304
    305exit:
    306	ret = sof_probes_deinit(cdev);
    307	if (ret < 0)
    308		dev_err(dai->dev, "Failed to deinit probe: %d\n", ret);
    309
    310	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
    311	snd_compr_free_pages(cstream);
    312
    313	ret = ops->free(cdev, cstream, dai);
    314
    315	sof_client_core_module_put(cdev);
    316
    317	return ret;
    318}
    319
    320static int sof_probes_compr_set_params(struct snd_compr_stream *cstream,
    321				       struct snd_compr_params *params,
    322				       struct snd_soc_dai *dai)
    323{
    324	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
    325	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
    326	struct snd_compr_runtime *rtd = cstream->runtime;
    327	struct sof_probes_priv *priv = cdev->data;
    328	const struct sof_probes_host_ops *ops = priv->host_ops;
    329	int ret;
    330
    331	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
    332	cstream->dma_buffer.dev.dev = sof_client_get_dma_dev(cdev);
    333	ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
    334	if (ret < 0)
    335		return ret;
    336
    337	ret = ops->set_params(cdev, cstream, params, dai);
    338	if (ret)
    339		return ret;
    340
    341	ret = sof_probes_init(cdev, priv->extractor_stream_tag, rtd->dma_bytes);
    342	if (ret < 0) {
    343		dev_err(dai->dev, "Failed to init probe: %d\n", ret);
    344		return ret;
    345	}
    346
    347	return 0;
    348}
    349
    350static int sof_probes_compr_trigger(struct snd_compr_stream *cstream, int cmd,
    351				    struct snd_soc_dai *dai)
    352{
    353	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
    354	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
    355	struct sof_probes_priv *priv = cdev->data;
    356	const struct sof_probes_host_ops *ops = priv->host_ops;
    357
    358	return ops->trigger(cdev, cstream, cmd, dai);
    359}
    360
    361static int sof_probes_compr_pointer(struct snd_compr_stream *cstream,
    362				    struct snd_compr_tstamp *tstamp,
    363				    struct snd_soc_dai *dai)
    364{
    365	struct snd_soc_card *card = snd_soc_component_get_drvdata(dai->component);
    366	struct sof_client_dev *cdev = snd_soc_card_get_drvdata(card);
    367	struct sof_probes_priv *priv = cdev->data;
    368	const struct sof_probes_host_ops *ops = priv->host_ops;
    369
    370	return ops->pointer(cdev, cstream, tstamp, dai);
    371}
    372
    373static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
    374	.startup = sof_probes_compr_startup,
    375	.shutdown = sof_probes_compr_shutdown,
    376	.set_params = sof_probes_compr_set_params,
    377	.trigger = sof_probes_compr_trigger,
    378	.pointer = sof_probes_compr_pointer,
    379};
    380
    381static int sof_probes_compr_copy(struct snd_soc_component *component,
    382				 struct snd_compr_stream *cstream,
    383				 char __user *buf, size_t count)
    384{
    385	struct snd_compr_runtime *rtd = cstream->runtime;
    386	unsigned int offset, n;
    387	void *ptr;
    388	int ret;
    389
    390	if (count > rtd->buffer_size)
    391		count = rtd->buffer_size;
    392
    393	div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
    394	ptr = rtd->dma_area + offset;
    395	n = rtd->buffer_size - offset;
    396
    397	if (count < n) {
    398		ret = copy_to_user(buf, ptr, count);
    399	} else {
    400		ret = copy_to_user(buf, ptr, n);
    401		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
    402	}
    403
    404	if (ret)
    405		return count - ret;
    406	return count;
    407}
    408
    409static const struct snd_compress_ops sof_probes_compressed_ops = {
    410	.copy = sof_probes_compr_copy,
    411};
    412
    413/**
    414 * strsplit_u32 - Split string into sequence of u32 tokens
    415 * @buf:	String to split into tokens.
    416 * @delim:	String containing delimiter characters.
    417 * @tkns:	Returned u32 sequence pointer.
    418 * @num_tkns:	Returned number of tokens obtained.
    419 */
    420static int strsplit_u32(char *buf, const char *delim, u32 **tkns, size_t *num_tkns)
    421{
    422	char *s;
    423	u32 *data, *tmp;
    424	size_t count = 0;
    425	size_t cap = 32;
    426	int ret = 0;
    427
    428	*tkns = NULL;
    429	*num_tkns = 0;
    430	data = kcalloc(cap, sizeof(*data), GFP_KERNEL);
    431	if (!data)
    432		return -ENOMEM;
    433
    434	while ((s = strsep(&buf, delim)) != NULL) {
    435		ret = kstrtouint(s, 0, data + count);
    436		if (ret)
    437			goto exit;
    438		if (++count >= cap) {
    439			cap *= 2;
    440			tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL);
    441			if (!tmp) {
    442				ret = -ENOMEM;
    443				goto exit;
    444			}
    445			data = tmp;
    446		}
    447	}
    448
    449	if (!count)
    450		goto exit;
    451	*tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL);
    452	if (!(*tkns)) {
    453		ret = -ENOMEM;
    454		goto exit;
    455	}
    456	*num_tkns = count;
    457
    458exit:
    459	kfree(data);
    460	return ret;
    461}
    462
    463static int tokenize_input(const char __user *from, size_t count,
    464			  loff_t *ppos, u32 **tkns, size_t *num_tkns)
    465{
    466	char *buf;
    467	int ret;
    468
    469	buf = kmalloc(count + 1, GFP_KERNEL);
    470	if (!buf)
    471		return -ENOMEM;
    472
    473	ret = simple_write_to_buffer(buf, count, ppos, from, count);
    474	if (ret != count) {
    475		ret = ret >= 0 ? -EIO : ret;
    476		goto exit;
    477	}
    478
    479	buf[count] = '\0';
    480	ret = strsplit_u32(buf, ",", tkns, num_tkns);
    481exit:
    482	kfree(buf);
    483	return ret;
    484}
    485
    486static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
    487					  size_t count, loff_t *ppos)
    488{
    489	struct sof_client_dev *cdev = file->private_data;
    490	struct sof_probes_priv *priv = cdev->data;
    491	struct device *dev = &cdev->auxdev.dev;
    492	struct sof_probe_point_desc *desc;
    493	int remaining, offset;
    494	size_t num_desc;
    495	char *buf;
    496	int i, ret, err;
    497
    498	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
    499		dev_warn(dev, "no extractor stream running\n");
    500		return -ENOENT;
    501	}
    502
    503	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
    504	if (!buf)
    505		return -ENOMEM;
    506
    507	ret = pm_runtime_resume_and_get(dev);
    508	if (ret < 0 && ret != -EACCES) {
    509		dev_err_ratelimited(dev, "debugfs read failed to resume %d\n", ret);
    510		goto exit;
    511	}
    512
    513	ret = sof_probes_points_info(cdev, &desc, &num_desc);
    514	if (ret < 0)
    515		goto exit;
    516
    517	pm_runtime_mark_last_busy(dev);
    518	err = pm_runtime_put_autosuspend(dev);
    519	if (err < 0)
    520		dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err);
    521
    522	for (i = 0; i < num_desc; i++) {
    523		offset = strlen(buf);
    524		remaining = PAGE_SIZE - offset;
    525		ret = snprintf(buf + offset, remaining,
    526			       "Id: %#010x  Purpose: %u  Node id: %#x\n",
    527				desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag);
    528		if (ret < 0 || ret >= remaining) {
    529			/* truncate the output buffer at the last full line */
    530			buf[offset] = '\0';
    531			break;
    532		}
    533	}
    534
    535	ret = simple_read_from_buffer(to, count, ppos, buf, strlen(buf));
    536
    537	kfree(desc);
    538exit:
    539	kfree(buf);
    540	return ret;
    541}
    542
    543static ssize_t
    544sof_probes_dfs_points_write(struct file *file, const char __user *from,
    545			    size_t count, loff_t *ppos)
    546{
    547	struct sof_client_dev *cdev = file->private_data;
    548	struct sof_probes_priv *priv = cdev->data;
    549	struct device *dev = &cdev->auxdev.dev;
    550	struct sof_probe_point_desc *desc;
    551	size_t num_tkns, bytes;
    552	u32 *tkns;
    553	int ret, err;
    554
    555	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
    556		dev_warn(dev, "no extractor stream running\n");
    557		return -ENOENT;
    558	}
    559
    560	ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
    561	if (ret < 0)
    562		return ret;
    563	bytes = sizeof(*tkns) * num_tkns;
    564	if (!num_tkns || (bytes % sizeof(*desc))) {
    565		ret = -EINVAL;
    566		goto exit;
    567	}
    568
    569	desc = (struct sof_probe_point_desc *)tkns;
    570
    571	ret = pm_runtime_resume_and_get(dev);
    572	if (ret < 0 && ret != -EACCES) {
    573		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
    574		goto exit;
    575	}
    576
    577	ret = sof_probes_points_add(cdev, desc, bytes / sizeof(*desc));
    578	if (!ret)
    579		ret = count;
    580
    581	pm_runtime_mark_last_busy(dev);
    582	err = pm_runtime_put_autosuspend(dev);
    583	if (err < 0)
    584		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
    585exit:
    586	kfree(tkns);
    587	return ret;
    588}
    589
    590static const struct file_operations sof_probes_points_fops = {
    591	.open = simple_open,
    592	.read = sof_probes_dfs_points_read,
    593	.write = sof_probes_dfs_points_write,
    594	.llseek = default_llseek,
    595
    596	.owner = THIS_MODULE,
    597};
    598
    599static ssize_t
    600sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
    601				   size_t count, loff_t *ppos)
    602{
    603	struct sof_client_dev *cdev = file->private_data;
    604	struct sof_probes_priv *priv = cdev->data;
    605	struct device *dev = &cdev->auxdev.dev;
    606	size_t num_tkns;
    607	u32 *tkns;
    608	int ret, err;
    609
    610	if (priv->extractor_stream_tag == SOF_PROBES_INVALID_NODE_ID) {
    611		dev_warn(dev, "no extractor stream running\n");
    612		return -ENOENT;
    613	}
    614
    615	ret = tokenize_input(from, count, ppos, &tkns, &num_tkns);
    616	if (ret < 0)
    617		return ret;
    618	if (!num_tkns) {
    619		ret = -EINVAL;
    620		goto exit;
    621	}
    622
    623	ret = pm_runtime_resume_and_get(dev);
    624	if (ret < 0) {
    625		dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret);
    626		goto exit;
    627	}
    628
    629	ret = sof_probes_points_remove(cdev, tkns, num_tkns);
    630	if (!ret)
    631		ret = count;
    632
    633	pm_runtime_mark_last_busy(dev);
    634	err = pm_runtime_put_autosuspend(dev);
    635	if (err < 0)
    636		dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", err);
    637exit:
    638	kfree(tkns);
    639	return ret;
    640}
    641
    642static const struct file_operations sof_probes_points_remove_fops = {
    643	.open = simple_open,
    644	.write = sof_probes_dfs_points_remove_write,
    645	.llseek = default_llseek,
    646
    647	.owner = THIS_MODULE,
    648};
    649
    650static struct snd_soc_dai_driver sof_probes_dai_drv[] = {
    651{
    652	.name = "Probe Extraction CPU DAI",
    653	.compress_new = snd_soc_new_compress,
    654	.cops = &sof_probes_compr_ops,
    655	.capture = {
    656		.stream_name = "Probe Extraction",
    657		.channels_min = 1,
    658		.channels_max = 8,
    659		.rates = SNDRV_PCM_RATE_48000,
    660		.rate_min = 48000,
    661		.rate_max = 48000,
    662	},
    663},
    664};
    665
    666static const struct snd_soc_component_driver sof_probes_component = {
    667	.name = "sof-probes-component",
    668	.compress_ops = &sof_probes_compressed_ops,
    669	.module_get_upon_open = 1,
    670};
    671
    672SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY()));
    673
    674static int sof_probes_client_probe(struct auxiliary_device *auxdev,
    675				   const struct auxiliary_device_id *id)
    676{
    677	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
    678	struct dentry *dfsroot = sof_client_get_debugfs_root(cdev);
    679	struct device *dev = &auxdev->dev;
    680	struct snd_soc_dai_link_component platform_component[] = {
    681		{
    682			.name = dev_name(dev),
    683		}
    684	};
    685	struct snd_soc_card *card;
    686	struct sof_probes_priv *priv;
    687	struct snd_soc_dai_link_component *cpus;
    688	struct sof_probes_host_ops *ops;
    689	struct snd_soc_dai_link *links;
    690	int ret;
    691
    692	/* do not set up the probes support if it is not enabled */
    693	if (!sof_probes_enabled)
    694		return -ENXIO;
    695
    696	if (!dev->platform_data) {
    697		dev_err(dev, "missing platform data\n");
    698		return -ENODEV;
    699	}
    700
    701	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    702	if (!priv)
    703		return -ENOMEM;
    704
    705	ops = dev->platform_data;
    706
    707	if (!ops->assign || !ops->free || !ops->set_params || !ops->trigger ||
    708	    !ops->pointer) {
    709		dev_err(dev, "missing platform callback(s)\n");
    710		return -ENODEV;
    711	}
    712
    713	priv->host_ops = ops;
    714	cdev->data = priv;
    715
    716	/* register probes component driver and dai */
    717	ret = devm_snd_soc_register_component(dev, &sof_probes_component,
    718					      sof_probes_dai_drv,
    719					      ARRAY_SIZE(sof_probes_dai_drv));
    720	if (ret < 0) {
    721		dev_err(dev, "failed to register SOF probes DAI driver %d\n", ret);
    722		return ret;
    723	}
    724
    725	/* set client data */
    726	priv->extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID;
    727
    728	/* create read-write probes_points debugfs entry */
    729	priv->dfs_points = debugfs_create_file("probe_points", 0644, dfsroot,
    730					       cdev, &sof_probes_points_fops);
    731
    732	/* create read-write probe_points_remove debugfs entry */
    733	priv->dfs_points_remove = debugfs_create_file("probe_points_remove", 0644,
    734						      dfsroot, cdev,
    735						      &sof_probes_points_remove_fops);
    736
    737	links = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*links), GFP_KERNEL);
    738	cpus = devm_kcalloc(dev, SOF_PROBES_NUM_DAI_LINKS, sizeof(*cpus), GFP_KERNEL);
    739	if (!links || !cpus) {
    740		debugfs_remove(priv->dfs_points);
    741		debugfs_remove(priv->dfs_points_remove);
    742		return -ENOMEM;
    743	}
    744
    745	/* extraction DAI link */
    746	links[0].name = "Compress Probe Capture";
    747	links[0].id = 0;
    748	links[0].cpus = &cpus[0];
    749	links[0].num_cpus = 1;
    750	links[0].cpus->dai_name = "Probe Extraction CPU DAI";
    751	links[0].codecs = dummy;
    752	links[0].num_codecs = 1;
    753	links[0].platforms = platform_component;
    754	links[0].num_platforms = ARRAY_SIZE(platform_component);
    755	links[0].nonatomic = 1;
    756
    757	card = &priv->card;
    758
    759	card->dev = dev;
    760	card->name = "sof-probes";
    761	card->owner = THIS_MODULE;
    762	card->num_links = SOF_PROBES_NUM_DAI_LINKS;
    763	card->dai_link = links;
    764
    765	/* set idle_bias_off to prevent the core from resuming the card->dev */
    766	card->dapm.idle_bias_off = true;
    767
    768	snd_soc_card_set_drvdata(card, cdev);
    769
    770	ret = devm_snd_soc_register_card(dev, card);
    771	if (ret < 0) {
    772		debugfs_remove(priv->dfs_points);
    773		debugfs_remove(priv->dfs_points_remove);
    774		dev_err(dev, "Probes card register failed %d\n", ret);
    775		return ret;
    776	}
    777
    778	/* enable runtime PM */
    779	pm_runtime_set_autosuspend_delay(dev, SOF_PROBES_SUSPEND_DELAY_MS);
    780	pm_runtime_use_autosuspend(dev);
    781	pm_runtime_enable(dev);
    782	pm_runtime_mark_last_busy(dev);
    783	pm_runtime_idle(dev);
    784
    785	return 0;
    786}
    787
    788static void sof_probes_client_remove(struct auxiliary_device *auxdev)
    789{
    790	struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
    791	struct sof_probes_priv *priv = cdev->data;
    792
    793	if (!sof_probes_enabled)
    794		return;
    795
    796	pm_runtime_disable(&auxdev->dev);
    797	debugfs_remove(priv->dfs_points);
    798	debugfs_remove(priv->dfs_points_remove);
    799}
    800
    801static const struct auxiliary_device_id sof_probes_client_id_table[] = {
    802	{ .name = "snd_sof.hda-probes", },
    803	{},
    804};
    805MODULE_DEVICE_TABLE(auxiliary, sof_probes_client_id_table);
    806
    807/* driver name will be set based on KBUILD_MODNAME */
    808static struct auxiliary_driver sof_probes_client_drv = {
    809	.probe = sof_probes_client_probe,
    810	.remove = sof_probes_client_remove,
    811
    812	.id_table = sof_probes_client_id_table,
    813};
    814
    815module_auxiliary_driver(sof_probes_client_drv);
    816
    817MODULE_DESCRIPTION("SOF Probes Client Driver");
    818MODULE_LICENSE("GPL v2");
    819MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);