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 <stdio.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <stdbool.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <ctype.h>
38 #include <limits.h>
39 #include <time.h>
40 #include <poll.h>
41
42 #include <sys/ioctl.h>
43
44 #include <linux/ioctl.h>
45
46 #ifndef __force
47 #define __force
48 #endif
49
50 #ifndef __bitwise
51 #define __bitwise
52 #endif
53
54 #ifndef __user
55 #define __user
56 #endif
57
58 #include <sound/asound.h>
59
60 #include <tinyalsa/mixer.h>
61 #include <tinyalsa/plugin.h>
62
63 #include "mixer_io.h"
64
65 /** A mixer control.
66 * @ingroup libtinyalsa-mixer
67 */
68 struct mixer_ctl {
69 /** The mixer that the mixer control belongs to */
70 struct mixer *mixer;
71 /** Information on the control's value (i.e. type, number of values) */
72 struct snd_ctl_elem_info info;
73 /** A list of string representations of enumerated values (only valid for enumerated controls) */
74 char **ename;
75 /** Pointer to the group that the control belongs to */
76 struct mixer_ctl_group *grp;
77 };
78
79 struct mixer_ctl_group {
80 /** A continuous array of mixer controls */
81 struct mixer_ctl *ctl;
82 /** The number of mixer controls */
83 unsigned int count;
84 /** The number of events associated with this group */
85 unsigned int event_cnt;
86 /** The operations corresponding to this group */
87 const struct mixer_ops *ops;
88 /** Private data for storing group specific data */
89 void *data;
90 };
91
92 /** A mixer handle.
93 * @ingroup libtinyalsa-mixer
94 */
95 struct mixer {
96 /** File descriptor for the card */
97 int fd;
98 /** Card information */
99 struct snd_ctl_card_info card_info;
100 /* Hardware (kernel interface) mixer control group */
101 struct mixer_ctl_group *h_grp;
102 /* Virtual (Plugin interface) mixer control group */
103 struct mixer_ctl_group *v_grp;
104 /* Total count of mixer controls from both groups */
105 unsigned int total_count;
106 /* Flag to track if card information is already retrieved */
107 bool is_card_info_retrieved;
108
109 };
110
mixer_cleanup_control(struct mixer_ctl * ctl)111 static void mixer_cleanup_control(struct mixer_ctl *ctl)
112 {
113 unsigned int m;
114
115 if (ctl->ename) {
116 unsigned int max = ctl->info.value.enumerated.items;
117 for (m = 0; m < max; m++)
118 free(ctl->ename[m]);
119 free(ctl->ename);
120 }
121 }
122
mixer_grp_close(struct mixer * mixer,struct mixer_ctl_group * grp)123 static void mixer_grp_close(struct mixer *mixer, struct mixer_ctl_group *grp)
124 {
125 unsigned int n;
126
127 if (!grp)
128 return;
129
130 if (grp->ctl) {
131 for (n = 0; n < grp->count; n++)
132 mixer_cleanup_control(&grp->ctl[n]);
133 free(grp->ctl);
134 }
135
136 free(grp);
137
138 mixer->is_card_info_retrieved = false;
139 }
140
141 /** Closes a mixer returned by @ref mixer_open.
142 * @param mixer A mixer handle.
143 * @ingroup libtinyalsa-mixer
144 */
mixer_close(struct mixer * mixer)145 void mixer_close(struct mixer *mixer)
146 {
147 if (!mixer)
148 return;
149
150 if (mixer->fd >= 0 && mixer->h_grp)
151 mixer->h_grp->ops->close(mixer->h_grp->data);
152 mixer_grp_close(mixer, mixer->h_grp);
153
154 #ifdef TINYALSA_USES_PLUGINS
155 if (mixer->v_grp)
156 mixer->v_grp->ops->close(mixer->v_grp->data);
157 mixer_grp_close(mixer, mixer->v_grp);
158 #endif
159
160 free(mixer);
161
162 /* TODO: verify frees */
163 }
164
mixer_realloc_z(void * ptr,size_t curnum,size_t newnum,size_t size)165 static void *mixer_realloc_z(void *ptr, size_t curnum, size_t newnum, size_t size)
166 {
167 int8_t *newp;
168
169 newp = realloc(ptr, size * newnum);
170 if (!newp)
171 return NULL;
172
173 memset(newp + (curnum * size), 0, (newnum - curnum) * size);
174 return newp;
175 }
176
add_controls(struct mixer * mixer,struct mixer_ctl_group * grp)177 static int add_controls(struct mixer *mixer, struct mixer_ctl_group *grp)
178 {
179 struct snd_ctl_elem_list elist;
180 struct snd_ctl_elem_id *eid = NULL;
181 struct mixer_ctl *ctl;
182 const unsigned int old_count = grp->count;
183 unsigned int new_count;
184 unsigned int n;
185
186 memset(&elist, 0, sizeof(elist));
187 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
188 goto fail;
189
190 if (old_count == elist.count)
191 return 0; /* no new controls return unchanged */
192
193 if (old_count > elist.count)
194 return -1; /* driver has removed controls - this is bad */
195
196 ctl = mixer_realloc_z(grp->ctl, old_count, elist.count,
197 sizeof(struct mixer_ctl));
198 if (!ctl)
199 goto fail;
200
201 grp->ctl = ctl;
202
203 /* ALSA drivers are not supposed to remove or re-order controls that
204 * have already been created so we know that any new controls must
205 * be after the ones we have already collected
206 */
207 new_count = elist.count;
208 elist.space = new_count - old_count; /* controls we haven't seen before */
209 elist.offset = old_count; /* first control we haven't seen */
210
211 eid = calloc(elist.space, sizeof(struct snd_ctl_elem_id));
212 if (!eid)
213 goto fail;
214
215 elist.pids = eid;
216
217 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
218 goto fail;
219
220 for (n = old_count; n < new_count; n++) {
221 struct snd_ctl_elem_info *ei = &grp->ctl[n].info;
222 ei->id.numid = eid[n - old_count].numid;
223 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
224 goto fail_extend;
225 ctl[n].mixer = mixer;
226 ctl[n].grp = grp;
227 }
228
229 grp->count = new_count;
230 mixer->total_count += (new_count - old_count);
231
232 free(eid);
233 return 0;
234
235 fail_extend:
236 /* cleanup the control we failed on but leave the ones that were already
237 * added. Also no advantage to shrinking the resized memory block, we
238 * might want to extend the controls again later
239 */
240 mixer_cleanup_control(&ctl[n]);
241
242 grp->count = n; /* keep controls we successfully added */
243 mixer->total_count += (n - old_count);
244 /* fall through... */
245 fail:
246 free(eid);
247 return -1;
248 }
249
mixer_grp_open(struct mixer * mixer,unsigned int card,bool is_hw)250 static int mixer_grp_open(struct mixer *mixer, unsigned int card, bool is_hw)
251 {
252 struct mixer_ctl_group *grp = NULL;
253 const struct mixer_ops *ops = NULL;
254 void *data = NULL;
255 int fd, ret;
256
257 grp = calloc(1, sizeof(*grp));
258 if (!grp)
259 return -ENOMEM;
260
261 if (is_hw) {
262 mixer->fd = -1;
263 fd = mixer_hw_open(card, &data, &ops);
264 if (fd < 0) {
265 ret = fd;
266 goto err_open;
267 }
268 mixer->fd = fd;
269 mixer->h_grp = grp;
270 }
271 #ifdef TINYALSA_USES_PLUGINS
272 else {
273 ret = mixer_plugin_open(card, &data, &ops);
274 if (ret < 0)
275 goto err_open;
276 mixer->v_grp = grp;
277 }
278 #endif
279 grp->ops = ops;
280 grp->data = data;
281
282 if (!mixer->is_card_info_retrieved) {
283 ret = grp->ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO,
284 &mixer->card_info);
285 if (ret < 0)
286 goto err_card_info;
287 mixer->is_card_info_retrieved = true;
288 }
289
290 ret = add_controls(mixer, grp);
291 if (ret < 0)
292 goto err_card_info;
293
294 return 0;
295
296 err_card_info:
297 grp->ops->close(grp->data);
298
299 err_open:
300 free(grp);
301 return ret;
302
303 }
304
305 /** Opens a mixer for a given card.
306 * @param card The card to open the mixer for.
307 * @returns An initialized mixer handle.
308 * @ingroup libtinyalsa-mixer
309 */
mixer_open(unsigned int card)310 struct mixer *mixer_open(unsigned int card)
311 {
312 struct mixer *mixer = NULL;
313 int h_status, v_status = -1;
314
315 mixer = calloc(1, sizeof(*mixer));
316 if (!mixer)
317 goto fail;
318
319 h_status = mixer_grp_open(mixer, card, true);
320
321 #ifdef TINYALSA_USES_PLUGINS
322 v_status = mixer_grp_open(mixer, card, false);
323 #endif
324
325 /* both hw and virtual should fail for mixer_open to fail */
326 if (h_status < 0 && v_status < 0)
327 goto fail;
328
329 return mixer;
330
331 fail:
332 if (mixer)
333 mixer_close(mixer);
334
335 return NULL;
336 }
337
338 /** Some controls may not be present at boot time, e.g. controls from runtime
339 * loadable DSP firmware. This function adds any new controls that have appeared
340 * since mixer_open() or the last call to this function. This assumes a well-
341 * behaved codec driver that does not delete controls that already exists, so
342 * any added controls must be after the last one we already saw. Scanning only
343 * the new controls is much faster than calling mixer_close() then mixer_open()
344 * to re-scan all controls.
345 *
346 * NOTE: this invalidates any struct mixer_ctl pointers previously obtained
347 * from mixer_get_ctl() and mixer_get_ctl_by_name(). Either refresh all your
348 * stored pointers after calling mixer_update_ctls(), or (better) do not
349 * store struct mixer_ctl pointers, instead lookup the control by name or
350 * id only when you are about to use it. The overhead of lookup by id
351 * using mixer_get_ctl() is negligible.
352 * @param mixer An initialized mixer handle.
353 * @returns 0 on success, -1 on failure
354 */
mixer_add_new_ctls(struct mixer * mixer)355 int mixer_add_new_ctls(struct mixer *mixer)
356 {
357 int rc1 = 0, rc2 = 0;
358
359 if (!mixer)
360 return 0;
361
362 /* add the h_grp controls */
363 if (mixer->h_grp)
364 rc1 = add_controls(mixer, mixer->h_grp);
365
366 #ifdef TINYALSA_USES_PLUGINS
367 /* add the v_grp controls */
368 if (mixer->v_grp)
369 rc2 = add_controls(mixer, mixer->v_grp);
370 #endif
371
372 if (rc1 < 0)
373 return rc1;
374 if (rc2 < 0)
375 return rc2;
376
377 return 0;
378 }
379
380 /** Gets the name of the mixer's card.
381 * @param mixer An initialized mixer handle.
382 * @returns The name of the mixer's card.
383 * @ingroup libtinyalsa-mixer
384 */
mixer_get_name(const struct mixer * mixer)385 const char *mixer_get_name(const struct mixer *mixer)
386 {
387 if (!mixer) {
388 return NULL;
389 }
390
391 return (const char *)mixer->card_info.name;
392 }
393
394 /** Gets the number of mixer controls for a given mixer.
395 * @param mixer An initialized mixer handle.
396 * @returns The number of mixer controls for the given mixer.
397 * @ingroup libtinyalsa-mixer
398 */
mixer_get_num_ctls(const struct mixer * mixer)399 unsigned int mixer_get_num_ctls(const struct mixer *mixer)
400 {
401 if (!mixer)
402 return 0;
403
404 return mixer->total_count;
405 }
406
407 /** Gets the number of mixer controls, that go by a specified name, for a given mixer.
408 * @param mixer An initialized mixer handle.
409 * @param name The name of the mixer control
410 * @returns The number of mixer controls, specified by @p name, for the given mixer.
411 * @ingroup libtinyalsa-mixer
412 */
mixer_get_num_ctls_by_name(const struct mixer * mixer,const char * name)413 unsigned int mixer_get_num_ctls_by_name(const struct mixer *mixer, const char *name)
414 {
415 struct mixer_ctl_group *grp;
416 unsigned int n;
417 unsigned int count = 0;
418 struct mixer_ctl *ctl;
419
420 if (!mixer || !name) {
421 return 0;
422 }
423
424 if (mixer->h_grp) {
425 grp = mixer->h_grp;
426 ctl = grp->ctl;
427
428 for (n = 0; n < grp->count; n++)
429 if (!strcmp(name, (char*) ctl[n].info.id.name))
430 count++;
431 }
432 #ifdef TINYALSA_USES_PLUGINS
433 if (mixer->v_grp) {
434 grp = mixer->v_grp;
435 ctl = grp->ctl;
436
437 for (n = 0; n < grp->count; n++)
438 if (!strcmp(name, (char*) ctl[n].info.id.name))
439 count++;
440 }
441 #endif
442
443 return count;
444 }
445
446 /** Subscribes for the mixer events.
447 * @param mixer A mixer handle.
448 * @param subscribe value indicating subscribe or unsubscribe for events
449 * @returns On success, zero.
450 * On failure, non-zero.
451 * @ingroup libtinyalsa-mixer
452 */
mixer_subscribe_events(struct mixer * mixer,int subscribe)453 int mixer_subscribe_events(struct mixer *mixer, int subscribe)
454 {
455 struct mixer_ctl_group *grp;
456
457 if (!mixer) {
458 return -EINVAL;
459 }
460
461 if (mixer->h_grp) {
462 grp = mixer->h_grp;
463 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
464 return -1;
465 }
466
467 #ifdef TINYALSA_USES_PLUGINS
468 if (mixer->v_grp) {
469 grp = mixer->v_grp;
470 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
471 return -1;
472 }
473 #endif
474 return 0;
475 }
476
477 /** Wait for mixer events.
478 * @param mixer A mixer handle.
479 * @param timeout timout value
480 * @returns On success, 1.
481 * On failure, -errno.
482 * On timeout, 0
483 * @ingroup libtinyalsa-mixer
484 */
mixer_wait_event(struct mixer * mixer,int timeout)485 int mixer_wait_event(struct mixer *mixer, int timeout)
486 {
487 struct pollfd *pfd;
488 struct mixer_ctl_group *grp;
489 int count = 0, num_fds = 0, i, ret = 0;
490
491 if (!mixer) {
492 return -EINVAL;
493 }
494
495 if (mixer->fd >= 0)
496 num_fds++;
497
498 #ifdef TINYALSA_USES_PLUGINS
499 if (mixer->v_grp)
500 num_fds++;
501 #endif
502
503 pfd = (struct pollfd *)calloc(num_fds, sizeof(struct pollfd));
504 if (!pfd)
505 return -ENOMEM;
506
507 if (mixer->fd >= 0) {
508 pfd[count].fd = mixer->fd;
509 pfd[count].events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
510 count++;
511 }
512
513 #ifdef TINYALSA_USES_PLUGINS
514 if (mixer->v_grp) {
515 grp = mixer->v_grp;
516 if (!grp->ops->get_poll_fd(grp->data, pfd, count)) {
517 pfd[count].events = POLLIN | POLLERR | POLLNVAL;
518 count++;
519 }
520 }
521 #endif
522
523 if (!count)
524 goto exit;
525
526 for (;;) {
527 int err;
528 err = poll(pfd, count, timeout);
529 if (err < 0) {
530 ret = -errno;
531 goto exit;
532 }
533 if (!err)
534 goto exit;
535
536 for (i = 0; i < count; i++) {
537 if (pfd[i].revents & (POLLERR | POLLNVAL)) {
538 ret = -EIO;
539 goto exit;
540 }
541 if (pfd[i].revents & (POLLIN | POLLOUT)) {
542 if ((i == 0) && mixer->fd >= 0) {
543 grp = mixer->h_grp;
544 grp->event_cnt++;
545 }
546 #ifdef TINYALSA_USES_PLUGINS
547 else {
548 grp = mixer->v_grp;
549 grp->event_cnt++;
550 }
551 #endif
552 ret = 1;
553 goto exit;
554 }
555 }
556 }
557 exit:
558 free(pfd);
559 return ret;
560 }
561
562 /** Consume a mixer event.
563 * If mixer_subscribe_events has been called,
564 * mixer_wait_event will identify when a control value has changed.
565 * This function will clear a single event from the mixer so that
566 * further events can be alerted.
567 *
568 * @param mixer A mixer handle.
569 * @returns 1 on success. 0, if no pending event. -errno on failure.
570 * @ingroup libtinyalsa-mixer
571 */
mixer_consume_event(struct mixer * mixer)572 int mixer_consume_event(struct mixer *mixer)
573 {
574 struct mixer_ctl_event ev;
575 if (!mixer) {
576 return -EINVAL;
577 }
578
579 return mixer_read_event(mixer, &ev);
580 }
581
582 /** Read a mixer control event.
583 * Try to read an control event from mixer.
584 *
585 * @param mixer A mixer handle.
586 * @param event Output parameter. If there is an event read form the mixer, this function will fill
587 * the event data into it.
588 * @returns 1 on success. 0, if no pending event. -errno on failure.
589 * @ingroup libtinyalsa-mixer
590 */
mixer_read_event(struct mixer * mixer,struct mixer_ctl_event * event)591 int mixer_read_event(struct mixer *mixer, struct mixer_ctl_event *event)
592 {
593 struct mixer_ctl_group *grp = NULL;
594 struct snd_ctl_event ev;
595 ssize_t bytes = 0;
596
597 if (!mixer || !event) {
598 return -EINVAL;
599 }
600
601 if (mixer->h_grp) {
602 if (mixer->h_grp->event_cnt > 0) {
603 grp = mixer->h_grp;
604 }
605 }
606 #ifdef TINYALSA_USES_PLUGINS
607 if (mixer->v_grp) {
608 if (mixer->v_grp->event_cnt > 0) {
609 grp = mixer->v_grp;
610 }
611 }
612 #endif
613 if (grp) {
614 grp->event_cnt--;
615 bytes = grp->ops->read_event(grp->data, &ev, sizeof(ev));
616
617 if (bytes < 0) {
618 return -errno;
619 }
620
621 if (bytes == sizeof(*event)) {
622 memcpy(event, &ev, sizeof(*event));
623 return 1;
624 }
625 }
626
627 return 0;
628 }
629
mixer_grp_get_count(struct mixer_ctl_group * grp)630 static unsigned int mixer_grp_get_count(struct mixer_ctl_group *grp)
631 {
632 if (!grp)
633 return 0;
634
635 return grp->count;
636 }
637
638 /** Gets a mixer control handle, by the mixer control's id.
639 * For non-const access, see @ref mixer_get_ctl
640 * @param mixer An initialized mixer handle.
641 * @param id The control's id in the given mixer.
642 * @returns A handle to the mixer control.
643 * @ingroup libtinyalsa-mixer
644 */
mixer_get_ctl_const(const struct mixer * mixer,unsigned int id)645 const struct mixer_ctl *mixer_get_ctl_const(const struct mixer *mixer, unsigned int id)
646 {
647 unsigned int h_count;
648
649 if (!mixer || (id >= mixer->total_count))
650 return NULL;
651
652 h_count = mixer_grp_get_count(mixer->h_grp);
653
654 if (id < h_count)
655 return mixer->h_grp->ctl + id;
656 #ifdef TINYALSA_USES_PLUGINS
657 else {
658 unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
659 if ((id - h_count) < v_count)
660 return mixer->v_grp->ctl + (id - h_count);
661 }
662 #endif
663
664 return NULL;
665 }
666
667 /** Gets a mixer control handle, by the mixer control's id.
668 * For const access, see @ref mixer_get_ctl_const
669 * @param mixer An initialized mixer handle.
670 * @param id The control's id in the given mixer.
671 * @returns A handle to the mixer control.
672 * @ingroup libtinyalsa-mixer
673 */
mixer_get_ctl(struct mixer * mixer,unsigned int id)674 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
675 {
676 unsigned int h_count;
677
678 if (!mixer || (id >= mixer->total_count))
679 return NULL;
680
681 h_count = mixer_grp_get_count(mixer->h_grp);
682
683 if (id < h_count)
684 return mixer->h_grp->ctl + id;
685 #ifdef TINYALSA_USES_PLUGINS
686 else {
687 unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
688 if ((id - h_count) < v_count)
689 return mixer->v_grp->ctl + (id - h_count);
690 }
691 #endif
692 return NULL;
693 }
694
695 /** Gets the first instance of mixer control handle, by the mixer control's name.
696 * @param mixer An initialized mixer handle.
697 * @param name The control's name in the given mixer.
698 * @returns A handle to the mixer control.
699 * @ingroup libtinyalsa-mixer
700 */
mixer_get_ctl_by_name(struct mixer * mixer,const char * name)701 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
702 {
703 if (!mixer || !name) {
704 return NULL;
705 }
706
707 return mixer_get_ctl_by_name_and_index(mixer, name, 0);
708 }
709
710 /** Gets an instance of mixer control handle, by the mixer control's name and index.
711 * For instance, if two controls have the name of 'Volume', then and index of 1 would return the second control.
712 * @param mixer An initialized mixer handle.
713 * @param name The control's name in the given mixer.
714 * @param index The control's index.
715 * @returns A handle to the mixer control.
716 * @ingroup libtinyalsa-mixer
717 */
mixer_get_ctl_by_name_and_index(struct mixer * mixer,const char * name,unsigned int index)718 struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer,
719 const char *name,
720 unsigned int index)
721 {
722 struct mixer_ctl_group *grp;
723 unsigned int n;
724 struct mixer_ctl *ctl;
725
726 if (!mixer || !name) {
727 return NULL;
728 }
729
730 if (mixer->h_grp) {
731 grp = mixer->h_grp;
732 ctl = grp->ctl;
733
734 for (n = 0; n < grp->count; n++)
735 if (!strcmp(name, (char*) ctl[n].info.id.name)) {
736 if (index == 0) {
737 return ctl + n;
738 } else {
739 index--;
740 }
741 }
742 }
743
744 #ifdef TINYALSA_USES_PLUGINS
745 if (mixer->v_grp) {
746 grp = mixer->v_grp;
747 ctl = grp->ctl;
748
749 for (n = 0; n < grp->count; n++)
750 if (!strcmp(name, (char*) ctl[n].info.id.name)) {
751 if (index == 0) {
752 return ctl + n;
753 } else {
754 index--;
755 }
756 }
757 }
758 #endif
759 return NULL;
760 }
761
762 /** Updates the control's info.
763 * This is useful for a program that may be idle for a period of time.
764 * @param ctl An initialized control handle.
765 * @ingroup libtinyalsa-mixer
766 */
mixer_ctl_update(struct mixer_ctl * ctl)767 void mixer_ctl_update(struct mixer_ctl *ctl)
768 {
769 struct mixer_ctl_group *grp;
770
771 if (!ctl)
772 return;
773
774 grp = ctl->grp;
775 grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &ctl->info);
776 }
777
778 /** Checks the control for TLV Read/Write access.
779 * @param ctl An initialized control handle.
780 * @returns On success, non-zero.
781 * On failure, zero.
782 * @ingroup libtinyalsa-mixer
783 */
mixer_ctl_is_access_tlv_rw(const struct mixer_ctl * ctl)784 int mixer_ctl_is_access_tlv_rw(const struct mixer_ctl *ctl)
785 {
786 if (!ctl) {
787 return 0;
788 }
789
790 return (ctl->info.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
791 }
792
793 /** Gets the control's ID.
794 * @param ctl An initialized control handle.
795 * @returns On success, the control's ID is returned.
796 * On error, UINT_MAX is returned instead.
797 * @ingroup libtinyalsa-mixer
798 */
mixer_ctl_get_id(const struct mixer_ctl * ctl)799 unsigned int mixer_ctl_get_id(const struct mixer_ctl *ctl)
800 {
801 if (!ctl)
802 return UINT_MAX;
803
804 /* numid values start at 1, return a 0-base value that
805 * can be passed to mixer_get_ctl()
806 */
807 return ctl->info.id.numid - 1;
808 }
809
810 /** Gets the name of the control.
811 * @param ctl An initialized control handle.
812 * @returns On success, the name of the control.
813 * On error, NULL.
814 * @ingroup libtinyalsa-mixer
815 */
mixer_ctl_get_name(const struct mixer_ctl * ctl)816 const char *mixer_ctl_get_name(const struct mixer_ctl *ctl)
817 {
818 if (!ctl)
819 return NULL;
820
821 return (const char *)ctl->info.id.name;
822 }
823
824 /** Gets the value type of the control.
825 * @param ctl An initialized control handle
826 * @returns On success, the type of mixer control.
827 * On failure, it returns @ref MIXER_CTL_TYPE_UNKNOWN
828 * @ingroup libtinyalsa-mixer
829 */
mixer_ctl_get_type(const struct mixer_ctl * ctl)830 enum mixer_ctl_type mixer_ctl_get_type(const struct mixer_ctl *ctl)
831 {
832 if (!ctl)
833 return MIXER_CTL_TYPE_UNKNOWN;
834
835 switch (ctl->info.type) {
836 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
837 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
838 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
839 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
840 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
841 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
842 default: return MIXER_CTL_TYPE_UNKNOWN;
843 };
844 }
845
846 /** Gets the string that describes the value type of the control.
847 * @param ctl An initialized control handle
848 * @returns On success, a string describing type of mixer control.
849 * @ingroup libtinyalsa-mixer
850 */
mixer_ctl_get_type_string(const struct mixer_ctl * ctl)851 const char *mixer_ctl_get_type_string(const struct mixer_ctl *ctl)
852 {
853 if (!ctl)
854 return "";
855
856 switch (ctl->info.type) {
857 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
858 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
859 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
860 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
861 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
862 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
863 default: return "Unknown";
864 };
865 }
866
867 /** Gets the number of values in the control.
868 * @param ctl An initialized control handle
869 * @returns The number of values in the mixer control
870 * @ingroup libtinyalsa-mixer
871 */
mixer_ctl_get_num_values(const struct mixer_ctl * ctl)872 unsigned int mixer_ctl_get_num_values(const struct mixer_ctl *ctl)
873 {
874 if (!ctl)
875 return 0;
876
877 return ctl->info.count;
878 }
879
percent_to_int(const struct snd_ctl_elem_info * ei,int percent)880 static int percent_to_int(const struct snd_ctl_elem_info *ei, int percent)
881 {
882 if ((percent > 100) || (percent < 0)) {
883 return -EINVAL;
884 }
885
886 int range = (ei->value.integer.max - ei->value.integer.min);
887
888 return ei->value.integer.min + (range * percent) / 100;
889 }
890
int_to_percent(const struct snd_ctl_elem_info * ei,int value)891 static int int_to_percent(const struct snd_ctl_elem_info *ei, int value)
892 {
893 int range = (ei->value.integer.max - ei->value.integer.min);
894
895 if (range == 0)
896 return 0;
897
898 return ((value - ei->value.integer.min) * 100) / range;
899 }
900
901 /** Gets a percentage representation of a specified control value.
902 * @param ctl An initialized control handle.
903 * @param id The index of the value within the control.
904 * @returns On success, the percentage representation of the control value.
905 * On failure, -EINVAL is returned.
906 * @ingroup libtinyalsa-mixer
907 */
mixer_ctl_get_percent(const struct mixer_ctl * ctl,unsigned int id)908 int mixer_ctl_get_percent(const struct mixer_ctl *ctl, unsigned int id)
909 {
910 if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
911 return -EINVAL;
912
913 return int_to_percent(&ctl->info, mixer_ctl_get_value(ctl, id));
914 }
915
916 /** Sets the value of a control by percent, specified by the value index.
917 * @param ctl An initialized control handle.
918 * @param id The index of the value to set
919 * @param percent A percentage value between 0 and 100.
920 * @returns On success, zero is returned.
921 * On failure, non-zero is returned.
922 * @ingroup libtinyalsa-mixer
923 */
mixer_ctl_set_percent(struct mixer_ctl * ctl,unsigned int id,int percent)924 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
925 {
926 if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
927 return -EINVAL;
928
929 return mixer_ctl_set_value(ctl, id, percent_to_int(&ctl->info, percent));
930 }
931
932 /** Gets the value of a control.
933 * @param ctl An initialized control handle.
934 * @param id The index of the control value.
935 * @returns On success, the specified value is returned.
936 * On failure, -EINVAL is returned.
937 * @ingroup libtinyalsa-mixer
938 */
mixer_ctl_get_value(const struct mixer_ctl * ctl,unsigned int id)939 int mixer_ctl_get_value(const struct mixer_ctl *ctl, unsigned int id)
940 {
941 struct mixer_ctl_group *grp;
942 struct snd_ctl_elem_value ev;
943 int ret;
944
945 if (!ctl || (id >= ctl->info.count))
946 return -EINVAL;
947
948 grp = ctl->grp;
949 memset(&ev, 0, sizeof(ev));
950 ev.id.numid = ctl->info.id.numid;
951 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
952 if (ret < 0)
953 return ret;
954
955 switch (ctl->info.type) {
956 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
957 return !!ev.value.integer.value[id];
958
959 case SNDRV_CTL_ELEM_TYPE_INTEGER:
960 return ev.value.integer.value[id];
961
962 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
963 return ev.value.enumerated.item[id];
964
965 case SNDRV_CTL_ELEM_TYPE_BYTES:
966 return ev.value.bytes.data[id];
967
968 default:
969 return -EINVAL;
970 }
971
972 return 0;
973 }
974
975 /** Gets the contents of a control's value array.
976 * @param ctl An initialized control handle.
977 * @param array A pointer to write the array data to.
978 * The size of this array must be equal to the number of items in the array
979 * multiplied by the size of each item.
980 * @param count The number of items in the array.
981 * This parameter must match the number of items in the control.
982 * The number of items in the control may be accessed via @ref mixer_ctl_get_num_values
983 * @returns On success, zero.
984 * On failure, non-zero.
985 * @ingroup libtinyalsa-mixer
986 */
mixer_ctl_get_array(const struct mixer_ctl * ctl,void * array,size_t count)987 int mixer_ctl_get_array(const struct mixer_ctl *ctl, void *array, size_t count)
988 {
989 struct mixer_ctl_group *grp;
990 struct snd_ctl_elem_value ev;
991 int ret = 0;
992 size_t size;
993 void *source;
994
995 if (!ctl || !array || count == 0) {
996 return -EINVAL;
997 }
998
999 grp = ctl->grp;
1000
1001 if (count > ctl->info.count)
1002 return -EINVAL;
1003
1004 memset(&ev, 0, sizeof(ev));
1005 ev.id.numid = ctl->info.id.numid;
1006
1007 switch (ctl->info.type) {
1008 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1009 case SNDRV_CTL_ELEM_TYPE_INTEGER:
1010 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1011 if (ret < 0)
1012 return ret;
1013 size = sizeof(ev.value.integer.value[0]);
1014 source = ev.value.integer.value;
1015 break;
1016
1017 case SNDRV_CTL_ELEM_TYPE_BYTES:
1018 /* check if this is new bytes TLV */
1019 if (mixer_ctl_is_access_tlv_rw(ctl)) {
1020 struct snd_ctl_tlv *tlv;
1021 int ret;
1022
1023 if (count > SIZE_MAX - sizeof(*tlv))
1024 return -EINVAL;
1025
1026 tlv = calloc(1, sizeof(*tlv) + count);
1027 if (!tlv)
1028 return -ENOMEM;
1029
1030 tlv->numid = ctl->info.id.numid;
1031 tlv->length = count;
1032 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_READ, tlv);
1033
1034 source = tlv->tlv;
1035 memcpy(array, source, count);
1036
1037 free(tlv);
1038
1039 return ret;
1040 } else {
1041 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1042 if (ret < 0)
1043 return ret;
1044 size = sizeof(ev.value.bytes.data[0]);
1045 source = ev.value.bytes.data;
1046 break;
1047 }
1048
1049 case SNDRV_CTL_ELEM_TYPE_IEC958:
1050 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1051 if (ret < 0)
1052 return ret;
1053 size = sizeof(ev.value.iec958);
1054 source = &ev.value.iec958;
1055 break;
1056
1057 default:
1058 return -EINVAL;
1059 }
1060
1061 memcpy(array, source, size * count);
1062
1063 return 0;
1064 }
1065
1066 /** Sets the value of a control, specified by the value index.
1067 * @param ctl An initialized control handle.
1068 * @param id The index of the value within the control.
1069 * @param value The value to set.
1070 * This must be in a range specified by @ref mixer_ctl_get_range_min
1071 * and @ref mixer_ctl_get_range_max.
1072 * @returns On success, zero is returned.
1073 * On failure, non-zero is returned.
1074 * @ingroup libtinyalsa-mixer
1075 */
mixer_ctl_set_value(struct mixer_ctl * ctl,unsigned int id,int value)1076 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
1077 {
1078 struct mixer_ctl_group *grp;
1079 struct snd_ctl_elem_value ev;
1080 int ret;
1081
1082 if (!ctl || id >= ctl->info.count) {
1083 return -EINVAL;
1084 }
1085
1086 grp = ctl->grp;
1087 memset(&ev, 0, sizeof(ev));
1088 ev.id.numid = ctl->info.id.numid;
1089 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1090 if (ret < 0)
1091 return ret;
1092
1093 switch (ctl->info.type) {
1094 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1095 ev.value.integer.value[id] = !!value;
1096 break;
1097
1098 case SNDRV_CTL_ELEM_TYPE_INTEGER:
1099 ev.value.integer.value[id] = value;
1100 break;
1101
1102 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
1103 ev.value.enumerated.item[id] = value;
1104 break;
1105
1106 case SNDRV_CTL_ELEM_TYPE_BYTES:
1107 ev.value.bytes.data[id] = value;
1108 break;
1109
1110 default:
1111 return -EINVAL;
1112 }
1113
1114 return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
1115 }
1116
1117 /** Sets the contents of a control's value array.
1118 * @param ctl An initialized control handle.
1119 * @param array The array containing control values.
1120 * @param count The number of values in the array.
1121 * This must match the number of values in the control.
1122 * The number of values in a control may be accessed via @ref mixer_ctl_get_num_values
1123 * @returns On success, zero.
1124 * On failure, non-zero.
1125 * @ingroup libtinyalsa-mixer
1126 */
mixer_ctl_set_array(struct mixer_ctl * ctl,const void * array,size_t count)1127 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
1128 {
1129 struct mixer_ctl_group *grp;
1130 struct snd_ctl_elem_value ev;
1131 size_t size;
1132 void *dest;
1133
1134 if (!ctl || !array || count == 0) {
1135 return -EINVAL;
1136 }
1137
1138 grp = ctl->grp;
1139
1140 if (count > ctl->info.count)
1141 return -EINVAL;
1142
1143 memset(&ev, 0, sizeof(ev));
1144 ev.id.numid = ctl->info.id.numid;
1145
1146 switch (ctl->info.type) {
1147 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1148 case SNDRV_CTL_ELEM_TYPE_INTEGER:
1149 size = sizeof(ev.value.integer.value[0]);
1150 dest = ev.value.integer.value;
1151 break;
1152
1153 case SNDRV_CTL_ELEM_TYPE_BYTES:
1154 /* check if this is new bytes TLV */
1155 if (mixer_ctl_is_access_tlv_rw(ctl)) {
1156 struct snd_ctl_tlv *tlv;
1157 int ret = 0;
1158
1159 if (count > SIZE_MAX - sizeof(*tlv))
1160 return -EINVAL;
1161
1162 tlv = calloc(1, sizeof(*tlv) + count);
1163 if (!tlv)
1164 return -ENOMEM;
1165
1166 tlv->numid = ctl->info.id.numid;
1167 tlv->length = count;
1168 memcpy(tlv->tlv, array, count);
1169
1170 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
1171 free(tlv);
1172
1173 return ret;
1174 } else {
1175 size = sizeof(ev.value.bytes.data[0]);
1176 dest = ev.value.bytes.data;
1177 }
1178 break;
1179
1180 case SNDRV_CTL_ELEM_TYPE_IEC958:
1181 size = sizeof(ev.value.iec958);
1182 dest = &ev.value.iec958;
1183 break;
1184
1185 default:
1186 return -EINVAL;
1187 }
1188
1189 memcpy(dest, array, size * count);
1190
1191 return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
1192 }
1193
1194 /** Gets the minimum value of an control.
1195 * The control must have an integer type.
1196 * The type of the control can be checked with @ref mixer_ctl_get_type.
1197 * @param ctl An initialized control handle.
1198 * @returns On success, the minimum value of the control.
1199 * On failure, -EINVAL.
1200 * @ingroup libtinyalsa-mixer
1201 */
mixer_ctl_get_range_min(const struct mixer_ctl * ctl)1202 int mixer_ctl_get_range_min(const struct mixer_ctl *ctl)
1203 {
1204 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
1205 return -EINVAL;
1206 }
1207
1208 return ctl->info.value.integer.min;
1209 }
1210
1211 /** Gets the maximum value of an control.
1212 * The control must have an integer type.
1213 * The type of the control can be checked with @ref mixer_ctl_get_type.
1214 * @param ctl An initialized control handle.
1215 * @returns On success, the maximum value of the control.
1216 * On failure, -EINVAL.
1217 * @ingroup libtinyalsa-mixer
1218 */
mixer_ctl_get_range_max(const struct mixer_ctl * ctl)1219 int mixer_ctl_get_range_max(const struct mixer_ctl *ctl)
1220 {
1221 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
1222 return -EINVAL;
1223 }
1224
1225 return ctl->info.value.integer.max;
1226 }
1227
1228 /** Get the number of enumerated items in the control.
1229 * @param ctl An initialized control handle.
1230 * @returns The number of enumerated items in the control.
1231 * @ingroup libtinyalsa-mixer
1232 */
mixer_ctl_get_num_enums(const struct mixer_ctl * ctl)1233 unsigned int mixer_ctl_get_num_enums(const struct mixer_ctl *ctl)
1234 {
1235 if (!ctl) {
1236 return 0;
1237 }
1238
1239 return ctl->info.value.enumerated.items;
1240 }
1241
mixer_ctl_fill_enum_string(struct mixer_ctl * ctl)1242 static int mixer_ctl_fill_enum_string(struct mixer_ctl *ctl)
1243 {
1244 struct mixer_ctl_group *grp = ctl->grp;
1245 struct snd_ctl_elem_info tmp;
1246 unsigned int m;
1247 char **enames;
1248
1249 if (ctl->ename) {
1250 return 0;
1251 }
1252
1253 enames = calloc(ctl->info.value.enumerated.items, sizeof(char*));
1254 if (!enames)
1255 goto fail;
1256 for (m = 0; m < ctl->info.value.enumerated.items; m++) {
1257 memset(&tmp, 0, sizeof(tmp));
1258 tmp.id.numid = ctl->info.id.numid;
1259 tmp.value.enumerated.item = m;
1260 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
1261 goto fail;
1262 enames[m] = strdup(tmp.value.enumerated.name);
1263 if (!enames[m])
1264 goto fail;
1265 }
1266 ctl->ename = enames;
1267 return 0;
1268
1269 fail:
1270 if (enames) {
1271 for (m = 0; m < ctl->info.value.enumerated.items; m++) {
1272 if (enames[m]) {
1273 free(enames[m]);
1274 }
1275 }
1276 free(enames);
1277 }
1278 return -1;
1279 }
1280
1281 /** Gets the string representation of an enumerated item.
1282 * @param ctl An initialized control handle.
1283 * @param enum_id The index of the enumerated value.
1284 * @returns A string representation of the enumerated item.
1285 * @ingroup libtinyalsa-mixer
1286 */
mixer_ctl_get_enum_string(struct mixer_ctl * ctl,unsigned int enum_id)1287 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
1288 unsigned int enum_id)
1289 {
1290 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED ||
1291 enum_id >= ctl->info.value.enumerated.items) {
1292 return NULL;
1293 }
1294
1295 if (mixer_ctl_fill_enum_string(ctl) < 0) {
1296 return NULL;
1297 }
1298
1299 return (const char *)ctl->ename[enum_id];
1300 }
1301
1302 /** Set an enumeration value by string value.
1303 * @param ctl An enumerated mixer control.
1304 * @param string The string representation of an enumeration.
1305 * @returns On success, zero.
1306 * On failure, zero.
1307 * @ingroup libtinyalsa-mixer
1308 */
mixer_ctl_set_enum_by_string(struct mixer_ctl * ctl,const char * string)1309 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
1310 {
1311 struct mixer_ctl_group *grp;
1312 unsigned int i, num_enums;
1313 struct snd_ctl_elem_value ev;
1314 int ret;
1315
1316 if (!ctl || !string || ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
1317 return -EINVAL;
1318 }
1319
1320 if (mixer_ctl_fill_enum_string(ctl) < 0) {
1321 return -EINVAL;
1322 }
1323
1324 grp = ctl->grp;
1325 num_enums = ctl->info.value.enumerated.items;
1326 for (i = 0; i < num_enums; i++) {
1327 if (!strcmp(string, ctl->ename[i])) {
1328 memset(&ev, 0, sizeof(ev));
1329 ev.value.enumerated.item[0] = i;
1330 ev.id.numid = ctl->info.id.numid;
1331 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
1332 if (ret < 0)
1333 return ret;
1334 return 0;
1335 }
1336 }
1337
1338 return -EINVAL;
1339 }
1340