• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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