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 }
739
740 #ifdef TINYALSA_USES_PLUGINS
741 if (mixer->v_grp) {
742 grp = mixer->v_grp;
743 ctl = grp->ctl;
744
745 for (n = 0; n < grp->count; n++)
746 if (!strcmp(name, (char*) ctl[n].info.id.name))
747 if (index-- == 0)
748 return ctl + n;
749 }
750 #endif
751 return NULL;
752 }
753
754 /** Updates the control's info.
755 * This is useful for a program that may be idle for a period of time.
756 * @param ctl An initialized control handle.
757 * @ingroup libtinyalsa-mixer
758 */
mixer_ctl_update(struct mixer_ctl * ctl)759 void mixer_ctl_update(struct mixer_ctl *ctl)
760 {
761 struct mixer_ctl_group *grp;
762
763 if (!ctl)
764 return;
765
766 grp = ctl->grp;
767 grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &ctl->info);
768 }
769
770 /** Checks the control for TLV Read/Write access.
771 * @param ctl An initialized control handle.
772 * @returns On success, non-zero.
773 * On failure, zero.
774 * @ingroup libtinyalsa-mixer
775 */
mixer_ctl_is_access_tlv_rw(const struct mixer_ctl * ctl)776 int mixer_ctl_is_access_tlv_rw(const struct mixer_ctl *ctl)
777 {
778 if (!ctl) {
779 return 0;
780 }
781
782 return (ctl->info.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
783 }
784
785 /** Gets the control's ID.
786 * @param ctl An initialized control handle.
787 * @returns On success, the control's ID is returned.
788 * On error, UINT_MAX is returned instead.
789 * @ingroup libtinyalsa-mixer
790 */
mixer_ctl_get_id(const struct mixer_ctl * ctl)791 unsigned int mixer_ctl_get_id(const struct mixer_ctl *ctl)
792 {
793 if (!ctl)
794 return UINT_MAX;
795
796 /* numid values start at 1, return a 0-base value that
797 * can be passed to mixer_get_ctl()
798 */
799 return ctl->info.id.numid - 1;
800 }
801
802 /** Gets the name of the control.
803 * @param ctl An initialized control handle.
804 * @returns On success, the name of the control.
805 * On error, NULL.
806 * @ingroup libtinyalsa-mixer
807 */
mixer_ctl_get_name(const struct mixer_ctl * ctl)808 const char *mixer_ctl_get_name(const struct mixer_ctl *ctl)
809 {
810 if (!ctl)
811 return NULL;
812
813 return (const char *)ctl->info.id.name;
814 }
815
816 /** Gets the value type of the control.
817 * @param ctl An initialized control handle
818 * @returns On success, the type of mixer control.
819 * On failure, it returns @ref MIXER_CTL_TYPE_UNKNOWN
820 * @ingroup libtinyalsa-mixer
821 */
mixer_ctl_get_type(const struct mixer_ctl * ctl)822 enum mixer_ctl_type mixer_ctl_get_type(const struct mixer_ctl *ctl)
823 {
824 if (!ctl)
825 return MIXER_CTL_TYPE_UNKNOWN;
826
827 switch (ctl->info.type) {
828 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
829 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
830 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
831 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
832 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
833 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
834 default: return MIXER_CTL_TYPE_UNKNOWN;
835 };
836 }
837
838 /** Gets the string that describes the value type of the control.
839 * @param ctl An initialized control handle
840 * @returns On success, a string describing type of mixer control.
841 * @ingroup libtinyalsa-mixer
842 */
mixer_ctl_get_type_string(const struct mixer_ctl * ctl)843 const char *mixer_ctl_get_type_string(const struct mixer_ctl *ctl)
844 {
845 if (!ctl)
846 return "";
847
848 switch (ctl->info.type) {
849 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
850 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
851 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
852 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
853 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
854 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
855 default: return "Unknown";
856 };
857 }
858
859 /** Gets the number of values in the control.
860 * @param ctl An initialized control handle
861 * @returns The number of values in the mixer control
862 * @ingroup libtinyalsa-mixer
863 */
mixer_ctl_get_num_values(const struct mixer_ctl * ctl)864 unsigned int mixer_ctl_get_num_values(const struct mixer_ctl *ctl)
865 {
866 if (!ctl)
867 return 0;
868
869 return ctl->info.count;
870 }
871
percent_to_int(const struct snd_ctl_elem_info * ei,int percent)872 static int percent_to_int(const struct snd_ctl_elem_info *ei, int percent)
873 {
874 if ((percent > 100) || (percent < 0)) {
875 return -EINVAL;
876 }
877
878 int range = (ei->value.integer.max - ei->value.integer.min);
879
880 return ei->value.integer.min + (range * percent) / 100;
881 }
882
int_to_percent(const struct snd_ctl_elem_info * ei,int value)883 static int int_to_percent(const struct snd_ctl_elem_info *ei, int value)
884 {
885 int range = (ei->value.integer.max - ei->value.integer.min);
886
887 if (range == 0)
888 return 0;
889
890 return ((value - ei->value.integer.min) * 100) / range;
891 }
892
893 /** Gets a percentage representation of a specified control value.
894 * @param ctl An initialized control handle.
895 * @param id The index of the value within the control.
896 * @returns On success, the percentage representation of the control value.
897 * On failure, -EINVAL is returned.
898 * @ingroup libtinyalsa-mixer
899 */
mixer_ctl_get_percent(const struct mixer_ctl * ctl,unsigned int id)900 int mixer_ctl_get_percent(const struct mixer_ctl *ctl, unsigned int id)
901 {
902 if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
903 return -EINVAL;
904
905 return int_to_percent(&ctl->info, mixer_ctl_get_value(ctl, id));
906 }
907
908 /** Sets the value of a control by percent, specified by the value index.
909 * @param ctl An initialized control handle.
910 * @param id The index of the value to set
911 * @param percent A percentage value between 0 and 100.
912 * @returns On success, zero is returned.
913 * On failure, non-zero is returned.
914 * @ingroup libtinyalsa-mixer
915 */
mixer_ctl_set_percent(struct mixer_ctl * ctl,unsigned int id,int percent)916 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
917 {
918 if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
919 return -EINVAL;
920
921 return mixer_ctl_set_value(ctl, id, percent_to_int(&ctl->info, percent));
922 }
923
924 /** Gets the value of a control.
925 * @param ctl An initialized control handle.
926 * @param id The index of the control value.
927 * @returns On success, the specified value is returned.
928 * On failure, -EINVAL is returned.
929 * @ingroup libtinyalsa-mixer
930 */
mixer_ctl_get_value(const struct mixer_ctl * ctl,unsigned int id)931 int mixer_ctl_get_value(const struct mixer_ctl *ctl, unsigned int id)
932 {
933 struct mixer_ctl_group *grp;
934 struct snd_ctl_elem_value ev;
935 int ret;
936
937 if (!ctl || (id >= ctl->info.count))
938 return -EINVAL;
939
940 grp = ctl->grp;
941 memset(&ev, 0, sizeof(ev));
942 ev.id.numid = ctl->info.id.numid;
943 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
944 if (ret < 0)
945 return ret;
946
947 switch (ctl->info.type) {
948 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
949 return !!ev.value.integer.value[id];
950
951 case SNDRV_CTL_ELEM_TYPE_INTEGER:
952 return ev.value.integer.value[id];
953
954 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
955 return ev.value.enumerated.item[id];
956
957 case SNDRV_CTL_ELEM_TYPE_BYTES:
958 return ev.value.bytes.data[id];
959
960 default:
961 return -EINVAL;
962 }
963
964 return 0;
965 }
966
967 /** Gets the contents of a control's value array.
968 * @param ctl An initialized control handle.
969 * @param array A pointer to write the array data to.
970 * The size of this array must be equal to the number of items in the array
971 * multiplied by the size of each item.
972 * @param count The number of items in the array.
973 * This parameter must match the number of items in the control.
974 * The number of items in the control may be accessed via @ref mixer_ctl_get_num_values
975 * @returns On success, zero.
976 * On failure, non-zero.
977 * @ingroup libtinyalsa-mixer
978 */
mixer_ctl_get_array(const struct mixer_ctl * ctl,void * array,size_t count)979 int mixer_ctl_get_array(const struct mixer_ctl *ctl, void *array, size_t count)
980 {
981 struct mixer_ctl_group *grp;
982 struct snd_ctl_elem_value ev;
983 int ret = 0;
984 size_t size;
985 void *source;
986
987 if (!ctl || !array || count == 0) {
988 return -EINVAL;
989 }
990
991 grp = ctl->grp;
992
993 if (count > ctl->info.count)
994 return -EINVAL;
995
996 memset(&ev, 0, sizeof(ev));
997 ev.id.numid = ctl->info.id.numid;
998
999 switch (ctl->info.type) {
1000 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1001 case SNDRV_CTL_ELEM_TYPE_INTEGER:
1002 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1003 if (ret < 0)
1004 return ret;
1005 size = sizeof(ev.value.integer.value[0]);
1006 source = ev.value.integer.value;
1007 break;
1008
1009 case SNDRV_CTL_ELEM_TYPE_BYTES:
1010 /* check if this is new bytes TLV */
1011 if (mixer_ctl_is_access_tlv_rw(ctl)) {
1012 struct snd_ctl_tlv *tlv;
1013 int ret;
1014
1015 if (count > SIZE_MAX - sizeof(*tlv))
1016 return -EINVAL;
1017
1018 tlv = calloc(1, sizeof(*tlv) + count);
1019 if (!tlv)
1020 return -ENOMEM;
1021
1022 tlv->numid = ctl->info.id.numid;
1023 tlv->length = count;
1024 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_READ, tlv);
1025
1026 source = tlv->tlv;
1027 memcpy(array, source, count);
1028
1029 free(tlv);
1030
1031 return ret;
1032 } else {
1033 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1034 if (ret < 0)
1035 return ret;
1036 size = sizeof(ev.value.bytes.data[0]);
1037 source = ev.value.bytes.data;
1038 break;
1039 }
1040
1041 case SNDRV_CTL_ELEM_TYPE_IEC958:
1042 size = sizeof(ev.value.iec958);
1043 source = &ev.value.iec958;
1044 break;
1045
1046 default:
1047 return -EINVAL;
1048 }
1049
1050 memcpy(array, source, size * count);
1051
1052 return 0;
1053 }
1054
1055 /** Sets the value of a control, specified by the value index.
1056 * @param ctl An initialized control handle.
1057 * @param id The index of the value within the control.
1058 * @param value The value to set.
1059 * This must be in a range specified by @ref mixer_ctl_get_range_min
1060 * and @ref mixer_ctl_get_range_max.
1061 * @returns On success, zero is returned.
1062 * On failure, non-zero is returned.
1063 * @ingroup libtinyalsa-mixer
1064 */
mixer_ctl_set_value(struct mixer_ctl * ctl,unsigned int id,int value)1065 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
1066 {
1067 struct mixer_ctl_group *grp;
1068 struct snd_ctl_elem_value ev;
1069 int ret;
1070
1071 if (!ctl || id >= ctl->info.count) {
1072 return -EINVAL;
1073 }
1074
1075 grp = ctl->grp;
1076 memset(&ev, 0, sizeof(ev));
1077 ev.id.numid = ctl->info.id.numid;
1078 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1079 if (ret < 0)
1080 return ret;
1081
1082 switch (ctl->info.type) {
1083 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1084 ev.value.integer.value[id] = !!value;
1085 break;
1086
1087 case SNDRV_CTL_ELEM_TYPE_INTEGER:
1088 ev.value.integer.value[id] = value;
1089 break;
1090
1091 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
1092 ev.value.enumerated.item[id] = value;
1093 break;
1094
1095 case SNDRV_CTL_ELEM_TYPE_BYTES:
1096 ev.value.bytes.data[id] = value;
1097 break;
1098
1099 default:
1100 return -EINVAL;
1101 }
1102
1103 return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
1104 }
1105
1106 /** Sets the contents of a control's value array.
1107 * @param ctl An initialized control handle.
1108 * @param array The array containing control values.
1109 * @param count The number of values in the array.
1110 * This must match the number of values in the control.
1111 * The number of values in a control may be accessed via @ref mixer_ctl_get_num_values
1112 * @returns On success, zero.
1113 * On failure, non-zero.
1114 * @ingroup libtinyalsa-mixer
1115 */
mixer_ctl_set_array(struct mixer_ctl * ctl,const void * array,size_t count)1116 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
1117 {
1118 struct mixer_ctl_group *grp;
1119 struct snd_ctl_elem_value ev;
1120 size_t size;
1121 void *dest;
1122
1123 if (!ctl || !array || count == 0) {
1124 return -EINVAL;
1125 }
1126
1127 grp = ctl->grp;
1128
1129 if (count > ctl->info.count)
1130 return -EINVAL;
1131
1132 memset(&ev, 0, sizeof(ev));
1133 ev.id.numid = ctl->info.id.numid;
1134
1135 switch (ctl->info.type) {
1136 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1137 case SNDRV_CTL_ELEM_TYPE_INTEGER:
1138 size = sizeof(ev.value.integer.value[0]);
1139 dest = ev.value.integer.value;
1140 break;
1141
1142 case SNDRV_CTL_ELEM_TYPE_BYTES:
1143 /* check if this is new bytes TLV */
1144 if (mixer_ctl_is_access_tlv_rw(ctl)) {
1145 struct snd_ctl_tlv *tlv;
1146 int ret = 0;
1147
1148 if (count > SIZE_MAX - sizeof(*tlv))
1149 return -EINVAL;
1150
1151 tlv = calloc(1, sizeof(*tlv) + count);
1152 if (!tlv)
1153 return -ENOMEM;
1154
1155 tlv->numid = ctl->info.id.numid;
1156 tlv->length = count;
1157 memcpy(tlv->tlv, array, count);
1158
1159 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
1160 free(tlv);
1161
1162 return ret;
1163 } else {
1164 size = sizeof(ev.value.bytes.data[0]);
1165 dest = ev.value.bytes.data;
1166 }
1167 break;
1168
1169 case SNDRV_CTL_ELEM_TYPE_IEC958:
1170 size = sizeof(ev.value.iec958);
1171 dest = &ev.value.iec958;
1172 break;
1173
1174 default:
1175 return -EINVAL;
1176 }
1177
1178 memcpy(dest, array, size * count);
1179
1180 return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
1181 }
1182
1183 /** Gets the minimum value of an control.
1184 * The control must have an integer type.
1185 * The type of the control can be checked with @ref mixer_ctl_get_type.
1186 * @param ctl An initialized control handle.
1187 * @returns On success, the minimum value of the control.
1188 * On failure, -EINVAL.
1189 * @ingroup libtinyalsa-mixer
1190 */
mixer_ctl_get_range_min(const struct mixer_ctl * ctl)1191 int mixer_ctl_get_range_min(const struct mixer_ctl *ctl)
1192 {
1193 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
1194 return -EINVAL;
1195 }
1196
1197 return ctl->info.value.integer.min;
1198 }
1199
1200 /** Gets the maximum value of an control.
1201 * The control must have an integer type.
1202 * The type of the control can be checked with @ref mixer_ctl_get_type.
1203 * @param ctl An initialized control handle.
1204 * @returns On success, the maximum value of the control.
1205 * On failure, -EINVAL.
1206 * @ingroup libtinyalsa-mixer
1207 */
mixer_ctl_get_range_max(const struct mixer_ctl * ctl)1208 int mixer_ctl_get_range_max(const struct mixer_ctl *ctl)
1209 {
1210 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
1211 return -EINVAL;
1212 }
1213
1214 return ctl->info.value.integer.max;
1215 }
1216
1217 /** Get the number of enumerated items in the control.
1218 * @param ctl An initialized control handle.
1219 * @returns The number of enumerated items in the control.
1220 * @ingroup libtinyalsa-mixer
1221 */
mixer_ctl_get_num_enums(const struct mixer_ctl * ctl)1222 unsigned int mixer_ctl_get_num_enums(const struct mixer_ctl *ctl)
1223 {
1224 if (!ctl) {
1225 return 0;
1226 }
1227
1228 return ctl->info.value.enumerated.items;
1229 }
1230
mixer_ctl_fill_enum_string(struct mixer_ctl * ctl)1231 static int mixer_ctl_fill_enum_string(struct mixer_ctl *ctl)
1232 {
1233 struct mixer_ctl_group *grp = ctl->grp;
1234 struct snd_ctl_elem_info tmp;
1235 unsigned int m;
1236 char **enames;
1237
1238 if (ctl->ename) {
1239 return 0;
1240 }
1241
1242 enames = calloc(ctl->info.value.enumerated.items, sizeof(char*));
1243 if (!enames)
1244 goto fail;
1245 for (m = 0; m < ctl->info.value.enumerated.items; m++) {
1246 memset(&tmp, 0, sizeof(tmp));
1247 tmp.id.numid = ctl->info.id.numid;
1248 tmp.value.enumerated.item = m;
1249 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
1250 goto fail;
1251 enames[m] = strdup(tmp.value.enumerated.name);
1252 if (!enames[m])
1253 goto fail;
1254 }
1255 ctl->ename = enames;
1256 return 0;
1257
1258 fail:
1259 if (enames) {
1260 for (m = 0; m < ctl->info.value.enumerated.items; m++) {
1261 if (enames[m]) {
1262 free(enames[m]);
1263 }
1264 }
1265 free(enames);
1266 }
1267 return -1;
1268 }
1269
1270 /** Gets the string representation of an enumerated item.
1271 * @param ctl An initialized control handle.
1272 * @param enum_id The index of the enumerated value.
1273 * @returns A string representation of the enumerated item.
1274 * @ingroup libtinyalsa-mixer
1275 */
mixer_ctl_get_enum_string(struct mixer_ctl * ctl,unsigned int enum_id)1276 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
1277 unsigned int enum_id)
1278 {
1279 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED ||
1280 enum_id >= ctl->info.value.enumerated.items) {
1281 return NULL;
1282 }
1283
1284 if (mixer_ctl_fill_enum_string(ctl) < 0) {
1285 return NULL;
1286 }
1287
1288 return (const char *)ctl->ename[enum_id];
1289 }
1290
1291 /** Set an enumeration value by string value.
1292 * @param ctl An enumerated mixer control.
1293 * @param string The string representation of an enumeration.
1294 * @returns On success, zero.
1295 * On failure, zero.
1296 * @ingroup libtinyalsa-mixer
1297 */
mixer_ctl_set_enum_by_string(struct mixer_ctl * ctl,const char * string)1298 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
1299 {
1300 struct mixer_ctl_group *grp;
1301 unsigned int i, num_enums;
1302 struct snd_ctl_elem_value ev;
1303 int ret;
1304
1305 if (!ctl || !string || ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
1306 return -EINVAL;
1307 }
1308
1309 if (mixer_ctl_fill_enum_string(ctl) < 0) {
1310 return -EINVAL;
1311 }
1312
1313 grp = ctl->grp;
1314 num_enums = ctl->info.value.enumerated.items;
1315 for (i = 0; i < num_enums; i++) {
1316 if (!strcmp(string, ctl->ename[i])) {
1317 memset(&ev, 0, sizeof(ev));
1318 ev.value.enumerated.item[0] = i;
1319 ev.id.numid = ctl->info.id.numid;
1320 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
1321 if (ret < 0)
1322 return ret;
1323 return 0;
1324 }
1325 }
1326
1327 return -EINVAL;
1328 }
1329