• 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 <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <ctype.h>
38 #include <poll.h>
39 
40 #include <sys/ioctl.h>
41 
42 #include <linux/ioctl.h>
43 
44 #define __force
45 #define __bitwise
46 #define __user
47 #include <sound/asound.h>
48 
49 #ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN
50 #define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44
51 #endif
52 
53 #include <tinyalsa/asoundlib.h>
54 #include "mixer_io.h"
55 
56 struct mixer_ctl {
57     struct mixer_ctl_group *grp;
58     struct snd_ctl_elem_info *info;
59     char **ename;
60     bool info_retrieved;
61 };
62 
63 struct mixer_ctl_group {
64     struct snd_ctl_elem_info *elem_info;
65     struct mixer_ctl *ctl;
66     unsigned int count;
67     int event_cnt;
68 
69     struct mixer_ops *ops;
70     void *data;
71 };
72 
73 struct mixer {
74     int fd;
75     struct snd_ctl_card_info card_info;
76 
77     /* hardware/physical mixer control group */
78     struct mixer_ctl_group *hw_grp;
79 
80     /*
81      * Virutal mixer control group.
82      * Currently supports one virtual mixer (.so)
83      * per card. Could be extended to multiple
84      */
85     struct mixer_ctl_group *virt_grp;
86 
87     unsigned int total_ctl_count;
88 };
89 
mixer_grp_close(struct mixer_ctl_group * grp)90 static void mixer_grp_close(struct mixer_ctl_group *grp)
91 {
92     unsigned int n, m;
93 
94     if (!grp)
95         return;
96 
97     if (grp->ctl) {
98         for (n = 0; n < grp->count; n++) {
99             if (grp->ctl[n].ename) {
100                 unsigned int max = grp->ctl[n].info->value.enumerated.items;
101                 for (m = 0; m < max; m++)
102                     free(grp->ctl[n].ename[m]);
103                 free(grp->ctl[n].ename);
104             }
105         }
106         free(grp->ctl);
107     }
108 
109     if (grp->elem_info)
110         free(grp->elem_info);
111 
112     free(grp);
113 }
114 
mixer_close(struct mixer * mixer)115 void mixer_close(struct mixer *mixer)
116 {
117     if (!mixer)
118         return;
119 
120     if (mixer->fd >= 0 && mixer->hw_grp)
121         mixer->hw_grp->ops->close(mixer->hw_grp->data);
122     mixer_grp_close(mixer->hw_grp);
123 
124     if (mixer->virt_grp)
125         mixer->virt_grp->ops->close(mixer->virt_grp->data);
126     mixer_grp_close(mixer->virt_grp);
127 
128     free(mixer);
129 
130     /* TODO: verify frees */
131 }
132 
mixer_grp_open(struct mixer_ctl_group ** ctl_grp,struct mixer_ops * ops,void * data,int * num_ctls_in_grp)133 static int mixer_grp_open(struct mixer_ctl_group **ctl_grp,
134                           struct mixer_ops *ops,
135                           void *data, int *num_ctls_in_grp)
136 {
137     struct mixer_ctl_group *grp;
138     struct snd_ctl_elem_list elist;
139     struct snd_ctl_elem_id *eid = NULL;
140     unsigned int n;
141     int ret;
142 
143     grp = calloc(1, sizeof(*grp));
144     if (!grp)
145         return -ENOMEM;
146 
147     memset(&elist, 0, sizeof(elist));
148     ret = ops->ioctl(data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist);
149     if (ret < 0)
150         goto err_get_elem_list;
151 
152     grp->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
153     grp->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
154     if (!grp->ctl || !grp->elem_info) {
155         ret = -ENOMEM;
156         goto err_ctl_alloc;
157     }
158 
159     eid = calloc(elist.count, sizeof(*eid));
160     if (!eid) {
161         ret = -ENOMEM;
162         goto err_ctl_alloc;
163     }
164 
165     grp->count = elist.count;
166     elist.space = grp->count;
167     elist.pids = eid;
168     ret = ops->ioctl(data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist);
169     if (ret < 0)
170         goto err_ctl_alloc;
171 
172     for (n = 0; n < grp->count; n++) {
173         struct mixer_ctl *ctl = grp->ctl + n;
174 
175         ctl->grp = grp;
176         ctl->info = grp->elem_info + n;
177         ctl->info->id.numid = eid[n].numid;
178         strncpy((char *)ctl->info->id.name, (char *)eid[n].name,
179                 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
180         ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
181     }
182 
183     grp->data = data;
184     grp->ops = ops;
185     *ctl_grp = grp;
186     *num_ctls_in_grp = grp->count;
187 
188     free(eid);
189     return 0;
190 
191 err_ctl_alloc:
192 
193     free(eid);
194     free(grp->elem_info);
195     free(grp->ctl);
196 
197 err_get_elem_list:
198 
199     free(grp);
200     return ret;
201 
202 }
203 
mixer_do_hw_open(struct mixer * mixer,unsigned int card)204 static int mixer_do_hw_open(struct mixer *mixer, unsigned int card)
205 {
206     struct mixer_ops *ops;
207     void *data;
208     int fd, ret, num_grp_ctls = 0;
209 
210     mixer->fd = -1;
211     fd = mixer_hw_open(card, &data, &ops);
212     if (fd < 0)
213         return fd;
214 
215     ret = ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info);
216     if (ret < 0)
217         goto err_card_info;
218 
219     ret = mixer_grp_open(&mixer->hw_grp, ops, data, &num_grp_ctls);
220     if (ret < 0)
221         goto err_card_info;
222 
223     mixer->total_ctl_count += num_grp_ctls;
224 
225     mixer->fd = fd;
226     return 0;
227 
228 err_card_info:
229     ops->close(data);
230     return ret;
231 
232 }
233 
mixer_do_plugin_open(struct mixer * mixer,unsigned int card,int is_hw_open_failed)234 static int mixer_do_plugin_open(struct mixer *mixer, unsigned int card,
235                 int is_hw_open_failed)
236 {
237     struct mixer_ops *ops;
238     void *data;
239     int ret, num_grp_ctls = 0;
240 
241     ret = mixer_plugin_open(card, &data, &ops);
242     if (ret < 0)
243         return ret;
244 
245     /* Get card_info if hw_open failed */
246     if (is_hw_open_failed) {
247         ret = ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info);
248         if (ret < 0)
249             goto err_card_info;
250     }
251 
252     ret = mixer_grp_open(&mixer->virt_grp, ops, data, &num_grp_ctls);
253     if (ret < 0)
254         goto err_card_info;
255 
256     mixer->total_ctl_count += num_grp_ctls;
257     return 0;
258 
259 err_card_info:
260     ops->close(data);
261     return ret;
262 
263 }
264 
mixer_open(unsigned int card)265 struct mixer *mixer_open(unsigned int card)
266 {
267     struct mixer *mixer = NULL;
268     int h_status, v_status;
269 
270     mixer = calloc(1, sizeof(*mixer));
271     if (!mixer)
272         goto fail;
273 
274     /* open hardware node first */
275     h_status = mixer_do_hw_open(mixer, card);
276 
277     /*
278      * open the virtual node even if hw_open fails
279      * since hw_open is expected to fail for virtual cards
280      * for which kernel does not register mixer node
281      */
282     //TODO: open virtual node only if mixer is defined under snd-card-def
283     v_status = mixer_do_plugin_open(mixer, card, h_status);
284 
285     /* Fail mixer_open if both hw and plugin nodes cannot be opened */
286     if (h_status < 0 && v_status < 0)
287         goto fail;
288 
289     return mixer;
290 
291 fail:
292     if (mixer)
293         mixer_close(mixer);
294 
295     return 0;
296 }
297 
mixer_ctl_get_elem_info(struct mixer_ctl * ctl)298 static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
299 {
300     struct mixer_ctl_group *grp;
301     unsigned int i;
302 
303     if (!ctl || !ctl->grp)
304         return false;
305 
306     grp = ctl->grp;
307     if (!ctl->info_retrieved) {
308         if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO,
309                                                   ctl->info) < 0)
310             return false;
311         ctl->info_retrieved = true;
312     }
313 
314     if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED || ctl->ename)
315         return true;
316 
317     struct snd_ctl_elem_info tmp;
318     char** enames = calloc(ctl->info->value.enumerated.items, sizeof(char*));
319     if (!enames)
320         return false;
321 
322     for (i = 0; i < ctl->info->value.enumerated.items; i++) {
323         memset(&tmp, 0, sizeof(tmp));
324         tmp.id.numid = ctl->info->id.numid;
325         tmp.value.enumerated.item = i;
326         if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
327             goto fail;
328         enames[i] = strdup(tmp.value.enumerated.name);
329         if (!enames[i])
330             goto fail;
331     }
332     ctl->ename = enames;
333     return true;
334 
335 fail:
336     free(enames);
337     return false;
338 }
339 
mixer_get_name(struct mixer * mixer)340 const char *mixer_get_name(struct mixer *mixer)
341 {
342     return (const char *)mixer->card_info.name;
343 }
344 
mixer_get_num_ctls(struct mixer * mixer)345 unsigned int mixer_get_num_ctls(struct mixer *mixer)
346 {
347     if (!mixer)
348         return 0;
349 
350     return mixer->total_ctl_count;
351 }
352 
mixer_grp_get_count(struct mixer_ctl_group * grp)353 static int mixer_grp_get_count(struct mixer_ctl_group *grp)
354 {
355     if (!grp)
356         return 0;
357 
358     return grp->count;
359 }
360 
mixer_get_ctl(struct mixer * mixer,unsigned int id)361 struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
362 {
363     struct mixer_ctl *ctl;
364     unsigned int hw_ctl_count, virt_ctl_count;
365 
366     if (!mixer || (id >= mixer->total_ctl_count))
367         return NULL;
368 
369     hw_ctl_count = mixer_grp_get_count(mixer->hw_grp);
370     virt_ctl_count = mixer_grp_get_count(mixer->virt_grp);
371 
372     if (id < hw_ctl_count)
373         ctl = mixer->hw_grp->ctl + id;
374     else if ((id - hw_ctl_count) < virt_ctl_count)
375         ctl = mixer->virt_grp->ctl + (id - hw_ctl_count);
376     else
377         return NULL;
378 
379     if (!mixer_ctl_get_elem_info(ctl))
380         return NULL;
381 
382     return ctl;
383 }
384 
mixer_get_ctl_by_name(struct mixer * mixer,const char * name)385 struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
386 {
387     struct mixer_ctl_group *grp;
388     unsigned int n;
389     int hw_ctl_count;
390 
391     if (!mixer || !name)
392         return NULL;
393 
394     hw_ctl_count = mixer_grp_get_count(mixer->hw_grp);
395     if (mixer->hw_grp) {
396         grp = mixer->hw_grp;
397 
398         for (n = 0; n < grp->count; n++)
399             if (!strcmp(name, (char*) grp->elem_info[n].id.name))
400                 return mixer_get_ctl(mixer, n);
401     }
402 
403     if (mixer->virt_grp) {
404         grp = mixer->virt_grp;
405 
406         for (n = 0; n < grp->count; n++)
407             if (!strcmp(name, (char*) grp->elem_info[n].id.name))
408                 return mixer_get_ctl(mixer, n + hw_ctl_count);
409     }
410 
411     return NULL;
412 }
413 
mixer_ctl_update(struct mixer_ctl * ctl)414 void mixer_ctl_update(struct mixer_ctl *ctl)
415 {
416     struct mixer_ctl_group *grp;
417 
418     if (!ctl || !ctl->grp)
419         return;
420 
421     grp = ctl->grp;
422     grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
423 }
424 
mixer_ctl_get_name(struct mixer_ctl * ctl)425 const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
426 {
427     if (!ctl)
428         return NULL;
429 
430     return (const char *)ctl->info->id.name;
431 }
432 
mixer_ctl_get_type(struct mixer_ctl * ctl)433 enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
434 {
435     if (!ctl)
436         return MIXER_CTL_TYPE_UNKNOWN;
437 
438     switch (ctl->info->type) {
439     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return MIXER_CTL_TYPE_BOOL;
440     case SNDRV_CTL_ELEM_TYPE_INTEGER:    return MIXER_CTL_TYPE_INT;
441     case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
442     case SNDRV_CTL_ELEM_TYPE_BYTES:      return MIXER_CTL_TYPE_BYTE;
443     case SNDRV_CTL_ELEM_TYPE_IEC958:     return MIXER_CTL_TYPE_IEC958;
444     case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return MIXER_CTL_TYPE_INT64;
445     default:                             return MIXER_CTL_TYPE_UNKNOWN;
446     };
447 }
448 
mixer_ctl_get_type_string(struct mixer_ctl * ctl)449 const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
450 {
451     if (!ctl)
452         return "";
453 
454     switch (ctl->info->type) {
455     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return "BOOL";
456     case SNDRV_CTL_ELEM_TYPE_INTEGER:    return "INT";
457     case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
458     case SNDRV_CTL_ELEM_TYPE_BYTES:      return "BYTE";
459     case SNDRV_CTL_ELEM_TYPE_IEC958:     return "IEC958";
460     case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return "INT64";
461     default:                             return "Unknown";
462     };
463 }
464 
mixer_ctl_get_num_values(struct mixer_ctl * ctl)465 unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
466 {
467     if (!ctl)
468         return 0;
469 
470     return ctl->info->count;
471 }
472 
percent_to_int(struct snd_ctl_elem_info * ei,int percent)473 static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
474 {
475     int range;
476 
477     if (percent > 100)
478         percent = 100;
479     else if (percent < 0)
480         percent = 0;
481 
482     range = (ei->value.integer.max - ei->value.integer.min);
483 
484     return ei->value.integer.min + (range * percent) / 100;
485 }
486 
int_to_percent(struct snd_ctl_elem_info * ei,int value)487 static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
488 {
489     int range = (ei->value.integer.max - ei->value.integer.min);
490 
491     if (range == 0)
492         return 0;
493 
494     return ((value - ei->value.integer.min) / range) * 100;
495 }
496 
mixer_ctl_get_percent(struct mixer_ctl * ctl,unsigned int id)497 int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
498 {
499     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
500         return -EINVAL;
501 
502     return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
503 }
504 
mixer_ctl_set_percent(struct mixer_ctl * ctl,unsigned int id,int percent)505 int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
506 {
507     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
508         return -EINVAL;
509 
510     return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
511 }
512 
mixer_ctl_get_value(struct mixer_ctl * ctl,unsigned int id)513 int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
514 {
515     struct mixer_ctl_group *grp;
516     struct snd_ctl_elem_value ev;
517     int ret;
518 
519     if (!ctl || (id >= ctl->info->count) || !ctl->grp)
520         return -EINVAL;
521 
522     grp = ctl->grp;
523     memset(&ev, 0, sizeof(ev));
524     ev.id.numid = ctl->info->id.numid;
525     ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
526     if (ret < 0)
527         return ret;
528 
529     switch (ctl->info->type) {
530     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
531         return !!ev.value.integer.value[id];
532 
533     case SNDRV_CTL_ELEM_TYPE_INTEGER:
534         return ev.value.integer.value[id];
535 
536     case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
537         return ev.value.enumerated.item[id];
538 
539     case SNDRV_CTL_ELEM_TYPE_BYTES:
540         return ev.value.bytes.data[id];
541 
542     default:
543         return -EINVAL;
544     }
545 
546     return 0;
547 }
548 
mixer_ctl_is_access_tlv_rw(struct mixer_ctl * ctl)549 int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
550 {
551     return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
552 }
553 
mixer_ctl_get_array(struct mixer_ctl * ctl,void * array,size_t count)554 int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
555 {
556     struct mixer_ctl_group *grp;
557     struct snd_ctl_elem_value ev;
558     int ret = 0;
559     size_t size;
560     void *source;
561     size_t total_count;
562 
563     if ((!ctl) || !count || !array || !ctl->grp)
564         return -EINVAL;
565 
566     grp = ctl->grp;
567     total_count = ctl->info->count;
568 
569     if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
570         mixer_ctl_is_access_tlv_rw(ctl)) {
571             /* Additional two words is for the TLV header */
572             total_count += TLV_HEADER_SIZE;
573     }
574 
575     if (count > total_count)
576         return -EINVAL;
577 
578     memset(&ev, 0, sizeof(ev));
579     ev.id.numid = ctl->info->id.numid;
580 
581     switch (ctl->info->type) {
582     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
583     case SNDRV_CTL_ELEM_TYPE_INTEGER:
584         ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
585         if (ret < 0)
586             return ret;
587         size = sizeof(ev.value.integer.value[0]);
588         source = ev.value.integer.value;
589         break;
590 
591     case SNDRV_CTL_ELEM_TYPE_BYTES:
592         /* check if this is new bytes TLV */
593         if (mixer_ctl_is_access_tlv_rw(ctl)) {
594             struct snd_ctl_tlv *tlv;
595             int ret;
596 
597             if (count > SIZE_MAX - sizeof(*tlv))
598                 return -EINVAL;
599             tlv = calloc(1, sizeof(*tlv) + count);
600             if (!tlv)
601                 return -ENOMEM;
602             tlv->numid = ctl->info->id.numid;
603             tlv->length = count;
604             ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_READ, tlv);
605 
606             source = tlv->tlv;
607             memcpy(array, source, count);
608 
609             free(tlv);
610 
611             return ret;
612         } else {
613             ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
614             if (ret < 0)
615                 return ret;
616             size = sizeof(ev.value.bytes.data[0]);
617             source = ev.value.bytes.data;
618             break;
619         }
620 
621     case SNDRV_CTL_ELEM_TYPE_IEC958:
622         ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
623         if (ret < 0)
624             return ret;
625         size = sizeof(ev.value.iec958);
626         source = &ev.value.iec958;
627         break;
628 
629     default:
630         return -EINVAL;
631     }
632 
633     memcpy(array, source, size * count);
634 
635     return 0;
636 }
637 
mixer_ctl_set_value(struct mixer_ctl * ctl,unsigned int id,int value)638 int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
639 {
640     struct mixer_ctl_group *grp;
641     struct snd_ctl_elem_value ev;
642     int ret;
643 
644     if (!ctl || (id >= ctl->info->count) || !ctl->grp)
645         return -EINVAL;
646 
647     grp = ctl->grp;
648     memset(&ev, 0, sizeof(ev));
649     ev.id.numid = ctl->info->id.numid;
650     ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
651     if (ret < 0)
652         return ret;
653 
654     switch (ctl->info->type) {
655     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
656         ev.value.integer.value[id] = !!value;
657         break;
658 
659     case SNDRV_CTL_ELEM_TYPE_INTEGER:
660         ev.value.integer.value[id] = value;
661         break;
662 
663     case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
664         ev.value.enumerated.item[id] = value;
665         break;
666 
667     case SNDRV_CTL_ELEM_TYPE_BYTES:
668         ev.value.bytes.data[id] = value;
669         break;
670 
671     default:
672         return -EINVAL;
673     }
674 
675     return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
676 }
677 
mixer_ctl_set_array(struct mixer_ctl * ctl,const void * array,size_t count)678 int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
679 {
680     struct mixer_ctl_group *grp;
681     struct snd_ctl_elem_value ev;
682     size_t size;
683     void *dest;
684     size_t total_count;
685 
686     if ((!ctl) || !count || !array || !ctl->grp)
687         return -EINVAL;
688 
689     grp = ctl->grp;
690     total_count = ctl->info->count;
691 
692     if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
693         mixer_ctl_is_access_tlv_rw(ctl)) {
694             /* Additional two words is for the TLV header */
695             total_count += TLV_HEADER_SIZE;
696     }
697 
698     if (count > total_count)
699         return -EINVAL;
700 
701     memset(&ev, 0, sizeof(ev));
702     ev.id.numid = ctl->info->id.numid;
703 
704     switch (ctl->info->type) {
705     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
706     case SNDRV_CTL_ELEM_TYPE_INTEGER:
707         size = sizeof(ev.value.integer.value[0]);
708         dest = ev.value.integer.value;
709         break;
710 
711     case SNDRV_CTL_ELEM_TYPE_BYTES:
712         /* check if this is new bytes TLV */
713         if (mixer_ctl_is_access_tlv_rw(ctl)) {
714             struct snd_ctl_tlv *tlv;
715             int ret = 0;
716             if (count > SIZE_MAX - sizeof(*tlv))
717                 return -EINVAL;
718             tlv = calloc(1, sizeof(*tlv) + count);
719             if (!tlv)
720                 return -ENOMEM;
721             tlv->numid = ctl->info->id.numid;
722             tlv->length = count;
723             memcpy(tlv->tlv, array, count);
724 
725             ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
726             free(tlv);
727 
728             return ret;
729         } else {
730             size = sizeof(ev.value.bytes.data[0]);
731             dest = ev.value.bytes.data;
732         }
733         break;
734 
735     case SNDRV_CTL_ELEM_TYPE_IEC958:
736         size = sizeof(ev.value.iec958);
737         dest = &ev.value.iec958;
738         break;
739 
740     default:
741         return -EINVAL;
742     }
743 
744     memcpy(dest, array, size * count);
745 
746     return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
747 }
748 
mixer_ctl_get_range_min(struct mixer_ctl * ctl)749 int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
750 {
751     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
752         return -EINVAL;
753 
754     return ctl->info->value.integer.min;
755 }
756 
mixer_ctl_get_range_max(struct mixer_ctl * ctl)757 int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
758 {
759     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
760         return -EINVAL;
761 
762     return ctl->info->value.integer.max;
763 }
764 
mixer_ctl_get_num_enums(struct mixer_ctl * ctl)765 unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
766 {
767     if (!ctl)
768         return 0;
769 
770     return ctl->info->value.enumerated.items;
771 }
772 
mixer_ctl_get_enum_string(struct mixer_ctl * ctl,unsigned int enum_id)773 const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
774                                       unsigned int enum_id)
775 {
776     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
777         (enum_id >= ctl->info->value.enumerated.items))
778         return NULL;
779 
780     return (const char *)ctl->ename[enum_id];
781 }
782 
mixer_ctl_set_enum_by_string(struct mixer_ctl * ctl,const char * string)783 int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
784 {
785     struct mixer_ctl_group *grp;
786     unsigned int i, num_enums;
787     struct snd_ctl_elem_value ev;
788     int ret;
789 
790     if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) || !ctl->grp)
791         return -EINVAL;
792 
793     grp = ctl->grp;
794     num_enums = ctl->info->value.enumerated.items;
795     for (i = 0; i < num_enums; i++) {
796         if (!strcmp(string, ctl->ename[i])) {
797             memset(&ev, 0, sizeof(ev));
798             ev.value.enumerated.item[0] = i;
799             ev.id.numid = ctl->info->id.numid;
800             ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
801             if (ret < 0)
802                 return ret;
803             return 0;
804         }
805     }
806 
807     return -EINVAL;
808 }
809 
810 /** Subscribes for the mixer events.
811  * @param mixer A mixer handle.
812  * @param subscribe value indicating subscribe or unsubscribe for events
813  * @returns On success, zero.
814  *  On failure, non-zero.
815  * @ingroup libtinyalsa-mixer
816  */
mixer_subscribe_events(struct mixer * mixer,int subscribe)817 int mixer_subscribe_events(struct mixer *mixer, int subscribe)
818 {
819     struct mixer_ctl_group *grp;
820 
821     if (mixer->hw_grp) {
822         grp = mixer->hw_grp;
823         if (!subscribe)
824             grp->event_cnt = 0;
825         if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
826             return -1;
827     }
828 
829     if (mixer->virt_grp) {
830         grp = mixer->virt_grp;
831         if (!subscribe)
832             grp->event_cnt = 0;
833         if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
834             return -1;
835     }
836     return 0;
837 }
838 
839 /** Wait for mixer events.
840  * @param mixer A mixer handle.
841  * @param timeout timout value
842  * @returns On success, 1.
843  *  On failure, -errno.
844  *  On timeout, 0
845  * @ingroup libtinyalsa-mixer
846  */
mixer_wait_event(struct mixer * mixer,int timeout)847 int mixer_wait_event(struct mixer *mixer, int timeout)
848 {
849     struct pollfd pfd[2];
850     struct mixer_ctl_group *grp;
851     int count = 0, num_fds = 0, i;
852 
853     memset(pfd, 0, sizeof(struct pollfd) * 2);
854 
855     if (mixer->fd >= 0)
856         num_fds++;
857 
858     if (mixer->virt_grp)
859         num_fds++;
860 
861 
862     /* TODO wait events for virt fd */
863     if (mixer->fd >= 0) {
864         pfd[count].fd = mixer->fd;
865         pfd[count].events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
866         count++;
867     }
868 
869     if (mixer->virt_grp) {
870         grp = mixer->virt_grp;
871         if (!grp->ops->get_poll_fd(grp->data, pfd, count)) {
872             pfd[count].events = POLLIN | POLLERR | POLLNVAL;
873             count++;
874         }
875     }
876 
877     if (!count)
878         return 0;
879 
880     for (;;) {
881         int err;
882         err = poll(pfd, count, timeout);
883         if (err < 0)
884             return -errno;
885         if (!err)
886             return 0;
887         for (i = 0; i < count; i++) {
888             if (pfd[i].revents & (POLLERR | POLLNVAL))
889                 return -EIO;
890             if (pfd[i].revents & (POLLIN | POLLOUT)) {
891                 if ((i == 0) && mixer->fd >= 0) {
892                     grp = mixer->hw_grp;
893                     grp->event_cnt++;
894                 } else {
895                     grp = mixer->virt_grp;
896                     grp->event_cnt++;
897                 }
898                 return 1;
899             }
900         }
901     }
902 }
903 
904 /** Consume a mixer event.
905  * If mixer_subscribe_events has been called,
906  * mixer_wait_event will identify when a control value has changed.
907  * This function will clear a single event from the mixer so that
908  * further events can be alerted.
909  *
910  * @param mixer A mixer handle.
911  * @returns 0 on success.  -errno on failure.
912  * @ingroup libtinyalsa-mixer
913  */
mixer_consume_event(struct mixer * mixer)914 int mixer_consume_event(struct mixer *mixer)
915 {
916     struct snd_ctl_event ev;
917     struct mixer_ctl_group *grp;
918     ssize_t count = 0;
919 
920     // Exporting the actual event would require exposing snd_ctl_event
921     // via the header file, and all associated structs.
922     // The events generally tell you exactly which value changed,
923     // but reading values you're interested isn't hard and simplifies
924     // the interface greatly.
925     if (mixer->hw_grp) {
926         grp = mixer->hw_grp;
927         if (grp->event_cnt) {
928             grp->event_cnt--;
929             count = grp->ops->read_event(grp->data, &ev, sizeof(ev));
930             return (count >= 0) ? 0 : -errno;
931         }
932     }
933 
934     if (mixer->virt_grp) {
935         grp = mixer->virt_grp;
936         if (grp->event_cnt) {
937             grp->event_cnt--;
938             count += grp->ops->read_event(grp->data, &ev, sizeof(ev));
939             return (count >= 0) ? 0 : -errno;
940         }
941     }
942     return 0;
943 }
944 
945 /** Read a mixer event.
946  * If mixer_subscribe_events has been called,
947  * mixer_wait_event will identify when a control value has changed.
948  * This function will read and clear a single event from the mixer
949  * so that further events can be alerted.
950  *
951  * @param mixer A mixer handle.
952  * @param ev snd_ctl_event pointer where event needs to be read
953  * @returns 0 on success.  -errno on failure.
954  * @ingroup libtinyalsa-mixer
955  */
mixer_read_event(struct mixer * mixer,struct ctl_event * ev)956 int mixer_read_event(struct mixer *mixer, struct ctl_event *ev)
957 {
958     struct mixer_ctl_group *grp;
959     ssize_t count = 0;
960 
961     if (mixer->hw_grp) {
962         grp = mixer->hw_grp;
963         if (grp->event_cnt) {
964             grp->event_cnt--;
965             count = grp->ops->read_event(grp->data, (struct snd_ctl_event *)ev, sizeof(*ev));
966             return (count >= 0) ? 0 : -errno;
967         }
968     }
969 
970     if (mixer->virt_grp) {
971         grp = mixer->virt_grp;
972         if (grp->event_cnt) {
973             grp->event_cnt--;
974             count = grp->ops->read_event(grp->data, (struct snd_ctl_event *)ev, sizeof(*ev));
975             return (count >= 0) ? 0 : -errno;
976         }
977     }
978     return 0;
979 }
980