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