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