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