xref: /openbmc/linux/sound/soc/sof/control.c (revision 95a32c98055f664f9b3f34c41e153d4dcedd0eff)
1c3078f53SLiam Girdwood // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2c3078f53SLiam Girdwood //
3c3078f53SLiam Girdwood // This file is provided under a dual BSD/GPLv2 license.  When using or
4c3078f53SLiam Girdwood // redistributing this file, you may do so under either license.
5c3078f53SLiam Girdwood //
6c3078f53SLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved.
7c3078f53SLiam Girdwood //
8c3078f53SLiam Girdwood // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9c3078f53SLiam Girdwood //
10c3078f53SLiam Girdwood 
11c3078f53SLiam Girdwood /* Mixer Controls */
12c3078f53SLiam Girdwood 
13c3078f53SLiam Girdwood #include <linux/pm_runtime.h>
14c3078f53SLiam Girdwood #include "sof-priv.h"
15c3078f53SLiam Girdwood 
16c3078f53SLiam Girdwood static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size)
17c3078f53SLiam Girdwood {
18c3078f53SLiam Girdwood 	if (value >= size)
19c3078f53SLiam Girdwood 		return volume_map[size - 1];
20c3078f53SLiam Girdwood 
21c3078f53SLiam Girdwood 	return volume_map[value];
22c3078f53SLiam Girdwood }
23c3078f53SLiam Girdwood 
24c3078f53SLiam Girdwood static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size)
25c3078f53SLiam Girdwood {
26c3078f53SLiam Girdwood 	int i;
27c3078f53SLiam Girdwood 
28c3078f53SLiam Girdwood 	for (i = 0; i < size; i++) {
29c3078f53SLiam Girdwood 		if (volume_map[i] >= value)
30c3078f53SLiam Girdwood 			return i;
31c3078f53SLiam Girdwood 	}
32c3078f53SLiam Girdwood 
33c3078f53SLiam Girdwood 	return i - 1;
34c3078f53SLiam Girdwood }
35c3078f53SLiam Girdwood 
36c3078f53SLiam Girdwood int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
37c3078f53SLiam Girdwood 		       struct snd_ctl_elem_value *ucontrol)
38c3078f53SLiam Girdwood {
39c3078f53SLiam Girdwood 	struct soc_mixer_control *sm =
40c3078f53SLiam Girdwood 		(struct soc_mixer_control *)kcontrol->private_value;
41c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = sm->dobj.private;
42c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
43c3078f53SLiam Girdwood 	unsigned int i, channels = scontrol->num_channels;
44c3078f53SLiam Girdwood 
45c3078f53SLiam Girdwood 	/* read back each channel */
46c3078f53SLiam Girdwood 	for (i = 0; i < channels; i++)
47c3078f53SLiam Girdwood 		ucontrol->value.integer.value[i] =
48c3078f53SLiam Girdwood 			ipc_to_mixer(cdata->chanv[i].value,
49c3078f53SLiam Girdwood 				     scontrol->volume_table, sm->max + 1);
50c3078f53SLiam Girdwood 
51c3078f53SLiam Girdwood 	return 0;
52c3078f53SLiam Girdwood }
53c3078f53SLiam Girdwood 
54c3078f53SLiam Girdwood int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
55c3078f53SLiam Girdwood 		       struct snd_ctl_elem_value *ucontrol)
56c3078f53SLiam Girdwood {
57c3078f53SLiam Girdwood 	struct soc_mixer_control *sm =
58c3078f53SLiam Girdwood 		(struct soc_mixer_control *)kcontrol->private_value;
59c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = sm->dobj.private;
60c3078f53SLiam Girdwood 	struct snd_sof_dev *sdev = scontrol->sdev;
61c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
62c3078f53SLiam Girdwood 	unsigned int i, channels = scontrol->num_channels;
63*95a32c98SDragos Tarcatu 	bool change = false;
64*95a32c98SDragos Tarcatu 	u32 value;
65c3078f53SLiam Girdwood 
66c3078f53SLiam Girdwood 	/* update each channel */
67c3078f53SLiam Girdwood 	for (i = 0; i < channels; i++) {
68*95a32c98SDragos Tarcatu 		value = mixer_to_ipc(ucontrol->value.integer.value[i],
69c3078f53SLiam Girdwood 				     scontrol->volume_table, sm->max + 1);
70*95a32c98SDragos Tarcatu 		change = change || (value != cdata->chanv[i].value);
71c3078f53SLiam Girdwood 		cdata->chanv[i].channel = i;
72*95a32c98SDragos Tarcatu 		cdata->chanv[i].value = value;
73c3078f53SLiam Girdwood 	}
74c3078f53SLiam Girdwood 
75c3078f53SLiam Girdwood 	/* notify DSP of mixer updates */
760c888babSBard Liao 	if (pm_runtime_active(sdev->dev))
77c3078f53SLiam Girdwood 		snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
78c3078f53SLiam Girdwood 					      SOF_IPC_COMP_SET_VALUE,
79c3078f53SLiam Girdwood 					      SOF_CTRL_TYPE_VALUE_CHAN_GET,
80c3078f53SLiam Girdwood 					      SOF_CTRL_CMD_VOLUME,
81c3078f53SLiam Girdwood 					      true);
82*95a32c98SDragos Tarcatu 	return change;
83c3078f53SLiam Girdwood }
84c3078f53SLiam Girdwood 
85c3078f53SLiam Girdwood int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
86c3078f53SLiam Girdwood 		       struct snd_ctl_elem_value *ucontrol)
87c3078f53SLiam Girdwood {
88c3078f53SLiam Girdwood 	struct soc_mixer_control *sm =
89c3078f53SLiam Girdwood 		(struct soc_mixer_control *)kcontrol->private_value;
90c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = sm->dobj.private;
91c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
92c3078f53SLiam Girdwood 	unsigned int i, channels = scontrol->num_channels;
93c3078f53SLiam Girdwood 
94c3078f53SLiam Girdwood 	/* read back each channel */
95c3078f53SLiam Girdwood 	for (i = 0; i < channels; i++)
96c3078f53SLiam Girdwood 		ucontrol->value.integer.value[i] = cdata->chanv[i].value;
97c3078f53SLiam Girdwood 
98c3078f53SLiam Girdwood 	return 0;
99c3078f53SLiam Girdwood }
100c3078f53SLiam Girdwood 
101c3078f53SLiam Girdwood int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
102c3078f53SLiam Girdwood 		       struct snd_ctl_elem_value *ucontrol)
103c3078f53SLiam Girdwood {
104c3078f53SLiam Girdwood 	struct soc_mixer_control *sm =
105c3078f53SLiam Girdwood 		(struct soc_mixer_control *)kcontrol->private_value;
106c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = sm->dobj.private;
107c3078f53SLiam Girdwood 	struct snd_sof_dev *sdev = scontrol->sdev;
108c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
109c3078f53SLiam Girdwood 	unsigned int i, channels = scontrol->num_channels;
110*95a32c98SDragos Tarcatu 	bool change = false;
111*95a32c98SDragos Tarcatu 	u32 value;
112c3078f53SLiam Girdwood 
113c3078f53SLiam Girdwood 	/* update each channel */
114c3078f53SLiam Girdwood 	for (i = 0; i < channels; i++) {
115*95a32c98SDragos Tarcatu 		value = ucontrol->value.integer.value[i];
116*95a32c98SDragos Tarcatu 		change = change || (value != cdata->chanv[i].value);
117c3078f53SLiam Girdwood 		cdata->chanv[i].channel = i;
118*95a32c98SDragos Tarcatu 		cdata->chanv[i].value = value;
119c3078f53SLiam Girdwood 	}
120c3078f53SLiam Girdwood 
121c3078f53SLiam Girdwood 	/* notify DSP of mixer updates */
1220c888babSBard Liao 	if (pm_runtime_active(sdev->dev))
123c3078f53SLiam Girdwood 		snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
124c3078f53SLiam Girdwood 					      SOF_IPC_COMP_SET_VALUE,
125c3078f53SLiam Girdwood 					      SOF_CTRL_TYPE_VALUE_CHAN_GET,
126c3078f53SLiam Girdwood 					      SOF_CTRL_CMD_SWITCH,
127c3078f53SLiam Girdwood 					      true);
128c3078f53SLiam Girdwood 
129*95a32c98SDragos Tarcatu 	return change;
130c3078f53SLiam Girdwood }
131c3078f53SLiam Girdwood 
132c3078f53SLiam Girdwood int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
133c3078f53SLiam Girdwood 		     struct snd_ctl_elem_value *ucontrol)
134c3078f53SLiam Girdwood {
135c3078f53SLiam Girdwood 	struct soc_enum *se =
136c3078f53SLiam Girdwood 		(struct soc_enum *)kcontrol->private_value;
137c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = se->dobj.private;
138c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
139c3078f53SLiam Girdwood 	unsigned int i, channels = scontrol->num_channels;
140c3078f53SLiam Girdwood 
141c3078f53SLiam Girdwood 	/* read back each channel */
142c3078f53SLiam Girdwood 	for (i = 0; i < channels; i++)
143c3078f53SLiam Girdwood 		ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
144c3078f53SLiam Girdwood 
145c3078f53SLiam Girdwood 	return 0;
146c3078f53SLiam Girdwood }
147c3078f53SLiam Girdwood 
148c3078f53SLiam Girdwood int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
149c3078f53SLiam Girdwood 		     struct snd_ctl_elem_value *ucontrol)
150c3078f53SLiam Girdwood {
151c3078f53SLiam Girdwood 	struct soc_enum *se =
152c3078f53SLiam Girdwood 		(struct soc_enum *)kcontrol->private_value;
153c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = se->dobj.private;
154c3078f53SLiam Girdwood 	struct snd_sof_dev *sdev = scontrol->sdev;
155c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
156c3078f53SLiam Girdwood 	unsigned int i, channels = scontrol->num_channels;
157*95a32c98SDragos Tarcatu 	bool change = false;
158*95a32c98SDragos Tarcatu 	u32 value;
159c3078f53SLiam Girdwood 
160c3078f53SLiam Girdwood 	/* update each channel */
161c3078f53SLiam Girdwood 	for (i = 0; i < channels; i++) {
162*95a32c98SDragos Tarcatu 		value = ucontrol->value.enumerated.item[i];
163*95a32c98SDragos Tarcatu 		change = change || (value != cdata->chanv[i].value);
164c3078f53SLiam Girdwood 		cdata->chanv[i].channel = i;
165*95a32c98SDragos Tarcatu 		cdata->chanv[i].value = value;
166c3078f53SLiam Girdwood 	}
167c3078f53SLiam Girdwood 
168c3078f53SLiam Girdwood 	/* notify DSP of enum updates */
1690c888babSBard Liao 	if (pm_runtime_active(sdev->dev))
170c3078f53SLiam Girdwood 		snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
171c3078f53SLiam Girdwood 					      SOF_IPC_COMP_SET_VALUE,
172c3078f53SLiam Girdwood 					      SOF_CTRL_TYPE_VALUE_CHAN_GET,
173c3078f53SLiam Girdwood 					      SOF_CTRL_CMD_ENUM,
174c3078f53SLiam Girdwood 					      true);
175c3078f53SLiam Girdwood 
176*95a32c98SDragos Tarcatu 	return change;
177c3078f53SLiam Girdwood }
178c3078f53SLiam Girdwood 
179c3078f53SLiam Girdwood int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
180c3078f53SLiam Girdwood 		      struct snd_ctl_elem_value *ucontrol)
181c3078f53SLiam Girdwood {
182c3078f53SLiam Girdwood 	struct soc_bytes_ext *be =
183c3078f53SLiam Girdwood 		(struct soc_bytes_ext *)kcontrol->private_value;
184c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = be->dobj.private;
185c3078f53SLiam Girdwood 	struct snd_sof_dev *sdev = scontrol->sdev;
186c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
187c3078f53SLiam Girdwood 	struct sof_abi_hdr *data = cdata->data;
188c3078f53SLiam Girdwood 	size_t size;
1890c888babSBard Liao 	int ret = 0;
190c3078f53SLiam Girdwood 
191c3078f53SLiam Girdwood 	if (be->max > sizeof(ucontrol->value.bytes.data)) {
192c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev,
193c3078f53SLiam Girdwood 				    "error: data max %d exceeds ucontrol data array size\n",
194c3078f53SLiam Girdwood 				    be->max);
195c3078f53SLiam Girdwood 		return -EINVAL;
196c3078f53SLiam Girdwood 	}
197c3078f53SLiam Girdwood 
198c3078f53SLiam Girdwood 	size = data->size + sizeof(*data);
199c3078f53SLiam Girdwood 	if (size > be->max) {
200c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev,
201c3078f53SLiam Girdwood 				    "error: DSP sent %zu bytes max is %d\n",
202c3078f53SLiam Girdwood 				    size, be->max);
203c3078f53SLiam Girdwood 		ret = -EINVAL;
204c3078f53SLiam Girdwood 		goto out;
205c3078f53SLiam Girdwood 	}
206c3078f53SLiam Girdwood 
207c3078f53SLiam Girdwood 	/* copy back to kcontrol */
208c3078f53SLiam Girdwood 	memcpy(ucontrol->value.bytes.data, data, size);
209c3078f53SLiam Girdwood 
210c3078f53SLiam Girdwood out:
211c3078f53SLiam Girdwood 	return ret;
212c3078f53SLiam Girdwood }
213c3078f53SLiam Girdwood 
214c3078f53SLiam Girdwood int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
215c3078f53SLiam Girdwood 		      struct snd_ctl_elem_value *ucontrol)
216c3078f53SLiam Girdwood {
217c3078f53SLiam Girdwood 	struct soc_bytes_ext *be =
218c3078f53SLiam Girdwood 		(struct soc_bytes_ext *)kcontrol->private_value;
219c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = be->dobj.private;
220c3078f53SLiam Girdwood 	struct snd_sof_dev *sdev = scontrol->sdev;
221c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
222c3078f53SLiam Girdwood 	struct sof_abi_hdr *data = cdata->data;
2235661ad94SKeyon Jie 	size_t size = data->size + sizeof(*data);
224c3078f53SLiam Girdwood 
225c3078f53SLiam Girdwood 	if (be->max > sizeof(ucontrol->value.bytes.data)) {
226c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev,
227c3078f53SLiam Girdwood 				    "error: data max %d exceeds ucontrol data array size\n",
228c3078f53SLiam Girdwood 				    be->max);
229c3078f53SLiam Girdwood 		return -EINVAL;
230c3078f53SLiam Girdwood 	}
231c3078f53SLiam Girdwood 
2325661ad94SKeyon Jie 	if (size > be->max) {
233c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev,
2345661ad94SKeyon Jie 				    "error: size too big %zu bytes max is %d\n",
2355661ad94SKeyon Jie 				    size, be->max);
236c3078f53SLiam Girdwood 		return -EINVAL;
237c3078f53SLiam Girdwood 	}
238c3078f53SLiam Girdwood 
239c3078f53SLiam Girdwood 	/* copy from kcontrol */
2405661ad94SKeyon Jie 	memcpy(data, ucontrol->value.bytes.data, size);
241c3078f53SLiam Girdwood 
242c3078f53SLiam Girdwood 	/* notify DSP of byte control updates */
2430c888babSBard Liao 	if (pm_runtime_active(sdev->dev))
244c3078f53SLiam Girdwood 		snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
245c3078f53SLiam Girdwood 					      SOF_IPC_COMP_SET_DATA,
246c3078f53SLiam Girdwood 					      SOF_CTRL_TYPE_DATA_SET,
247c3078f53SLiam Girdwood 					      scontrol->cmd,
248c3078f53SLiam Girdwood 					      true);
249c3078f53SLiam Girdwood 
2500c888babSBard Liao 	return 0;
251c3078f53SLiam Girdwood }
252c3078f53SLiam Girdwood 
253c3078f53SLiam Girdwood int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
254c3078f53SLiam Girdwood 			  const unsigned int __user *binary_data,
255c3078f53SLiam Girdwood 			  unsigned int size)
256c3078f53SLiam Girdwood {
257c3078f53SLiam Girdwood 	struct soc_bytes_ext *be =
258c3078f53SLiam Girdwood 		(struct soc_bytes_ext *)kcontrol->private_value;
259c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = be->dobj.private;
260c3078f53SLiam Girdwood 	struct snd_sof_dev *sdev = scontrol->sdev;
261c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
262c3078f53SLiam Girdwood 	struct snd_ctl_tlv header;
263c3078f53SLiam Girdwood 	const struct snd_ctl_tlv __user *tlvd =
264c3078f53SLiam Girdwood 		(const struct snd_ctl_tlv __user *)binary_data;
265c3078f53SLiam Girdwood 
266c3078f53SLiam Girdwood 	/*
267c3078f53SLiam Girdwood 	 * The beginning of bytes data contains a header from where
268c3078f53SLiam Girdwood 	 * the length (as bytes) is needed to know the correct copy
269c3078f53SLiam Girdwood 	 * length of data from tlvd->tlv.
270c3078f53SLiam Girdwood 	 */
271c3078f53SLiam Girdwood 	if (copy_from_user(&header, tlvd, sizeof(const struct snd_ctl_tlv)))
272c3078f53SLiam Girdwood 		return -EFAULT;
273c3078f53SLiam Girdwood 
274c3078f53SLiam Girdwood 	/* be->max is coming from topology */
275c3078f53SLiam Girdwood 	if (header.length > be->max) {
276c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev, "error: Bytes data size %d exceeds max %d.\n",
277c3078f53SLiam Girdwood 				    header.length, be->max);
278c3078f53SLiam Girdwood 		return -EINVAL;
279c3078f53SLiam Girdwood 	}
280c3078f53SLiam Girdwood 
281c3078f53SLiam Girdwood 	/* Check that header id matches the command */
282c3078f53SLiam Girdwood 	if (header.numid != scontrol->cmd) {
283c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev,
284c3078f53SLiam Girdwood 				    "error: incorrect numid %d\n",
285c3078f53SLiam Girdwood 				    header.numid);
286c3078f53SLiam Girdwood 		return -EINVAL;
287c3078f53SLiam Girdwood 	}
288c3078f53SLiam Girdwood 
289c3078f53SLiam Girdwood 	if (copy_from_user(cdata->data, tlvd->tlv, header.length))
290c3078f53SLiam Girdwood 		return -EFAULT;
291c3078f53SLiam Girdwood 
292c3078f53SLiam Girdwood 	if (cdata->data->magic != SOF_ABI_MAGIC) {
293c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev,
294c3078f53SLiam Girdwood 				    "error: Wrong ABI magic 0x%08x.\n",
295c3078f53SLiam Girdwood 				    cdata->data->magic);
296c3078f53SLiam Girdwood 		return -EINVAL;
297c3078f53SLiam Girdwood 	}
298c3078f53SLiam Girdwood 
299c3078f53SLiam Girdwood 	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
300c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev, "error: Incompatible ABI version 0x%08x.\n",
301c3078f53SLiam Girdwood 				    cdata->data->abi);
302c3078f53SLiam Girdwood 		return -EINVAL;
303c3078f53SLiam Girdwood 	}
304c3078f53SLiam Girdwood 
305c3078f53SLiam Girdwood 	if (cdata->data->size + sizeof(const struct sof_abi_hdr) > be->max) {
306c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev, "error: Mismatch in ABI data size (truncated?).\n");
307c3078f53SLiam Girdwood 		return -EINVAL;
308c3078f53SLiam Girdwood 	}
309c3078f53SLiam Girdwood 
310c3078f53SLiam Girdwood 	/* notify DSP of byte control updates */
3110c888babSBard Liao 	if (pm_runtime_active(sdev->dev))
312c3078f53SLiam Girdwood 		snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
313c3078f53SLiam Girdwood 					      SOF_IPC_COMP_SET_DATA,
314c3078f53SLiam Girdwood 					      SOF_CTRL_TYPE_DATA_SET,
315c3078f53SLiam Girdwood 					      scontrol->cmd,
316c3078f53SLiam Girdwood 					      true);
317c3078f53SLiam Girdwood 
3180c888babSBard Liao 	return 0;
319c3078f53SLiam Girdwood }
320c3078f53SLiam Girdwood 
321c3078f53SLiam Girdwood int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
322c3078f53SLiam Girdwood 			  unsigned int __user *binary_data,
323c3078f53SLiam Girdwood 			  unsigned int size)
324c3078f53SLiam Girdwood {
325c3078f53SLiam Girdwood 	struct soc_bytes_ext *be =
326c3078f53SLiam Girdwood 		(struct soc_bytes_ext *)kcontrol->private_value;
327c3078f53SLiam Girdwood 	struct snd_sof_control *scontrol = be->dobj.private;
328c3078f53SLiam Girdwood 	struct snd_sof_dev *sdev = scontrol->sdev;
329c3078f53SLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
330c3078f53SLiam Girdwood 	struct snd_ctl_tlv header;
331c3078f53SLiam Girdwood 	struct snd_ctl_tlv __user *tlvd =
332c3078f53SLiam Girdwood 		(struct snd_ctl_tlv __user *)binary_data;
333c3078f53SLiam Girdwood 	int data_size;
3340c888babSBard Liao 	int ret = 0;
335c3078f53SLiam Girdwood 
336c3078f53SLiam Girdwood 	/*
337c3078f53SLiam Girdwood 	 * Decrement the limit by ext bytes header size to
338c3078f53SLiam Girdwood 	 * ensure the user space buffer is not exceeded.
339c3078f53SLiam Girdwood 	 */
340c3078f53SLiam Girdwood 	size -= sizeof(const struct snd_ctl_tlv);
341c3078f53SLiam Girdwood 
342c3078f53SLiam Girdwood 	/* set the ABI header values */
343c3078f53SLiam Girdwood 	cdata->data->magic = SOF_ABI_MAGIC;
344c3078f53SLiam Girdwood 	cdata->data->abi = SOF_ABI_VERSION;
345c3078f53SLiam Girdwood 
346c3078f53SLiam Girdwood 	/* Prevent read of other kernel data or possibly corrupt response */
347c3078f53SLiam Girdwood 	data_size = cdata->data->size + sizeof(const struct sof_abi_hdr);
348c3078f53SLiam Girdwood 
349c3078f53SLiam Girdwood 	/* check data size doesn't exceed max coming from topology */
350c3078f53SLiam Girdwood 	if (data_size > be->max) {
351c3078f53SLiam Girdwood 		dev_err_ratelimited(sdev->dev, "error: user data size %d exceeds max size %d.\n",
352c3078f53SLiam Girdwood 				    data_size, be->max);
353c3078f53SLiam Girdwood 		ret = -EINVAL;
354c3078f53SLiam Girdwood 		goto out;
355c3078f53SLiam Girdwood 	}
356c3078f53SLiam Girdwood 
357c3078f53SLiam Girdwood 	header.numid = scontrol->cmd;
358c3078f53SLiam Girdwood 	header.length = data_size;
359c3078f53SLiam Girdwood 	if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) {
360c3078f53SLiam Girdwood 		ret = -EFAULT;
361c3078f53SLiam Girdwood 		goto out;
362c3078f53SLiam Girdwood 	}
363c3078f53SLiam Girdwood 
364c3078f53SLiam Girdwood 	if (copy_to_user(tlvd->tlv, cdata->data, data_size))
365c3078f53SLiam Girdwood 		ret = -EFAULT;
366c3078f53SLiam Girdwood 
367c3078f53SLiam Girdwood out:
368c3078f53SLiam Girdwood 	return ret;
369c3078f53SLiam Girdwood }
370