1d5ac70f0Sopenharmony_ci/*
2d5ac70f0Sopenharmony_ci * user-control-element-set.c - a program to test in-kernel implementation of
3d5ac70f0Sopenharmony_ci *				user-defined control element set.
4d5ac70f0Sopenharmony_ci *
5d5ac70f0Sopenharmony_ci * Copyright (c) 2015-2016 Takashi Sakamoto
6d5ac70f0Sopenharmony_ci *
7d5ac70f0Sopenharmony_ci * Licensed under the terms of the GNU General Public License, version 2.
8d5ac70f0Sopenharmony_ci */
9d5ac70f0Sopenharmony_ci
10d5ac70f0Sopenharmony_ci#include "config.h"
11d5ac70f0Sopenharmony_ci#include "../include/asoundlib.h"
12d5ac70f0Sopenharmony_ci#include <sound/tlv.h>
13d5ac70f0Sopenharmony_ci#include <stdbool.h>
14d5ac70f0Sopenharmony_ci
15d5ac70f0Sopenharmony_cistruct elem_set_trial {
16d5ac70f0Sopenharmony_ci	snd_ctl_t *handle;
17d5ac70f0Sopenharmony_ci
18d5ac70f0Sopenharmony_ci	snd_ctl_elem_type_t type;
19d5ac70f0Sopenharmony_ci	unsigned int member_count;
20d5ac70f0Sopenharmony_ci	unsigned int element_count;
21d5ac70f0Sopenharmony_ci
22d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_t *id;
23d5ac70f0Sopenharmony_ci
24d5ac70f0Sopenharmony_ci	int (*add_elem_set)(struct elem_set_trial *trial,
25d5ac70f0Sopenharmony_ci			    snd_ctl_elem_info_t *info);
26d5ac70f0Sopenharmony_ci	int (*check_elem_props)(struct elem_set_trial *trial,
27d5ac70f0Sopenharmony_ci				snd_ctl_elem_info_t *info);
28d5ac70f0Sopenharmony_ci	void (*change_elem_members)(struct elem_set_trial *trial,
29d5ac70f0Sopenharmony_ci				    snd_ctl_elem_value_t *elem_data);
30d5ac70f0Sopenharmony_ci	int (*allocate_elem_set_tlv)(struct elem_set_trial *trial,
31d5ac70f0Sopenharmony_ci				     unsigned int **tlv);
32d5ac70f0Sopenharmony_ci
33d5ac70f0Sopenharmony_ci	bool tlv_readable;
34d5ac70f0Sopenharmony_ci};
35d5ac70f0Sopenharmony_ci
36d5ac70f0Sopenharmony_cistruct chmap_entry {
37d5ac70f0Sopenharmony_ci	unsigned int type;
38d5ac70f0Sopenharmony_ci	unsigned int length;
39d5ac70f0Sopenharmony_ci	unsigned int maps[0];
40d5ac70f0Sopenharmony_ci};
41d5ac70f0Sopenharmony_ci
42d5ac70f0Sopenharmony_ci/*
43d5ac70f0Sopenharmony_ci * History of TLV feature:
44d5ac70f0Sopenharmony_ci *
45d5ac70f0Sopenharmony_ci * 2016/09/15: 398fa4db6c69 ("ALSA: control: move layout of TLV payload to UAPI
46d5ac70f0Sopenharmony_ci *			      header")
47d5ac70f0Sopenharmony_ci * 2012/07/21: 2d3391ec0ecc ("ALSA: PCM: channel mapping API implementation")
48d5ac70f0Sopenharmony_ci * 2011/11/20: bf1d1c9b6179 ("ALSA: tlv: add DECLARE_TLV_DB_RANGE()")
49d5ac70f0Sopenharmony_ci * 2009/07/16: 085f30654175 ("ALSA: Add new TLV types for dBwith min/max")
50d5ac70f0Sopenharmony_ci * 2006/09/06: 55a29af5ed5d ("[ALSA] Add definition of TLV dB range compound")
51d5ac70f0Sopenharmony_ci * 2006/08/28: 063a40d9111c ("Add the definition of linear volume TLV")
52d5ac70f0Sopenharmony_ci * 2006/08/28: 42750b04c5ba ("[ALSA] Control API - TLV implementation for
53d5ac70f0Sopenharmony_ci *			      additional information like dB scale")
54d5ac70f0Sopenharmony_ci */
55d5ac70f0Sopenharmony_ci
56d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with boolean type. */
57d5ac70f0Sopenharmony_cistatic int add_bool_elem_set(struct elem_set_trial *trial,
58d5ac70f0Sopenharmony_ci			     snd_ctl_elem_info_t *info)
59d5ac70f0Sopenharmony_ci{
60d5ac70f0Sopenharmony_ci	return snd_ctl_add_boolean_elem_set(trial->handle, info,
61d5ac70f0Sopenharmony_ci				trial->element_count, trial->member_count);
62d5ac70f0Sopenharmony_ci}
63d5ac70f0Sopenharmony_ci
64d5ac70f0Sopenharmony_cistatic void change_bool_elem_members(struct elem_set_trial *trial,
65d5ac70f0Sopenharmony_ci				     snd_ctl_elem_value_t *elem_data)
66d5ac70f0Sopenharmony_ci{
67d5ac70f0Sopenharmony_ci	int val;
68d5ac70f0Sopenharmony_ci	unsigned int i;
69d5ac70f0Sopenharmony_ci
70d5ac70f0Sopenharmony_ci	for (i = 0; i < trial->member_count; ++i) {
71d5ac70f0Sopenharmony_ci		val = snd_ctl_elem_value_get_boolean(elem_data, i);
72d5ac70f0Sopenharmony_ci		snd_ctl_elem_value_set_boolean(elem_data, i, !val);
73d5ac70f0Sopenharmony_ci	}
74d5ac70f0Sopenharmony_ci}
75d5ac70f0Sopenharmony_ci
76d5ac70f0Sopenharmony_cistatic int allocate_bool_elem_set_tlv(struct elem_set_trial *trial,
77d5ac70f0Sopenharmony_ci				      unsigned int **tlv)
78d5ac70f0Sopenharmony_ci{
79d5ac70f0Sopenharmony_ci	/*
80d5ac70f0Sopenharmony_ci	 * Performs like a toggle switch for attenuation, because they're bool
81d5ac70f0Sopenharmony_ci	 * elements.
82d5ac70f0Sopenharmony_ci	 */
83d5ac70f0Sopenharmony_ci	static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(range, -10000, 0);
84d5ac70f0Sopenharmony_ci
85d5ac70f0Sopenharmony_ci	*tlv = malloc(sizeof(range));
86d5ac70f0Sopenharmony_ci	if (*tlv == NULL)
87d5ac70f0Sopenharmony_ci		return -ENOMEM;
88d5ac70f0Sopenharmony_ci	memcpy(*tlv, range, sizeof(range));
89d5ac70f0Sopenharmony_ci
90d5ac70f0Sopenharmony_ci	return 0;
91d5ac70f0Sopenharmony_ci}
92d5ac70f0Sopenharmony_ci
93d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with integer type. */
94d5ac70f0Sopenharmony_cistatic int add_int_elem_set(struct elem_set_trial *trial,
95d5ac70f0Sopenharmony_ci			    snd_ctl_elem_info_t *info)
96d5ac70f0Sopenharmony_ci{
97d5ac70f0Sopenharmony_ci	return snd_ctl_add_integer_elem_set(trial->handle, info,
98d5ac70f0Sopenharmony_ci				trial->element_count, trial->member_count,
99d5ac70f0Sopenharmony_ci				0, 25, 1);
100d5ac70f0Sopenharmony_ci}
101d5ac70f0Sopenharmony_ci
102d5ac70f0Sopenharmony_cistatic int check_int_elem_props(struct elem_set_trial *trial,
103d5ac70f0Sopenharmony_ci				snd_ctl_elem_info_t *info)
104d5ac70f0Sopenharmony_ci{
105d5ac70f0Sopenharmony_ci	if (snd_ctl_elem_info_get_min(info) != 0)
106d5ac70f0Sopenharmony_ci		return -EIO;
107d5ac70f0Sopenharmony_ci	if (snd_ctl_elem_info_get_max(info) != 25)
108d5ac70f0Sopenharmony_ci		return -EIO;
109d5ac70f0Sopenharmony_ci	if (snd_ctl_elem_info_get_step(info) != 1)
110d5ac70f0Sopenharmony_ci		return -EIO;
111d5ac70f0Sopenharmony_ci
112d5ac70f0Sopenharmony_ci	return 0;
113d5ac70f0Sopenharmony_ci}
114d5ac70f0Sopenharmony_ci
115d5ac70f0Sopenharmony_cistatic void change_int_elem_members(struct elem_set_trial *trial,
116d5ac70f0Sopenharmony_ci				    snd_ctl_elem_value_t *elem_data)
117d5ac70f0Sopenharmony_ci{
118d5ac70f0Sopenharmony_ci	long val;
119d5ac70f0Sopenharmony_ci	unsigned int i;
120d5ac70f0Sopenharmony_ci
121d5ac70f0Sopenharmony_ci	for (i = 0; i < trial->member_count; ++i) {
122d5ac70f0Sopenharmony_ci		val = snd_ctl_elem_value_get_integer(elem_data, i);
123d5ac70f0Sopenharmony_ci		snd_ctl_elem_value_set_integer(elem_data, i, ++val);
124d5ac70f0Sopenharmony_ci	}
125d5ac70f0Sopenharmony_ci}
126d5ac70f0Sopenharmony_ci
127d5ac70f0Sopenharmony_cistatic int allocate_int_elem_set_tlv(struct elem_set_trial *trial,
128d5ac70f0Sopenharmony_ci				     unsigned int **tlv)
129d5ac70f0Sopenharmony_ci{
130d5ac70f0Sopenharmony_ci	unsigned int count, pos;
131d5ac70f0Sopenharmony_ci	unsigned int i, j;
132d5ac70f0Sopenharmony_ci	struct chmap_entry *entry;
133d5ac70f0Sopenharmony_ci
134d5ac70f0Sopenharmony_ci	/* Calculate size of TLV packet for channel-mapping information. */
135d5ac70f0Sopenharmony_ci	count = 0;
136d5ac70f0Sopenharmony_ci	for (i = 1; i <= 25; ++i) {
137d5ac70f0Sopenharmony_ci		count += 2; /* sizeof(struct chmap_entry). */
138d5ac70f0Sopenharmony_ci		count += i; /* struct chmap_entry.maps. */
139d5ac70f0Sopenharmony_ci	}
140d5ac70f0Sopenharmony_ci
141d5ac70f0Sopenharmony_ci	*tlv = malloc((2 + count) * sizeof(unsigned int));
142d5ac70f0Sopenharmony_ci	if (!*tlv)
143d5ac70f0Sopenharmony_ci		return -ENOMEM;
144d5ac70f0Sopenharmony_ci
145d5ac70f0Sopenharmony_ci	/*
146d5ac70f0Sopenharmony_ci	 * Emulate channel-mapping information in in-kernel implementation.
147d5ac70f0Sopenharmony_ci	 * Here, 25 entries are for each different channel.
148d5ac70f0Sopenharmony_ci	 */
149d5ac70f0Sopenharmony_ci	(*tlv)[0] = SNDRV_CTL_TLVT_CONTAINER;
150d5ac70f0Sopenharmony_ci	(*tlv)[1] = count * sizeof(unsigned int);
151d5ac70f0Sopenharmony_ci	pos = 2;
152d5ac70f0Sopenharmony_ci
153d5ac70f0Sopenharmony_ci	for (i = 1; i <= 25 && pos < count; ++i) {
154d5ac70f0Sopenharmony_ci		entry = (struct chmap_entry *)&(*tlv)[pos];
155d5ac70f0Sopenharmony_ci
156d5ac70f0Sopenharmony_ci		entry->type = SNDRV_CTL_TLVT_CHMAP_FIXED;
157d5ac70f0Sopenharmony_ci		entry->length = i * sizeof(unsigned int);
158d5ac70f0Sopenharmony_ci		pos += 2;
159d5ac70f0Sopenharmony_ci
160d5ac70f0Sopenharmony_ci		for (j = 0; j < i; ++j)
161d5ac70f0Sopenharmony_ci			entry->maps[j] = SND_CHMAP_MONO + j;
162d5ac70f0Sopenharmony_ci		pos += i;
163d5ac70f0Sopenharmony_ci	}
164d5ac70f0Sopenharmony_ci
165d5ac70f0Sopenharmony_ci	return 0;
166d5ac70f0Sopenharmony_ci}
167d5ac70f0Sopenharmony_ci
168d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with enumerated type. */
169d5ac70f0Sopenharmony_cistatic const char *const labels[] = {
170d5ac70f0Sopenharmony_ci	"trusty",
171d5ac70f0Sopenharmony_ci	"utopic",
172d5ac70f0Sopenharmony_ci	"vivid",
173d5ac70f0Sopenharmony_ci	"willy",
174d5ac70f0Sopenharmony_ci	"xenial",
175d5ac70f0Sopenharmony_ci};
176d5ac70f0Sopenharmony_ci
177d5ac70f0Sopenharmony_cistatic int add_enum_elem_set(struct elem_set_trial *trial,
178d5ac70f0Sopenharmony_ci			     snd_ctl_elem_info_t *info)
179d5ac70f0Sopenharmony_ci{
180d5ac70f0Sopenharmony_ci	return snd_ctl_add_enumerated_elem_set(trial->handle, info,
181d5ac70f0Sopenharmony_ci				trial->element_count, trial->member_count,
182d5ac70f0Sopenharmony_ci				sizeof(labels) / sizeof(labels[0]),
183d5ac70f0Sopenharmony_ci				labels);
184d5ac70f0Sopenharmony_ci}
185d5ac70f0Sopenharmony_ci
186d5ac70f0Sopenharmony_cistatic int check_enum_elem_props(struct elem_set_trial *trial,
187d5ac70f0Sopenharmony_ci				 snd_ctl_elem_info_t *info)
188d5ac70f0Sopenharmony_ci{
189d5ac70f0Sopenharmony_ci	unsigned int items;
190d5ac70f0Sopenharmony_ci	unsigned int i;
191d5ac70f0Sopenharmony_ci	const char *label;
192d5ac70f0Sopenharmony_ci	int err;
193d5ac70f0Sopenharmony_ci
194d5ac70f0Sopenharmony_ci	items = snd_ctl_elem_info_get_items(info);
195d5ac70f0Sopenharmony_ci	if (items != sizeof(labels) / sizeof(labels[0]))
196d5ac70f0Sopenharmony_ci		return -EIO;
197d5ac70f0Sopenharmony_ci
198d5ac70f0Sopenharmony_ci	/* Enumerate and validate all of labels registered to this element. */
199d5ac70f0Sopenharmony_ci	for (i = 0; i < items; ++i) {
200d5ac70f0Sopenharmony_ci		snd_ctl_elem_info_set_item(info, i);
201d5ac70f0Sopenharmony_ci		err = snd_ctl_elem_info(trial->handle, info);
202d5ac70f0Sopenharmony_ci		if (err < 0)
203d5ac70f0Sopenharmony_ci			return err;
204d5ac70f0Sopenharmony_ci
205d5ac70f0Sopenharmony_ci		label = snd_ctl_elem_info_get_item_name(info);
206d5ac70f0Sopenharmony_ci		if (strncmp(label, labels[i], strlen(labels[i])) != 0)
207d5ac70f0Sopenharmony_ci			return -EIO;
208d5ac70f0Sopenharmony_ci	}
209d5ac70f0Sopenharmony_ci
210d5ac70f0Sopenharmony_ci	return 0;
211d5ac70f0Sopenharmony_ci}
212d5ac70f0Sopenharmony_ci
213d5ac70f0Sopenharmony_cistatic void change_enum_elem_members(struct elem_set_trial *trial,
214d5ac70f0Sopenharmony_ci				     snd_ctl_elem_value_t *elem_data)
215d5ac70f0Sopenharmony_ci{
216d5ac70f0Sopenharmony_ci	unsigned int val;
217d5ac70f0Sopenharmony_ci	unsigned int i;
218d5ac70f0Sopenharmony_ci
219d5ac70f0Sopenharmony_ci	for (i = 0; i < trial->member_count; ++i) {
220d5ac70f0Sopenharmony_ci		val = snd_ctl_elem_value_get_enumerated(elem_data, i);
221d5ac70f0Sopenharmony_ci		snd_ctl_elem_value_set_enumerated(elem_data, i, ++val);
222d5ac70f0Sopenharmony_ci	}
223d5ac70f0Sopenharmony_ci}
224d5ac70f0Sopenharmony_ci
225d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with bytes type. */
226d5ac70f0Sopenharmony_cistatic int add_bytes_elem_set(struct elem_set_trial *trial,
227d5ac70f0Sopenharmony_ci			      snd_ctl_elem_info_t *info)
228d5ac70f0Sopenharmony_ci{
229d5ac70f0Sopenharmony_ci	return snd_ctl_add_bytes_elem_set(trial->handle, info,
230d5ac70f0Sopenharmony_ci				trial->element_count, trial->member_count);
231d5ac70f0Sopenharmony_ci}
232d5ac70f0Sopenharmony_ci
233d5ac70f0Sopenharmony_cistatic void change_bytes_elem_members(struct elem_set_trial *trial,
234d5ac70f0Sopenharmony_ci				      snd_ctl_elem_value_t *elem_data)
235d5ac70f0Sopenharmony_ci{
236d5ac70f0Sopenharmony_ci	unsigned char val;
237d5ac70f0Sopenharmony_ci	unsigned int i;
238d5ac70f0Sopenharmony_ci
239d5ac70f0Sopenharmony_ci	for (i = 0; i < trial->member_count; ++i) {
240d5ac70f0Sopenharmony_ci		val = snd_ctl_elem_value_get_byte(elem_data, i);
241d5ac70f0Sopenharmony_ci		snd_ctl_elem_value_set_byte(elem_data, i, ++val);
242d5ac70f0Sopenharmony_ci	}
243d5ac70f0Sopenharmony_ci}
244d5ac70f0Sopenharmony_ci
245d5ac70f0Sopenharmony_cistatic int allocate_bytes_elem_set_tlv(struct elem_set_trial *trial,
246d5ac70f0Sopenharmony_ci				       unsigned int **tlv)
247d5ac70f0Sopenharmony_ci{
248d5ac70f0Sopenharmony_ci	/*
249d5ac70f0Sopenharmony_ci	 * Emulate AK4396.
250d5ac70f0Sopenharmony_ci	 * 20 * log10(x/255) (dB)
251d5ac70f0Sopenharmony_ci	 * Here, x is written value.
252d5ac70f0Sopenharmony_ci	 */
253d5ac70f0Sopenharmony_ci	static const SNDRV_CTL_TLVD_DECLARE_DB_LINEAR(range, -4813, 0);
254d5ac70f0Sopenharmony_ci
255d5ac70f0Sopenharmony_ci	*tlv = malloc(sizeof(range));
256d5ac70f0Sopenharmony_ci	if (*tlv == NULL)
257d5ac70f0Sopenharmony_ci		return -ENOMEM;
258d5ac70f0Sopenharmony_ci	memcpy(*tlv, range, sizeof(range));
259d5ac70f0Sopenharmony_ci
260d5ac70f0Sopenharmony_ci	return 0;
261d5ac70f0Sopenharmony_ci}
262d5ac70f0Sopenharmony_ci
263d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with iec958 type. */
264d5ac70f0Sopenharmony_cistatic int add_iec958_elem_set(struct elem_set_trial *trial,
265d5ac70f0Sopenharmony_ci			       snd_ctl_elem_info_t *info)
266d5ac70f0Sopenharmony_ci{
267d5ac70f0Sopenharmony_ci	int err;
268d5ac70f0Sopenharmony_ci
269d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_get_id(info, trial->id);
270d5ac70f0Sopenharmony_ci
271d5ac70f0Sopenharmony_ci	err = snd_ctl_elem_add_iec958(trial->handle, trial->id);
272d5ac70f0Sopenharmony_ci	if (err < 0)
273d5ac70f0Sopenharmony_ci	        return err;
274d5ac70f0Sopenharmony_ci
275d5ac70f0Sopenharmony_ci	/*
276d5ac70f0Sopenharmony_ci	 * In historical reason, the above API is not allowed to fill all of
277d5ac70f0Sopenharmony_ci	 * fields in identification data.
278d5ac70f0Sopenharmony_ci	 */
279d5ac70f0Sopenharmony_ci	return snd_ctl_elem_info(trial->handle, info);
280d5ac70f0Sopenharmony_ci}
281d5ac70f0Sopenharmony_ci
282d5ac70f0Sopenharmony_cistatic void change_iec958_elem_members(struct elem_set_trial *trial,
283d5ac70f0Sopenharmony_ci				       snd_ctl_elem_value_t *elem_data)
284d5ac70f0Sopenharmony_ci{
285d5ac70f0Sopenharmony_ci	snd_aes_iec958_t data;
286d5ac70f0Sopenharmony_ci
287d5ac70f0Sopenharmony_ci	/* To suppress GCC warnings. */
288d5ac70f0Sopenharmony_ci	trial->element_count = 1;
289d5ac70f0Sopenharmony_ci
290d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_get_iec958(elem_data, &data);
291d5ac70f0Sopenharmony_ci	/* This is an arbitrary number. */
292d5ac70f0Sopenharmony_ci	data.pad = 10;
293d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_set_iec958(elem_data, &data);
294d5ac70f0Sopenharmony_ci}
295d5ac70f0Sopenharmony_ci
296d5ac70f0Sopenharmony_ci/* Operations for elements in an element set with integer64 type. */
297d5ac70f0Sopenharmony_cistatic int add_int64_elem_set(struct elem_set_trial *trial,
298d5ac70f0Sopenharmony_ci			      snd_ctl_elem_info_t *info)
299d5ac70f0Sopenharmony_ci{
300d5ac70f0Sopenharmony_ci	return snd_ctl_add_integer64_elem_set(trial->handle, info,
301d5ac70f0Sopenharmony_ci				trial->element_count, trial->member_count,
302d5ac70f0Sopenharmony_ci				0, 10000, 1);
303d5ac70f0Sopenharmony_ci}
304d5ac70f0Sopenharmony_ci
305d5ac70f0Sopenharmony_cistatic int check_int64_elem_props(struct elem_set_trial *trial,
306d5ac70f0Sopenharmony_ci				  snd_ctl_elem_info_t *info)
307d5ac70f0Sopenharmony_ci{
308d5ac70f0Sopenharmony_ci	if (snd_ctl_elem_info_get_min64(info) != 0)
309d5ac70f0Sopenharmony_ci		return -EIO;
310d5ac70f0Sopenharmony_ci	if (snd_ctl_elem_info_get_max64(info) != 10000)
311d5ac70f0Sopenharmony_ci		return -EIO;
312d5ac70f0Sopenharmony_ci	if (snd_ctl_elem_info_get_step64(info) != 1)
313d5ac70f0Sopenharmony_ci		return -EIO;
314d5ac70f0Sopenharmony_ci
315d5ac70f0Sopenharmony_ci	return 0;
316d5ac70f0Sopenharmony_ci}
317d5ac70f0Sopenharmony_ci
318d5ac70f0Sopenharmony_cistatic void change_int64_elem_members(struct elem_set_trial *trial,
319d5ac70f0Sopenharmony_ci				      snd_ctl_elem_value_t *elem_data)
320d5ac70f0Sopenharmony_ci{
321d5ac70f0Sopenharmony_ci	long long val;
322d5ac70f0Sopenharmony_ci	unsigned int i;
323d5ac70f0Sopenharmony_ci
324d5ac70f0Sopenharmony_ci	for (i = 0; i < trial->member_count; ++i) {
325d5ac70f0Sopenharmony_ci		val = snd_ctl_elem_value_get_integer64(elem_data, i);
326d5ac70f0Sopenharmony_ci		snd_ctl_elem_value_set_integer64(elem_data, i, ++val);
327d5ac70f0Sopenharmony_ci	}
328d5ac70f0Sopenharmony_ci}
329d5ac70f0Sopenharmony_ci
330d5ac70f0Sopenharmony_cistatic int allocate_int64_elem_set_tlv(struct elem_set_trial *trial,
331d5ac70f0Sopenharmony_ci				       unsigned int **tlv)
332d5ac70f0Sopenharmony_ci{
333d5ac70f0Sopenharmony_ci	/*
334d5ac70f0Sopenharmony_ci	 * Use this fomula between linear/dB value:
335d5ac70f0Sopenharmony_ci	 *
336d5ac70f0Sopenharmony_ci	 *  Linear: dB range (coeff)
337d5ac70f0Sopenharmony_ci	 *   0<-> 4: -59.40<->-56.36 (44)
338d5ac70f0Sopenharmony_ci	 *   4<->22: -56.36<->-45.56 (60)
339d5ac70f0Sopenharmony_ci	 *  22<->33: -45.56<->-40.72 (76)
340d5ac70f0Sopenharmony_ci	 *  33<->37: -40.72<->-38.32 (44)
341d5ac70f0Sopenharmony_ci	 *  37<->48: -38.32<->-29.96 (76)
342d5ac70f0Sopenharmony_ci	 *  48<->66: -29.96<->-22.04 (60)
343d5ac70f0Sopenharmony_ci	 *  66<->84: -22.04<-> -8.36 (44)
344d5ac70f0Sopenharmony_ci	 *  84<->95:  -8.36<-> -1.76 (60)
345d5ac70f0Sopenharmony_ci	 *  95<->99:  -1.76<->  0.00 (76)
346d5ac70f0Sopenharmony_ci	 * 100<->..:   0.0
347d5ac70f0Sopenharmony_ci	 */
348d5ac70f0Sopenharmony_ci	static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(range,
349d5ac70f0Sopenharmony_ci		 0,   4, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-5940, 44, 1),
350d5ac70f0Sopenharmony_ci		 4,  22, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-5636, 60, 0),
351d5ac70f0Sopenharmony_ci		22,  33, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-4556, 76, 0),
352d5ac70f0Sopenharmony_ci		33,  37, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-4072, 44, 0),
353d5ac70f0Sopenharmony_ci		37,  48, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-3832, 76, 0),
354d5ac70f0Sopenharmony_ci		48,  66, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-2996, 60, 0),
355d5ac70f0Sopenharmony_ci		66,  84, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-2204, 44, 0),
356d5ac70f0Sopenharmony_ci		84,  95, SNDRV_CTL_TLVD_DB_SCALE_ITEM( -836, 60, 0),
357d5ac70f0Sopenharmony_ci		95,  99, SNDRV_CTL_TLVD_DB_SCALE_ITEM( -176, 76, 0),
358d5ac70f0Sopenharmony_ci		100, 10000, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0),
359d5ac70f0Sopenharmony_ci	);
360d5ac70f0Sopenharmony_ci
361d5ac70f0Sopenharmony_ci	*tlv = malloc(sizeof(range));
362d5ac70f0Sopenharmony_ci	if (*tlv == NULL)
363d5ac70f0Sopenharmony_ci		return -ENOMEM;
364d5ac70f0Sopenharmony_ci	memcpy(*tlv, range, sizeof(range));
365d5ac70f0Sopenharmony_ci
366d5ac70f0Sopenharmony_ci	return 0;
367d5ac70f0Sopenharmony_ci}
368d5ac70f0Sopenharmony_ci
369d5ac70f0Sopenharmony_ci/* Common operations. */
370d5ac70f0Sopenharmony_cistatic int add_elem_set(struct elem_set_trial *trial)
371d5ac70f0Sopenharmony_ci{
372d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_t *info;
373d5ac70f0Sopenharmony_ci	char name[64] = {0};
374d5ac70f0Sopenharmony_ci	int err;
375d5ac70f0Sopenharmony_ci
376d5ac70f0Sopenharmony_ci	snprintf(name, 64, "userspace-control-element-%s",
377d5ac70f0Sopenharmony_ci		 snd_ctl_elem_type_name(trial->type));
378d5ac70f0Sopenharmony_ci
379d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_alloca(&info);
380d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_set_interface(info, SND_CTL_ELEM_IFACE_MIXER);
381d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_set_name(info, name);
382d5ac70f0Sopenharmony_ci
383d5ac70f0Sopenharmony_ci	err = trial->add_elem_set(trial, info);
384d5ac70f0Sopenharmony_ci	if (err >= 0)
385d5ac70f0Sopenharmony_ci		snd_ctl_elem_info_get_id(info, trial->id);
386d5ac70f0Sopenharmony_ci
387d5ac70f0Sopenharmony_ci	return err;
388d5ac70f0Sopenharmony_ci}
389d5ac70f0Sopenharmony_ci
390d5ac70f0Sopenharmony_cistatic int check_event(struct elem_set_trial *trial, unsigned int mask,
391d5ac70f0Sopenharmony_ci		       unsigned int expected_count)
392d5ac70f0Sopenharmony_ci{
393d5ac70f0Sopenharmony_ci	struct pollfd pfds;
394d5ac70f0Sopenharmony_ci	int count;
395d5ac70f0Sopenharmony_ci	unsigned short revents;
396d5ac70f0Sopenharmony_ci	snd_ctl_event_t *event;
397d5ac70f0Sopenharmony_ci	int err;
398d5ac70f0Sopenharmony_ci
399d5ac70f0Sopenharmony_ci	snd_ctl_event_alloca(&event);
400d5ac70f0Sopenharmony_ci
401d5ac70f0Sopenharmony_ci	if (snd_ctl_poll_descriptors_count(trial->handle) != 1)
402d5ac70f0Sopenharmony_ci		return -ENXIO;
403d5ac70f0Sopenharmony_ci
404d5ac70f0Sopenharmony_ci	if (snd_ctl_poll_descriptors(trial->handle, &pfds, 1) != 1)
405d5ac70f0Sopenharmony_ci		return -ENXIO;
406d5ac70f0Sopenharmony_ci
407d5ac70f0Sopenharmony_ci	while (expected_count > 0) {
408d5ac70f0Sopenharmony_ci		count = poll(&pfds, 1, 1000);
409d5ac70f0Sopenharmony_ci		if (count < 0)
410d5ac70f0Sopenharmony_ci			return errno;
411d5ac70f0Sopenharmony_ci		/* Some events are already supplied. */
412d5ac70f0Sopenharmony_ci		if (count == 0)
413d5ac70f0Sopenharmony_ci			return -ETIMEDOUT;
414d5ac70f0Sopenharmony_ci
415d5ac70f0Sopenharmony_ci		err = snd_ctl_poll_descriptors_revents(trial->handle, &pfds,
416d5ac70f0Sopenharmony_ci						       count, &revents);
417d5ac70f0Sopenharmony_ci		if (err < 0)
418d5ac70f0Sopenharmony_ci			return err;
419d5ac70f0Sopenharmony_ci		if (revents & POLLERR)
420d5ac70f0Sopenharmony_ci			return -EIO;
421d5ac70f0Sopenharmony_ci		if (!(revents & POLLIN))
422d5ac70f0Sopenharmony_ci			continue;
423d5ac70f0Sopenharmony_ci
424d5ac70f0Sopenharmony_ci		err = snd_ctl_read(trial->handle, event);
425d5ac70f0Sopenharmony_ci		if (err < 0)
426d5ac70f0Sopenharmony_ci			return err;
427d5ac70f0Sopenharmony_ci		if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
428d5ac70f0Sopenharmony_ci			continue;
429d5ac70f0Sopenharmony_ci		/*
430d5ac70f0Sopenharmony_ci		 * I expect each event is generated separately to the same
431d5ac70f0Sopenharmony_ci		 * element or several events are generated at once.
432d5ac70f0Sopenharmony_ci		 */
433d5ac70f0Sopenharmony_ci		if ((snd_ctl_event_elem_get_mask(event) & mask) != mask)
434d5ac70f0Sopenharmony_ci			continue;
435d5ac70f0Sopenharmony_ci		--expected_count;
436d5ac70f0Sopenharmony_ci	}
437d5ac70f0Sopenharmony_ci
438d5ac70f0Sopenharmony_ci	if (expected_count != 0)
439d5ac70f0Sopenharmony_ci		return -EIO;
440d5ac70f0Sopenharmony_ci
441d5ac70f0Sopenharmony_ci	return 0;
442d5ac70f0Sopenharmony_ci}
443d5ac70f0Sopenharmony_ci
444d5ac70f0Sopenharmony_cistatic int check_elem_list(struct elem_set_trial *trial)
445d5ac70f0Sopenharmony_ci{
446d5ac70f0Sopenharmony_ci	snd_ctl_elem_list_t *list;
447d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_t *id;
448d5ac70f0Sopenharmony_ci	int e;
449d5ac70f0Sopenharmony_ci	unsigned int i;
450d5ac70f0Sopenharmony_ci	int err;
451d5ac70f0Sopenharmony_ci
452d5ac70f0Sopenharmony_ci	snd_ctl_elem_list_alloca(&list);
453d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_alloca(&id);
454d5ac70f0Sopenharmony_ci
455d5ac70f0Sopenharmony_ci	err = snd_ctl_elem_list(trial->handle, list);
456d5ac70f0Sopenharmony_ci	if (err < 0)
457d5ac70f0Sopenharmony_ci		return err;
458d5ac70f0Sopenharmony_ci
459d5ac70f0Sopenharmony_ci	/* Certainly some elements are already added. */
460d5ac70f0Sopenharmony_ci	if (snd_ctl_elem_list_get_count(list) == 0)
461d5ac70f0Sopenharmony_ci		return -EIO;
462d5ac70f0Sopenharmony_ci
463d5ac70f0Sopenharmony_ci	err = snd_ctl_elem_list_alloc_space(list,
464d5ac70f0Sopenharmony_ci					    snd_ctl_elem_list_get_count(list));
465d5ac70f0Sopenharmony_ci	if (err < 0)
466d5ac70f0Sopenharmony_ci		return err;
467d5ac70f0Sopenharmony_ci
468d5ac70f0Sopenharmony_ci	err = snd_ctl_elem_list(trial->handle, list);
469d5ac70f0Sopenharmony_ci	if (err < 0)
470d5ac70f0Sopenharmony_ci		goto end;
471d5ac70f0Sopenharmony_ci
472d5ac70f0Sopenharmony_ci	if (trial->element_count > snd_ctl_elem_list_get_count(list)) {
473d5ac70f0Sopenharmony_ci		err = -EIO;
474d5ac70f0Sopenharmony_ci		goto end;
475d5ac70f0Sopenharmony_ci	}
476d5ac70f0Sopenharmony_ci
477d5ac70f0Sopenharmony_ci	i = 0;
478d5ac70f0Sopenharmony_ci	for (e = 0; e < snd_ctl_elem_list_get_count(list); ++e) {
479d5ac70f0Sopenharmony_ci		snd_ctl_elem_list_get_id(list, e, id);
480d5ac70f0Sopenharmony_ci
481d5ac70f0Sopenharmony_ci		if (strcmp(snd_ctl_elem_id_get_name(id),
482d5ac70f0Sopenharmony_ci			   snd_ctl_elem_id_get_name(trial->id)) != 0)
483d5ac70f0Sopenharmony_ci			continue;
484d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_id_get_interface(id) !=
485d5ac70f0Sopenharmony_ci		    snd_ctl_elem_id_get_interface(trial->id))
486d5ac70f0Sopenharmony_ci			continue;
487d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_id_get_device(id) !=
488d5ac70f0Sopenharmony_ci		    snd_ctl_elem_id_get_device(trial->id))
489d5ac70f0Sopenharmony_ci			continue;
490d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_id_get_subdevice(id) !=
491d5ac70f0Sopenharmony_ci		    snd_ctl_elem_id_get_subdevice(trial->id))
492d5ac70f0Sopenharmony_ci			continue;
493d5ac70f0Sopenharmony_ci
494d5ac70f0Sopenharmony_ci		/*
495d5ac70f0Sopenharmony_ci		 * Here, I expect the list includes element ID data in numerical
496d5ac70f0Sopenharmony_ci		 * order. Actually, it does.
497d5ac70f0Sopenharmony_ci		 */
498d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_id_get_numid(id) !=
499d5ac70f0Sopenharmony_ci		    snd_ctl_elem_id_get_numid(trial->id) + i)
500d5ac70f0Sopenharmony_ci			continue;
501d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_id_get_index(id) !=
502d5ac70f0Sopenharmony_ci		    snd_ctl_elem_id_get_index(trial->id) + i)
503d5ac70f0Sopenharmony_ci			continue;
504d5ac70f0Sopenharmony_ci
505d5ac70f0Sopenharmony_ci		++i;
506d5ac70f0Sopenharmony_ci	}
507d5ac70f0Sopenharmony_ci
508d5ac70f0Sopenharmony_ci	if (i != trial->element_count)
509d5ac70f0Sopenharmony_ci		err = -EIO;
510d5ac70f0Sopenharmony_ciend:
511d5ac70f0Sopenharmony_ci	snd_ctl_elem_list_free_space(list);
512d5ac70f0Sopenharmony_ci
513d5ac70f0Sopenharmony_ci	return err;
514d5ac70f0Sopenharmony_ci}
515d5ac70f0Sopenharmony_ci
516d5ac70f0Sopenharmony_cistatic int check_elem_set_props(struct elem_set_trial *trial)
517d5ac70f0Sopenharmony_ci{
518d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_t *id;
519d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_t *info;
520d5ac70f0Sopenharmony_ci	unsigned int numid;
521d5ac70f0Sopenharmony_ci	unsigned int index;
522d5ac70f0Sopenharmony_ci	unsigned int i;
523d5ac70f0Sopenharmony_ci	unsigned int j;
524d5ac70f0Sopenharmony_ci	int err;
525d5ac70f0Sopenharmony_ci
526d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_alloca(&id);
527d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_alloca(&info);
528d5ac70f0Sopenharmony_ci
529d5ac70f0Sopenharmony_ci	snd_ctl_elem_info_set_id(info, trial->id);
530d5ac70f0Sopenharmony_ci	numid = snd_ctl_elem_id_get_numid(trial->id);
531d5ac70f0Sopenharmony_ci	index = snd_ctl_elem_id_get_index(trial->id);
532d5ac70f0Sopenharmony_ci
533d5ac70f0Sopenharmony_ci	for (i = 0; i < trial->element_count; ++i) {
534d5ac70f0Sopenharmony_ci		snd_ctl_elem_info_set_index(info, index + i);
535d5ac70f0Sopenharmony_ci
536d5ac70f0Sopenharmony_ci		/*
537d5ac70f0Sopenharmony_ci		 * In Linux 4.0 or former, ioctl(SNDRV_CTL_IOCTL_ELEM_ADD)
538d5ac70f0Sopenharmony_ci		 * doesn't fill all of fields for identification.
539d5ac70f0Sopenharmony_ci		 */
540d5ac70f0Sopenharmony_ci		if (numid > 0)
541d5ac70f0Sopenharmony_ci			snd_ctl_elem_info_set_numid(info, numid + i);
542d5ac70f0Sopenharmony_ci
543d5ac70f0Sopenharmony_ci		err = snd_ctl_elem_info(trial->handle, info);
544d5ac70f0Sopenharmony_ci		if (err < 0)
545d5ac70f0Sopenharmony_ci			return err;
546d5ac70f0Sopenharmony_ci
547d5ac70f0Sopenharmony_ci		/* Check some common properties. */
548d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_info_get_type(info) != trial->type)
549d5ac70f0Sopenharmony_ci			return -EIO;
550d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_info_get_count(info) != trial->member_count)
551d5ac70f0Sopenharmony_ci			return -EIO;
552d5ac70f0Sopenharmony_ci
553d5ac70f0Sopenharmony_ci		/*
554d5ac70f0Sopenharmony_ci		 * In a case of IPC, this is the others. But in this case,
555d5ac70f0Sopenharmony_ci		 * it's myself.
556d5ac70f0Sopenharmony_ci		 */
557d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_info_get_owner(info) != getpid())
558d5ac70f0Sopenharmony_ci			return -EIO;
559d5ac70f0Sopenharmony_ci
560d5ac70f0Sopenharmony_ci		/*
561d5ac70f0Sopenharmony_ci		 * Just adding an element set by userspace applications,
562d5ac70f0Sopenharmony_ci		 * included elements are initially locked.
563d5ac70f0Sopenharmony_ci		 */
564d5ac70f0Sopenharmony_ci		if (!snd_ctl_elem_info_is_locked(info))
565d5ac70f0Sopenharmony_ci			return -EIO;
566d5ac70f0Sopenharmony_ci
567d5ac70f0Sopenharmony_ci		/*
568d5ac70f0Sopenharmony_ci		 * In initial state, any application can register TLV data for
569d5ac70f0Sopenharmony_ci		 * user-defined element set except for IEC 958 type, thus
570d5ac70f0Sopenharmony_ci		 * elements in any user-defined set should allow any write
571d5ac70f0Sopenharmony_ci		 * operation.
572d5ac70f0Sopenharmony_ci		 */
573d5ac70f0Sopenharmony_ci		if (trial->type != SND_CTL_ELEM_TYPE_IEC958 &&
574d5ac70f0Sopenharmony_ci		    !snd_ctl_elem_info_is_tlv_writable(info))
575d5ac70f0Sopenharmony_ci			return -EIO;
576d5ac70f0Sopenharmony_ci
577d5ac70f0Sopenharmony_ci		/* Check type-specific properties. */
578d5ac70f0Sopenharmony_ci		if (trial->check_elem_props != NULL) {
579d5ac70f0Sopenharmony_ci			err = trial->check_elem_props(trial, info);
580d5ac70f0Sopenharmony_ci			if (err < 0)
581d5ac70f0Sopenharmony_ci				return err;
582d5ac70f0Sopenharmony_ci		}
583d5ac70f0Sopenharmony_ci
584d5ac70f0Sopenharmony_ci		snd_ctl_elem_info_get_id(info, id);
585d5ac70f0Sopenharmony_ci		err = snd_ctl_elem_unlock(trial->handle, id);
586d5ac70f0Sopenharmony_ci		if (err < 0)
587d5ac70f0Sopenharmony_ci			return err;
588d5ac70f0Sopenharmony_ci
589d5ac70f0Sopenharmony_ci		/*
590d5ac70f0Sopenharmony_ci		 * Till kernel v4.14, ALSA control core allows elements in any
591d5ac70f0Sopenharmony_ci		 * user-defined set to have TLV_READ flag even if they have no
592d5ac70f0Sopenharmony_ci		 * TLV data in their initial state. In this case, any read
593d5ac70f0Sopenharmony_ci		 * operation for TLV data should return -ENXIO.
594d5ac70f0Sopenharmony_ci		 */
595d5ac70f0Sopenharmony_ci		if (snd_ctl_elem_info_is_tlv_readable(info)) {
596d5ac70f0Sopenharmony_ci			unsigned int data[32];
597d5ac70f0Sopenharmony_ci			err = snd_ctl_elem_tlv_read(trial->handle, trial->id,
598d5ac70f0Sopenharmony_ci						    data, sizeof(data));
599d5ac70f0Sopenharmony_ci			if (err >= 0)
600d5ac70f0Sopenharmony_ci				return -EIO;
601d5ac70f0Sopenharmony_ci			if (err != -ENXIO)
602d5ac70f0Sopenharmony_ci				return err;
603d5ac70f0Sopenharmony_ci
604d5ac70f0Sopenharmony_ci			trial->tlv_readable = true;
605d5ac70f0Sopenharmony_ci		}
606d5ac70f0Sopenharmony_ci
607d5ac70f0Sopenharmony_ci	}
608d5ac70f0Sopenharmony_ci
609d5ac70f0Sopenharmony_ci	return 0;
610d5ac70f0Sopenharmony_ci}
611d5ac70f0Sopenharmony_ci
612d5ac70f0Sopenharmony_cistatic int check_elems(struct elem_set_trial *trial)
613d5ac70f0Sopenharmony_ci{
614d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_t *data;
615d5ac70f0Sopenharmony_ci	unsigned int numid;
616d5ac70f0Sopenharmony_ci	unsigned int index;
617d5ac70f0Sopenharmony_ci	unsigned int i;
618d5ac70f0Sopenharmony_ci	int err;
619d5ac70f0Sopenharmony_ci
620d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_alloca(&data);
621d5ac70f0Sopenharmony_ci
622d5ac70f0Sopenharmony_ci	snd_ctl_elem_value_set_id(data, trial->id);
623d5ac70f0Sopenharmony_ci	numid = snd_ctl_elem_id_get_numid(trial->id);
624d5ac70f0Sopenharmony_ci	index = snd_ctl_elem_id_get_index(trial->id);
625d5ac70f0Sopenharmony_ci
626d5ac70f0Sopenharmony_ci	for (i = 0; i < trial->element_count; ++i) {
627d5ac70f0Sopenharmony_ci		snd_ctl_elem_value_set_index(data, index + i);
628d5ac70f0Sopenharmony_ci
629d5ac70f0Sopenharmony_ci		/*
630d5ac70f0Sopenharmony_ci		 * In Linux 4.0 or former, ioctl(SNDRV_CTL_IOCTL_ELEM_ADD)
631d5ac70f0Sopenharmony_ci		 * doesn't fill all of fields for identification.
632d5ac70f0Sopenharmony_ci		 */
633d5ac70f0Sopenharmony_ci		if (numid > 0)
634d5ac70f0Sopenharmony_ci			snd_ctl_elem_value_set_numid(data, numid + i);
635d5ac70f0Sopenharmony_ci
636d5ac70f0Sopenharmony_ci		err = snd_ctl_elem_read(trial->handle, data);
637d5ac70f0Sopenharmony_ci		if (err < 0)
638d5ac70f0Sopenharmony_ci			return err;
639d5ac70f0Sopenharmony_ci
640d5ac70f0Sopenharmony_ci		/* Change members of an element in this element set. */
641d5ac70f0Sopenharmony_ci		trial->change_elem_members(trial, data);
642d5ac70f0Sopenharmony_ci
643d5ac70f0Sopenharmony_ci		err = snd_ctl_elem_write(trial->handle, data);
644d5ac70f0Sopenharmony_ci		if (err < 0)
645d5ac70f0Sopenharmony_ci			return err;
646d5ac70f0Sopenharmony_ci	}
647d5ac70f0Sopenharmony_ci
648d5ac70f0Sopenharmony_ci	return 0;
649d5ac70f0Sopenharmony_ci}
650d5ac70f0Sopenharmony_ci
651d5ac70f0Sopenharmony_cistatic int check_tlv(struct elem_set_trial *trial)
652d5ac70f0Sopenharmony_ci{
653d5ac70f0Sopenharmony_ci	unsigned int *tlv;
654d5ac70f0Sopenharmony_ci	int mask;
655d5ac70f0Sopenharmony_ci	unsigned int count;
656d5ac70f0Sopenharmony_ci	unsigned int len;
657d5ac70f0Sopenharmony_ci	unsigned int *curr;
658d5ac70f0Sopenharmony_ci	int err;
659d5ac70f0Sopenharmony_ci
660d5ac70f0Sopenharmony_ci	err = trial->allocate_elem_set_tlv(trial, &tlv);
661d5ac70f0Sopenharmony_ci	if (err < 0)
662d5ac70f0Sopenharmony_ci		return err;
663d5ac70f0Sopenharmony_ci
664d5ac70f0Sopenharmony_ci	len = tlv[SNDRV_CTL_TLVO_LEN] + sizeof(unsigned int) * 2;
665d5ac70f0Sopenharmony_ci	curr = malloc(len);
666d5ac70f0Sopenharmony_ci	if (curr == NULL) {
667d5ac70f0Sopenharmony_ci		free(tlv);
668d5ac70f0Sopenharmony_ci		return -ENOMEM;
669d5ac70f0Sopenharmony_ci	}
670d5ac70f0Sopenharmony_ci
671d5ac70f0Sopenharmony_ci	/*
672d5ac70f0Sopenharmony_ci	 * In in-kernel implementation, write and command operations are the
673d5ac70f0Sopenharmony_ci	 * same for an element set added by userspace applications. Here, I
674d5ac70f0Sopenharmony_ci	 * use write.
675d5ac70f0Sopenharmony_ci	 */
676d5ac70f0Sopenharmony_ci	err = snd_ctl_elem_tlv_write(trial->handle, trial->id,
677d5ac70f0Sopenharmony_ci				     (const unsigned int *)tlv);
678d5ac70f0Sopenharmony_ci	if (err < 0)
679d5ac70f0Sopenharmony_ci		goto end;
680d5ac70f0Sopenharmony_ci
681d5ac70f0Sopenharmony_ci	/*
682d5ac70f0Sopenharmony_ci	 * Since kernel v4.14, any write operation to an element in user-defined
683d5ac70f0Sopenharmony_ci	 * set can change state of the other elements in the same set. In this
684d5ac70f0Sopenharmony_ci	 * case, any TLV data is firstly available after the operation.
685d5ac70f0Sopenharmony_ci	 */
686d5ac70f0Sopenharmony_ci	if (!trial->tlv_readable) {
687d5ac70f0Sopenharmony_ci		mask = SND_CTL_EVENT_MASK_INFO | SND_CTL_EVENT_MASK_TLV;
688d5ac70f0Sopenharmony_ci		count = trial->element_count;
689d5ac70f0Sopenharmony_ci	} else {
690d5ac70f0Sopenharmony_ci		mask = SND_CTL_EVENT_MASK_TLV;
691d5ac70f0Sopenharmony_ci		count = 1;
692d5ac70f0Sopenharmony_ci	}
693d5ac70f0Sopenharmony_ci	err = check_event(trial, mask, count);
694d5ac70f0Sopenharmony_ci	if (err < 0)
695d5ac70f0Sopenharmony_ci		goto end;
696d5ac70f0Sopenharmony_ci	if (!trial->tlv_readable) {
697d5ac70f0Sopenharmony_ci		snd_ctl_elem_info_t *info;
698d5ac70f0Sopenharmony_ci		snd_ctl_elem_info_alloca(&info);
699d5ac70f0Sopenharmony_ci
700d5ac70f0Sopenharmony_ci		snd_ctl_elem_info_set_id(info, trial->id);
701d5ac70f0Sopenharmony_ci		err = snd_ctl_elem_info(trial->handle, info);
702d5ac70f0Sopenharmony_ci		if (err < 0)
703d5ac70f0Sopenharmony_ci			return err;
704d5ac70f0Sopenharmony_ci		if (!snd_ctl_elem_info_is_tlv_readable(info))
705d5ac70f0Sopenharmony_ci			return -EIO;
706d5ac70f0Sopenharmony_ci
707d5ac70f0Sopenharmony_ci		/* Now TLV data is available for this element set. */
708d5ac70f0Sopenharmony_ci		trial->tlv_readable = true;
709d5ac70f0Sopenharmony_ci	}
710d5ac70f0Sopenharmony_ci
711d5ac70f0Sopenharmony_ci	err = snd_ctl_elem_tlv_read(trial->handle, trial->id, curr, len);
712d5ac70f0Sopenharmony_ci	if (err < 0)
713d5ac70f0Sopenharmony_ci		goto end;
714d5ac70f0Sopenharmony_ci
715d5ac70f0Sopenharmony_ci	if (memcmp(curr, tlv, len) != 0)
716d5ac70f0Sopenharmony_ci		err = -EIO;
717d5ac70f0Sopenharmony_ciend:
718d5ac70f0Sopenharmony_ci	free(tlv);
719d5ac70f0Sopenharmony_ci	free(curr);
720d5ac70f0Sopenharmony_ci	return 0;
721d5ac70f0Sopenharmony_ci}
722d5ac70f0Sopenharmony_ci
723d5ac70f0Sopenharmony_ciint main(void)
724d5ac70f0Sopenharmony_ci{
725d5ac70f0Sopenharmony_ci	struct elem_set_trial trial = {0};
726d5ac70f0Sopenharmony_ci	unsigned int i;
727d5ac70f0Sopenharmony_ci	int err;
728d5ac70f0Sopenharmony_ci
729d5ac70f0Sopenharmony_ci	snd_ctl_elem_id_alloca(&trial.id);
730d5ac70f0Sopenharmony_ci
731d5ac70f0Sopenharmony_ci	err = snd_ctl_open(&trial.handle, "hw:0", 0);
732d5ac70f0Sopenharmony_ci	if (err < 0)
733d5ac70f0Sopenharmony_ci		return EXIT_FAILURE;
734d5ac70f0Sopenharmony_ci
735d5ac70f0Sopenharmony_ci	err = snd_ctl_subscribe_events(trial.handle, 1);
736d5ac70f0Sopenharmony_ci	if (err < 0)
737d5ac70f0Sopenharmony_ci		return EXIT_FAILURE;
738d5ac70f0Sopenharmony_ci
739d5ac70f0Sopenharmony_ci	/* Test all of types. */
740d5ac70f0Sopenharmony_ci	for (i = 0; i < SND_CTL_ELEM_TYPE_LAST; ++i) {
741d5ac70f0Sopenharmony_ci		trial.type = i + 1;
742d5ac70f0Sopenharmony_ci
743d5ac70f0Sopenharmony_ci		/* Assign type-dependent operations. */
744d5ac70f0Sopenharmony_ci		switch (trial.type) {
745d5ac70f0Sopenharmony_ci		case SND_CTL_ELEM_TYPE_BOOLEAN:
746d5ac70f0Sopenharmony_ci			trial.element_count = 900;
747d5ac70f0Sopenharmony_ci			trial.member_count = 128;
748d5ac70f0Sopenharmony_ci			trial.add_elem_set = add_bool_elem_set;
749d5ac70f0Sopenharmony_ci			trial.check_elem_props = NULL;
750d5ac70f0Sopenharmony_ci			trial.change_elem_members = change_bool_elem_members;
751d5ac70f0Sopenharmony_ci			trial.allocate_elem_set_tlv =
752d5ac70f0Sopenharmony_ci						allocate_bool_elem_set_tlv;
753d5ac70f0Sopenharmony_ci			trial.tlv_readable = false;
754d5ac70f0Sopenharmony_ci			break;
755d5ac70f0Sopenharmony_ci		case SND_CTL_ELEM_TYPE_INTEGER:
756d5ac70f0Sopenharmony_ci			trial.element_count = 900;
757d5ac70f0Sopenharmony_ci			trial.member_count = 128;
758d5ac70f0Sopenharmony_ci			trial.add_elem_set = add_int_elem_set;
759d5ac70f0Sopenharmony_ci			trial.check_elem_props = check_int_elem_props;
760d5ac70f0Sopenharmony_ci			trial.change_elem_members = change_int_elem_members;
761d5ac70f0Sopenharmony_ci			trial.allocate_elem_set_tlv =
762d5ac70f0Sopenharmony_ci						allocate_int_elem_set_tlv;
763d5ac70f0Sopenharmony_ci			trial.tlv_readable = false;
764d5ac70f0Sopenharmony_ci			break;
765d5ac70f0Sopenharmony_ci		case SND_CTL_ELEM_TYPE_ENUMERATED:
766d5ac70f0Sopenharmony_ci			trial.element_count = 900;
767d5ac70f0Sopenharmony_ci			trial.member_count = 128;
768d5ac70f0Sopenharmony_ci			trial.add_elem_set = add_enum_elem_set;
769d5ac70f0Sopenharmony_ci			trial.check_elem_props = check_enum_elem_props;
770d5ac70f0Sopenharmony_ci			trial.change_elem_members = change_enum_elem_members;
771d5ac70f0Sopenharmony_ci			trial.allocate_elem_set_tlv = NULL;
772d5ac70f0Sopenharmony_ci			trial.tlv_readable = false;
773d5ac70f0Sopenharmony_ci			break;
774d5ac70f0Sopenharmony_ci		case SND_CTL_ELEM_TYPE_BYTES:
775d5ac70f0Sopenharmony_ci			trial.element_count = 900;
776d5ac70f0Sopenharmony_ci			trial.member_count = 512;
777d5ac70f0Sopenharmony_ci			trial.add_elem_set = add_bytes_elem_set;
778d5ac70f0Sopenharmony_ci			trial.check_elem_props = NULL;
779d5ac70f0Sopenharmony_ci			trial.change_elem_members = change_bytes_elem_members;
780d5ac70f0Sopenharmony_ci			trial.allocate_elem_set_tlv =
781d5ac70f0Sopenharmony_ci						allocate_bytes_elem_set_tlv;
782d5ac70f0Sopenharmony_ci			trial.tlv_readable = false;
783d5ac70f0Sopenharmony_ci			break;
784d5ac70f0Sopenharmony_ci		case SND_CTL_ELEM_TYPE_IEC958:
785d5ac70f0Sopenharmony_ci			trial.element_count = 1;
786d5ac70f0Sopenharmony_ci			trial.member_count = 1;
787d5ac70f0Sopenharmony_ci			trial.add_elem_set = add_iec958_elem_set;
788d5ac70f0Sopenharmony_ci			trial.check_elem_props = NULL;
789d5ac70f0Sopenharmony_ci			trial.change_elem_members = change_iec958_elem_members;
790d5ac70f0Sopenharmony_ci			trial.allocate_elem_set_tlv = NULL;
791d5ac70f0Sopenharmony_ci			trial.tlv_readable = false;
792d5ac70f0Sopenharmony_ci			break;
793d5ac70f0Sopenharmony_ci		case SND_CTL_ELEM_TYPE_INTEGER64:
794d5ac70f0Sopenharmony_ci		default:
795d5ac70f0Sopenharmony_ci			trial.element_count = 900;
796d5ac70f0Sopenharmony_ci			trial.member_count = 64;
797d5ac70f0Sopenharmony_ci			trial.add_elem_set = add_int64_elem_set;
798d5ac70f0Sopenharmony_ci			trial.check_elem_props = check_int64_elem_props;
799d5ac70f0Sopenharmony_ci			trial.change_elem_members = change_int64_elem_members;
800d5ac70f0Sopenharmony_ci			trial.allocate_elem_set_tlv =
801d5ac70f0Sopenharmony_ci						allocate_int64_elem_set_tlv;
802d5ac70f0Sopenharmony_ci			trial.tlv_readable = false;
803d5ac70f0Sopenharmony_ci			break;
804d5ac70f0Sopenharmony_ci		}
805d5ac70f0Sopenharmony_ci
806d5ac70f0Sopenharmony_ci		/* Test an operation to add an element set. */
807d5ac70f0Sopenharmony_ci		err = add_elem_set(&trial);
808d5ac70f0Sopenharmony_ci		if (err < 0) {
809d5ac70f0Sopenharmony_ci			printf("Fail to add an element set with %s type.\n",
810d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
811d5ac70f0Sopenharmony_ci			break;
812d5ac70f0Sopenharmony_ci		}
813d5ac70f0Sopenharmony_ci		err = check_event(&trial, SND_CTL_EVENT_MASK_ADD,
814d5ac70f0Sopenharmony_ci				  trial.element_count);
815d5ac70f0Sopenharmony_ci		if (err < 0) {
816d5ac70f0Sopenharmony_ci			printf("Fail to check some events to add elements with "
817d5ac70f0Sopenharmony_ci			       "%s type.\n",
818d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
819d5ac70f0Sopenharmony_ci			break;
820d5ac70f0Sopenharmony_ci		}
821d5ac70f0Sopenharmony_ci
822d5ac70f0Sopenharmony_ci		/* Check added elements are retrieved in a list. */
823d5ac70f0Sopenharmony_ci		err = check_elem_list(&trial);
824d5ac70f0Sopenharmony_ci		if (err < 0) {
825d5ac70f0Sopenharmony_ci			printf("Fail to list each element with %s type.\n",
826d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
827d5ac70f0Sopenharmony_ci			break;
828d5ac70f0Sopenharmony_ci		}
829d5ac70f0Sopenharmony_ci
830d5ac70f0Sopenharmony_ci		/* Check properties of each element in this element set. */
831d5ac70f0Sopenharmony_ci		err = check_elem_set_props(&trial);
832d5ac70f0Sopenharmony_ci		if (err < 0) {
833d5ac70f0Sopenharmony_ci			printf("Fail to check properties of each element with "
834d5ac70f0Sopenharmony_ci			       "%s type.\n",
835d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
836d5ac70f0Sopenharmony_ci			break;
837d5ac70f0Sopenharmony_ci		}
838d5ac70f0Sopenharmony_ci
839d5ac70f0Sopenharmony_ci		/*
840d5ac70f0Sopenharmony_ci		 * Test operations to change the state of members in each
841d5ac70f0Sopenharmony_ci		 * element in the element set.
842d5ac70f0Sopenharmony_ci		 */
843d5ac70f0Sopenharmony_ci		err = check_elems(&trial);
844d5ac70f0Sopenharmony_ci		if (err < 0) {
845d5ac70f0Sopenharmony_ci			printf("Fail to change status of each element with %s "
846d5ac70f0Sopenharmony_ci			       "type.\n",
847d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
848d5ac70f0Sopenharmony_ci			break;
849d5ac70f0Sopenharmony_ci		}
850d5ac70f0Sopenharmony_ci		err = check_event(&trial, SND_CTL_EVENT_MASK_VALUE,
851d5ac70f0Sopenharmony_ci				  trial.element_count);
852d5ac70f0Sopenharmony_ci		if (err < 0) {
853d5ac70f0Sopenharmony_ci			printf("Fail to check some events to change status of "
854d5ac70f0Sopenharmony_ci			       "each elements with %s type.\n",
855d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
856d5ac70f0Sopenharmony_ci			break;
857d5ac70f0Sopenharmony_ci		}
858d5ac70f0Sopenharmony_ci
859d5ac70f0Sopenharmony_ci		/*
860d5ac70f0Sopenharmony_ci		 * Test an operation to change TLV data of this element set,
861d5ac70f0Sopenharmony_ci		 * except for enumerated and IEC958 type.
862d5ac70f0Sopenharmony_ci		 */
863d5ac70f0Sopenharmony_ci		if (trial.allocate_elem_set_tlv != NULL) {
864d5ac70f0Sopenharmony_ci			err = check_tlv(&trial);
865d5ac70f0Sopenharmony_ci			if (err < 0) {
866d5ac70f0Sopenharmony_ci				printf("Fail to change TLV data of an element "
867d5ac70f0Sopenharmony_ci				       "set with %s type.\n",
868d5ac70f0Sopenharmony_ci				       snd_ctl_elem_type_name(trial.type));
869d5ac70f0Sopenharmony_ci				break;
870d5ac70f0Sopenharmony_ci			}
871d5ac70f0Sopenharmony_ci		}
872d5ac70f0Sopenharmony_ci
873d5ac70f0Sopenharmony_ci		/* Test an operation to remove elements in this element set. */
874d5ac70f0Sopenharmony_ci		err = snd_ctl_elem_remove(trial.handle, trial.id);
875d5ac70f0Sopenharmony_ci		if (err < 0) {
876d5ac70f0Sopenharmony_ci			printf("Fail to remove elements with %s type.\n",
877d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
878d5ac70f0Sopenharmony_ci			break;
879d5ac70f0Sopenharmony_ci		}
880d5ac70f0Sopenharmony_ci		err = check_event(&trial, SND_CTL_EVENT_MASK_REMOVE,
881d5ac70f0Sopenharmony_ci						  trial.element_count);
882d5ac70f0Sopenharmony_ci		if (err < 0) {
883d5ac70f0Sopenharmony_ci			printf("Fail to check some events to remove each "
884d5ac70f0Sopenharmony_ci			       "element with %s type.\n",
885d5ac70f0Sopenharmony_ci			       snd_ctl_elem_type_name(trial.type));
886d5ac70f0Sopenharmony_ci			break;
887d5ac70f0Sopenharmony_ci		}
888d5ac70f0Sopenharmony_ci	}
889d5ac70f0Sopenharmony_ci
890d5ac70f0Sopenharmony_ci	if (err < 0) {
891d5ac70f0Sopenharmony_ci		printf("%s\n", snd_strerror(err));
892d5ac70f0Sopenharmony_ci
893d5ac70f0Sopenharmony_ci		/* To ensure. */
894d5ac70f0Sopenharmony_ci		snd_ctl_elem_remove(trial.handle, trial.id);
895d5ac70f0Sopenharmony_ci		return EXIT_FAILURE;
896d5ac70f0Sopenharmony_ci	}
897d5ac70f0Sopenharmony_ci
898d5ac70f0Sopenharmony_ci	return EXIT_SUCCESS;
899d5ac70f0Sopenharmony_ci}
900