1 /* mixer.c
2 **
3 ** Copyright 2011, The Android Open Source Project
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions are met:
7 ** * Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
9 ** * Redistributions in binary form must reproduce the above copyright
10 ** notice, this list of conditions and the following disclaimer in the
11 ** documentation and/or other materials provided with the distribution.
12 ** * Neither the name of The Android Open Source Project nor the names of
13 ** its contributors may be used to endorse or promote products derived
14 ** from this software without specific prior written permission.
15 **
16 ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 ** DAMAGE.
27 */
28
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <ctype.h>
38 #include <poll.h>
39
40 #include <sys/ioctl.h>
41
42 #include <linux/ioctl.h>
43 #define __force
44 #define __bitwise
45 #define __user
46 #include <sound/asound.h>
47
48 #ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN
49 #define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44
50 #endif
51
52 #include <tinyalsa/asoundlib.h>
53
54 struct mixer_ctl {
55 struct mixer *mixer;
56 struct snd_ctl_elem_info *info;
57 char **ename;
58 bool info_retrieved;
59 };
60
61 struct mixer {
62 int fd;
63 struct snd_ctl_card_info card_info;
64 struct snd_ctl_elem_info *elem_info;
65 struct mixer_ctl *ctl;
66 unsigned int count;
67 };
68
mixer_close(struct mixer * mixer)69 void mixer_close(struct mixer *mixer)
70 {
71 unsigned int n,m;
72
73 if (!mixer)
74 return;
75
76 if (mixer->fd >= 0)
77 close(mixer->fd);
78
79 if (mixer->ctl) {
80 for (n = 0; n < mixer->count; n++) {
81 if (mixer->ctl[n].ename) {
82 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
83 for (m = 0; m < max; m++)
84 free(mixer->ctl[n].ename[m]);
85 free(mixer->ctl[n].ename);
86 }
87 }
88 free(mixer->ctl);
89 }
90
91 if (mixer->elem_info)
92 free(mixer->elem_info);
93
94 free(mixer);
95
96 /* TODO: verify frees */
97 }
98
mixer_open(unsigned int card)99 struct mixer *mixer_open(unsigned int card)
100 {
101 struct snd_ctl_elem_list elist;
102 struct snd_ctl_elem_id *eid = NULL;
103 struct mixer *mixer = NULL;
104 unsigned int n;
105 int fd;
106 char fn[256];
107
108 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
109 fd = open(fn, O_RDWR);
110 if (fd < 0)
111 return 0;
112
113 memset(&elist, 0, sizeof(elist));
114 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
115 goto fail;
116
117 mixer = calloc(1, sizeof(*mixer));
118 if (!mixer)
119 goto fail;
120
121 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
122 mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
123 if (!mixer->ctl || !mixer->elem_info)
124 goto fail;
125
126 if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
127 goto fail;
128
129 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
130 if (!eid)
131 goto fail;
132
133 mixer->count = elist.count;
134 mixer->fd = fd;
135 elist.space = mixer->count;
136 elist.pids = eid;
137 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
138 goto fail;
139
140 for (n = 0; n < mixer->count; n++) {
141 struct mixer_ctl *ctl = mixer->ctl + n;
142
143 ctl->mixer = mixer;
144 ctl->info = mixer->elem_info + n;
145 ctl->info->id.numid = eid[n].numid;
146 strncpy((char *)ctl->info->id.name, (char *)eid[n].name,
147 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
148 ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
149 }
150
151 free(eid);
152 return mixer;
153
154 fail:
155 /* TODO: verify frees in failure case */
156 if (eid)
157 free(eid);
158 if (mixer)
159 mixer_close(mixer);
160 else if (fd >= 0)
161 close(fd);
162 return 0;
163 }
164
mixer_ctl_get_elem_info(struct mixer_ctl * ctl)165 static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
166 {
167 if (!ctl->info_retrieved) {
168 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info) < 0)
169 return false;
170 ctl->info_retrieved = true;
171 }
172
173 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED || ctl->ename)
174 return true;
175
176 struct snd_ctl_elem_info tmp;
177 char** enames = calloc(ctl->info->value.enumerated.items, sizeof(char*));
178 if (!enames)
179 return false;
180
181 for (unsigned int i = 0; i < ctl->info->value.enumerated.items; i++) {
182 memset(&tmp, 0, sizeof(tmp));
183 tmp.id.numid = ctl->info->id.numid;
184 tmp.value.enumerated.item = i;
185 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
186 goto fail;
187 enames[i] = strdup(tmp.value.enumerated.name);
188 if (!enames[i])
189 goto fail;
190 }
191 ctl->ename = enames;
192 return true;
193
194 fail:
195 free(enames);
196 return false;
197 }
198
mixer_get_name(struct mixer * mixer)199 const char *mixer_get_name(struct mixer *mixer)
200 {
201 return (const char *)mixer->card_info.name;
202 }
203
mixer_get_num_ctls(struct mixer * mixer)204 unsigned int mixer_get_num_ctls(struct mixer *mixer)
205 {
206 if (!mixer)
207 return 0;
208
209 return mixer->count;
210 }
211
mixer_get_ctl(struct mixer * mixer,unsigned int id)212 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
213 {
214 struct mixer_ctl *ctl;
215
216 if (!mixer || (id >= mixer->count))
217 return NULL;
218
219 ctl = mixer->ctl + id;
220 if (!mixer_ctl_get_elem_info(ctl))
221 return NULL;
222
223 return ctl;
224 }
225
mixer_get_ctl_by_name(struct mixer * mixer,const char * name)226 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
227 {
228 unsigned int n;
229
230 if (!mixer)
231 return NULL;
232
233 for (n = 0; n < mixer->count; n++)
234 if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
235 return mixer_get_ctl(mixer, n);
236
237 return NULL;
238 }
239
mixer_ctl_update(struct mixer_ctl * ctl)240 void mixer_ctl_update(struct mixer_ctl *ctl)
241 {
242 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
243 }
244
mixer_ctl_get_name(struct mixer_ctl * ctl)245 const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
246 {
247 if (!ctl)
248 return NULL;
249
250 return (const char *)ctl->info->id.name;
251 }
252
mixer_ctl_get_type(struct mixer_ctl * ctl)253 enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
254 {
255 if (!ctl)
256 return MIXER_CTL_TYPE_UNKNOWN;
257
258 switch (ctl->info->type) {
259 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
260 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
261 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
262 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
263 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
264 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
265 default: return MIXER_CTL_TYPE_UNKNOWN;
266 };
267 }
268
mixer_ctl_get_type_string(struct mixer_ctl * ctl)269 const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
270 {
271 if (!ctl)
272 return "";
273
274 switch (ctl->info->type) {
275 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
276 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
277 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
278 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
279 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
280 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
281 default: return "Unknown";
282 };
283 }
284
mixer_ctl_get_num_values(struct mixer_ctl * ctl)285 unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
286 {
287 if (!ctl)
288 return 0;
289
290 return ctl->info->count;
291 }
292
percent_to_int(struct snd_ctl_elem_info * ei,int percent)293 static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
294 {
295 int range;
296
297 if (percent > 100)
298 percent = 100;
299 else if (percent < 0)
300 percent = 0;
301
302 range = (ei->value.integer.max - ei->value.integer.min);
303
304 return ei->value.integer.min + (range * percent) / 100;
305 }
306
int_to_percent(struct snd_ctl_elem_info * ei,int value)307 static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
308 {
309 int range = (ei->value.integer.max - ei->value.integer.min);
310
311 if (range == 0)
312 return 0;
313
314 return ((value - ei->value.integer.min) / range) * 100;
315 }
316
mixer_ctl_get_percent(struct mixer_ctl * ctl,unsigned int id)317 int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
318 {
319 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
320 return -EINVAL;
321
322 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
323 }
324
mixer_ctl_set_percent(struct mixer_ctl * ctl,unsigned int id,int percent)325 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
326 {
327 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
328 return -EINVAL;
329
330 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
331 }
332
mixer_ctl_get_value(struct mixer_ctl * ctl,unsigned int id)333 int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
334 {
335 struct snd_ctl_elem_value ev;
336 int ret;
337
338 if (!ctl || (id >= ctl->info->count))
339 return -EINVAL;
340
341 memset(&ev, 0, sizeof(ev));
342 ev.id.numid = ctl->info->id.numid;
343 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
344 if (ret < 0)
345 return ret;
346
347 switch (ctl->info->type) {
348 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
349 return !!ev.value.integer.value[id];
350
351 case SNDRV_CTL_ELEM_TYPE_INTEGER:
352 return ev.value.integer.value[id];
353
354 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
355 return ev.value.enumerated.item[id];
356
357 case SNDRV_CTL_ELEM_TYPE_BYTES:
358 return ev.value.bytes.data[id];
359
360 default:
361 return -EINVAL;
362 }
363
364 return 0;
365 }
366
mixer_ctl_is_access_tlv_rw(struct mixer_ctl * ctl)367 int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
368 {
369 return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
370 }
371
mixer_ctl_get_array(struct mixer_ctl * ctl,void * array,size_t count)372 int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
373 {
374 struct snd_ctl_elem_value ev;
375 int ret = 0;
376 size_t size;
377 void *source;
378 size_t total_count;
379
380 if ((!ctl) || !count || !array)
381 return -EINVAL;
382
383 total_count = ctl->info->count;
384
385 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
386 mixer_ctl_is_access_tlv_rw(ctl)) {
387 /* Additional two words is for the TLV header */
388 total_count += TLV_HEADER_SIZE;
389 }
390
391 if (count > total_count)
392 return -EINVAL;
393
394 memset(&ev, 0, sizeof(ev));
395 ev.id.numid = ctl->info->id.numid;
396
397 switch (ctl->info->type) {
398 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
399 case SNDRV_CTL_ELEM_TYPE_INTEGER:
400 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
401 if (ret < 0)
402 return ret;
403 size = sizeof(ev.value.integer.value[0]);
404 source = ev.value.integer.value;
405 break;
406
407 case SNDRV_CTL_ELEM_TYPE_BYTES:
408 /* check if this is new bytes TLV */
409 if (mixer_ctl_is_access_tlv_rw(ctl)) {
410 struct snd_ctl_tlv *tlv;
411 int ret;
412
413 if (count > SIZE_MAX - sizeof(*tlv))
414 return -EINVAL;
415 tlv = calloc(1, sizeof(*tlv) + count);
416 if (!tlv)
417 return -ENOMEM;
418 tlv->numid = ctl->info->id.numid;
419 tlv->length = count;
420 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
421
422 source = tlv->tlv;
423 memcpy(array, source, count);
424
425 free(tlv);
426
427 return ret;
428 } else {
429 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
430 if (ret < 0)
431 return ret;
432 size = sizeof(ev.value.bytes.data[0]);
433 source = ev.value.bytes.data;
434 break;
435 }
436
437 case SNDRV_CTL_ELEM_TYPE_IEC958:
438 size = sizeof(ev.value.iec958);
439 source = &ev.value.iec958;
440 break;
441
442 default:
443 return -EINVAL;
444 }
445
446 memcpy(array, source, size * count);
447
448 return 0;
449 }
450
mixer_ctl_set_value(struct mixer_ctl * ctl,unsigned int id,int value)451 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
452 {
453 struct snd_ctl_elem_value ev;
454 int ret;
455
456 if (!ctl || (id >= ctl->info->count))
457 return -EINVAL;
458
459 memset(&ev, 0, sizeof(ev));
460 ev.id.numid = ctl->info->id.numid;
461 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
462 if (ret < 0)
463 return ret;
464
465 switch (ctl->info->type) {
466 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
467 ev.value.integer.value[id] = !!value;
468 break;
469
470 case SNDRV_CTL_ELEM_TYPE_INTEGER:
471 ev.value.integer.value[id] = value;
472 break;
473
474 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
475 ev.value.enumerated.item[id] = value;
476 break;
477
478 case SNDRV_CTL_ELEM_TYPE_BYTES:
479 ev.value.bytes.data[id] = value;
480 break;
481
482 default:
483 return -EINVAL;
484 }
485
486 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
487 }
488
mixer_ctl_set_array(struct mixer_ctl * ctl,const void * array,size_t count)489 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
490 {
491 struct snd_ctl_elem_value ev;
492 size_t size;
493 void *dest;
494 size_t total_count;
495
496 if ((!ctl) || !count || !array)
497 return -EINVAL;
498
499 total_count = ctl->info->count;
500
501 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
502 mixer_ctl_is_access_tlv_rw(ctl)) {
503 /* Additional two words is for the TLV header */
504 total_count += TLV_HEADER_SIZE;
505 }
506
507 if (count > total_count)
508 return -EINVAL;
509
510 memset(&ev, 0, sizeof(ev));
511 ev.id.numid = ctl->info->id.numid;
512
513 switch (ctl->info->type) {
514 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
515 case SNDRV_CTL_ELEM_TYPE_INTEGER:
516 size = sizeof(ev.value.integer.value[0]);
517 dest = ev.value.integer.value;
518 break;
519
520 case SNDRV_CTL_ELEM_TYPE_BYTES:
521 /* check if this is new bytes TLV */
522 if (mixer_ctl_is_access_tlv_rw(ctl)) {
523 struct snd_ctl_tlv *tlv;
524 int ret = 0;
525 if (count > SIZE_MAX - sizeof(*tlv))
526 return -EINVAL;
527 tlv = calloc(1, sizeof(*tlv) + count);
528 if (!tlv)
529 return -ENOMEM;
530 tlv->numid = ctl->info->id.numid;
531 tlv->length = count;
532 memcpy(tlv->tlv, array, count);
533
534 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
535 free(tlv);
536
537 return ret;
538 } else {
539 size = sizeof(ev.value.bytes.data[0]);
540 dest = ev.value.bytes.data;
541 }
542 break;
543
544 case SNDRV_CTL_ELEM_TYPE_IEC958:
545 size = sizeof(ev.value.iec958);
546 dest = &ev.value.iec958;
547 break;
548
549 default:
550 return -EINVAL;
551 }
552
553 memcpy(dest, array, size * count);
554
555 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
556 }
557
mixer_ctl_get_range_min(struct mixer_ctl * ctl)558 int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
559 {
560 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
561 return -EINVAL;
562
563 return ctl->info->value.integer.min;
564 }
565
mixer_ctl_get_range_max(struct mixer_ctl * ctl)566 int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
567 {
568 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
569 return -EINVAL;
570
571 return ctl->info->value.integer.max;
572 }
573
mixer_ctl_get_num_enums(struct mixer_ctl * ctl)574 unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
575 {
576 if (!ctl)
577 return 0;
578
579 return ctl->info->value.enumerated.items;
580 }
581
mixer_ctl_get_enum_string(struct mixer_ctl * ctl,unsigned int enum_id)582 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
583 unsigned int enum_id)
584 {
585 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
586 (enum_id >= ctl->info->value.enumerated.items))
587 return NULL;
588
589 return (const char *)ctl->ename[enum_id];
590 }
591
mixer_ctl_set_enum_by_string(struct mixer_ctl * ctl,const char * string)592 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
593 {
594 unsigned int i, num_enums;
595 struct snd_ctl_elem_value ev;
596 int ret;
597
598 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
599 return -EINVAL;
600
601 num_enums = ctl->info->value.enumerated.items;
602 for (i = 0; i < num_enums; i++) {
603 if (!strcmp(string, ctl->ename[i])) {
604 memset(&ev, 0, sizeof(ev));
605 ev.value.enumerated.item[0] = i;
606 ev.id.numid = ctl->info->id.numid;
607 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
608 if (ret < 0)
609 return ret;
610 return 0;
611 }
612 }
613
614 return -EINVAL;
615 }
616
617 /** Subscribes for the mixer events.
618 * @param mixer A mixer handle.
619 * @param subscribe value indicating subscribe or unsubscribe for events
620 * @returns On success, zero.
621 * On failure, non-zero.
622 * @ingroup libtinyalsa-mixer
623 */
mixer_subscribe_events(struct mixer * mixer,int subscribe)624 int mixer_subscribe_events(struct mixer *mixer, int subscribe)
625 {
626 if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
627 return -1;
628 }
629 return 0;
630 }
631
632 /** Wait for mixer events.
633 * @param mixer A mixer handle.
634 * @param timeout timout value
635 * @returns On success, 1.
636 * On failure, -errno.
637 * On timeout, 0
638 * @ingroup libtinyalsa-mixer
639 */
mixer_wait_event(struct mixer * mixer,int timeout)640 int mixer_wait_event(struct mixer *mixer, int timeout)
641 {
642 struct pollfd pfd;
643
644 pfd.fd = mixer->fd;
645 pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
646
647 for (;;) {
648 int err;
649 err = poll(&pfd, 1, timeout);
650 if (err < 0)
651 return -errno;
652 if (!err)
653 return 0;
654 if (pfd.revents & (POLLERR | POLLNVAL))
655 return -EIO;
656 if (pfd.revents & (POLLIN | POLLOUT))
657 return 1;
658 }
659 }
660
661 /** Consume a mixer event.
662 * If mixer_subscribe_events has been called,
663 * mixer_wait_event will identify when a control value has changed.
664 * This function will clear a single event from the mixer so that
665 * further events can be alerted.
666 *
667 * @param mixer A mixer handle.
668 * @returns 0 on success. -errno on failure.
669 * @ingroup libtinyalsa-mixer
670 */
mixer_consume_event(struct mixer * mixer)671 int mixer_consume_event(struct mixer *mixer) {
672 struct snd_ctl_event ev;
673 ssize_t count = read(mixer->fd, &ev, sizeof(ev));
674 // Exporting the actual event would require exposing snd_ctl_event
675 // via the header file, and all associated structs.
676 // The events generally tell you exactly which value changed,
677 // but reading values you're interested isn't hard and simplifies
678 // the interface greatly.
679 return (count >= 0) ? 0 : -errno;
680 }
681