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