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#define __force
44#define __bitwise
45#define __user
46#include <sound/asound.h>
47
48#ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN
49#define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44
50#endif
51
52#include <tinyalsa/asoundlib.h>
53
54struct mixer_ctl {
55    struct mixer *mixer;
56    struct snd_ctl_elem_info *info;
57    char **ename;
58    bool info_retrieved;
59};
60
61struct mixer {
62    int fd;
63    struct snd_ctl_card_info card_info;
64    struct snd_ctl_elem_info *elem_info;
65    struct mixer_ctl *ctl;
66    unsigned int count;
67};
68
69void mixer_close(struct mixer *mixer)
70{
71    unsigned int n,m;
72
73    if (!mixer)
74        return;
75
76    if (mixer->fd >= 0)
77        close(mixer->fd);
78
79    if (mixer->ctl) {
80        for (n = 0; n < mixer->count; n++) {
81            if (mixer->ctl[n].ename) {
82                unsigned int max = mixer->ctl[n].info->value.enumerated.items;
83                for (m = 0; m < max; m++)
84                    free(mixer->ctl[n].ename[m]);
85                free(mixer->ctl[n].ename);
86            }
87        }
88        free(mixer->ctl);
89    }
90
91    if (mixer->elem_info)
92        free(mixer->elem_info);
93
94    free(mixer);
95
96    /* TODO: verify frees */
97}
98
99struct mixer *mixer_open(unsigned int card)
100{
101    struct snd_ctl_elem_list elist;
102    struct snd_ctl_elem_id *eid = NULL;
103    struct mixer *mixer = NULL;
104    unsigned int n;
105    int fd;
106    char fn[256];
107
108    snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
109    fd = open(fn, O_RDWR);
110    if (fd < 0)
111        return 0;
112
113    memset(&elist, 0, sizeof(elist));
114    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
115        goto fail;
116
117    mixer = calloc(1, sizeof(*mixer));
118    if (!mixer)
119        goto fail;
120
121    mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
122    mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
123    if (!mixer->ctl || !mixer->elem_info)
124        goto fail;
125
126    if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
127        goto fail;
128
129    eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
130    if (!eid)
131        goto fail;
132
133    mixer->count = elist.count;
134    mixer->fd = fd;
135    elist.space = mixer->count;
136    elist.pids = eid;
137    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
138        goto fail;
139
140    for (n = 0; n < mixer->count; n++) {
141        struct mixer_ctl *ctl = mixer->ctl + n;
142
143        ctl->mixer = mixer;
144        ctl->info = mixer->elem_info + n;
145        ctl->info->id.numid = eid[n].numid;
146        strncpy((char *)ctl->info->id.name, (char *)eid[n].name,
147                SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
148        ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
149    }
150
151    free(eid);
152    return mixer;
153
154fail:
155    /* TODO: verify frees in failure case */
156    if (eid)
157        free(eid);
158    if (mixer)
159        mixer_close(mixer);
160    else if (fd >= 0)
161        close(fd);
162    return 0;
163}
164
165static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
166{
167    if (!ctl->info_retrieved) {
168        if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info) < 0)
169            return false;
170        ctl->info_retrieved = true;
171    }
172
173    if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED || ctl->ename)
174        return true;
175
176    struct snd_ctl_elem_info tmp;
177    char** enames = calloc(ctl->info->value.enumerated.items, sizeof(char*));
178    if (!enames)
179        return false;
180
181    for (unsigned int i = 0; i < ctl->info->value.enumerated.items; i++) {
182        memset(&tmp, 0, sizeof(tmp));
183        tmp.id.numid = ctl->info->id.numid;
184        tmp.value.enumerated.item = i;
185        if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
186            goto fail;
187        enames[i] = strdup(tmp.value.enumerated.name);
188        if (!enames[i])
189            goto fail;
190    }
191    ctl->ename = enames;
192    return true;
193
194fail:
195    free(enames);
196    return false;
197}
198
199const char *mixer_get_name(struct mixer *mixer)
200{
201    return (const char *)mixer->card_info.name;
202}
203
204unsigned int mixer_get_num_ctls(struct mixer *mixer)
205{
206    if (!mixer)
207        return 0;
208
209    return mixer->count;
210}
211
212struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
213{
214    struct mixer_ctl *ctl;
215
216    if (!mixer || (id >= mixer->count))
217        return NULL;
218
219    ctl = mixer->ctl + id;
220    if (!mixer_ctl_get_elem_info(ctl))
221        return NULL;
222
223    return ctl;
224}
225
226struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
227{
228    unsigned int n;
229
230    if (!mixer)
231        return NULL;
232
233    for (n = 0; n < mixer->count; n++)
234        if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
235            return mixer_get_ctl(mixer, n);
236
237    return NULL;
238}
239
240void mixer_ctl_update(struct mixer_ctl *ctl)
241{
242    ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
243}
244
245const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
246{
247    if (!ctl)
248        return NULL;
249
250    return (const char *)ctl->info->id.name;
251}
252
253enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
254{
255    if (!ctl)
256        return MIXER_CTL_TYPE_UNKNOWN;
257
258    switch (ctl->info->type) {
259    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return MIXER_CTL_TYPE_BOOL;
260    case SNDRV_CTL_ELEM_TYPE_INTEGER:    return MIXER_CTL_TYPE_INT;
261    case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
262    case SNDRV_CTL_ELEM_TYPE_BYTES:      return MIXER_CTL_TYPE_BYTE;
263    case SNDRV_CTL_ELEM_TYPE_IEC958:     return MIXER_CTL_TYPE_IEC958;
264    case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return MIXER_CTL_TYPE_INT64;
265    default:                             return MIXER_CTL_TYPE_UNKNOWN;
266    };
267}
268
269const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
270{
271    if (!ctl)
272        return "";
273
274    switch (ctl->info->type) {
275    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:    return "BOOL";
276    case SNDRV_CTL_ELEM_TYPE_INTEGER:    return "INT";
277    case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
278    case SNDRV_CTL_ELEM_TYPE_BYTES:      return "BYTE";
279    case SNDRV_CTL_ELEM_TYPE_IEC958:     return "IEC958";
280    case SNDRV_CTL_ELEM_TYPE_INTEGER64:  return "INT64";
281    default:                             return "Unknown";
282    };
283}
284
285unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
286{
287    if (!ctl)
288        return 0;
289
290    return ctl->info->count;
291}
292
293static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
294{
295    int range;
296
297    if (percent > 100)
298        percent = 100;
299    else if (percent < 0)
300        percent = 0;
301
302    range = (ei->value.integer.max - ei->value.integer.min);
303
304    return ei->value.integer.min + (range * percent) / 100;
305}
306
307static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
308{
309    int range = (ei->value.integer.max - ei->value.integer.min);
310
311    if (range == 0)
312        return 0;
313
314    return ((value - ei->value.integer.min) / range) * 100;
315}
316
317int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
318{
319    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
320        return -EINVAL;
321
322    return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
323}
324
325int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
326{
327    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
328        return -EINVAL;
329
330    return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
331}
332
333int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
334{
335    struct snd_ctl_elem_value ev;
336    int ret;
337
338    if (!ctl || (id >= ctl->info->count))
339        return -EINVAL;
340
341    memset(&ev, 0, sizeof(ev));
342    ev.id.numid = ctl->info->id.numid;
343    ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
344    if (ret < 0)
345        return ret;
346
347    switch (ctl->info->type) {
348    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
349        return !!ev.value.integer.value[id];
350
351    case SNDRV_CTL_ELEM_TYPE_INTEGER:
352        return ev.value.integer.value[id];
353
354    case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
355        return ev.value.enumerated.item[id];
356
357    case SNDRV_CTL_ELEM_TYPE_BYTES:
358        return ev.value.bytes.data[id];
359
360    default:
361        return -EINVAL;
362    }
363
364    return 0;
365}
366
367int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
368{
369    return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
370}
371
372int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
373{
374    struct snd_ctl_elem_value ev;
375    int ret = 0;
376    size_t size;
377    void *source;
378    size_t total_count;
379
380    if ((!ctl) || !count || !array)
381        return -EINVAL;
382
383    total_count = ctl->info->count;
384
385    if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
386        mixer_ctl_is_access_tlv_rw(ctl)) {
387            /* Additional two words is for the TLV header */
388            total_count += TLV_HEADER_SIZE;
389    }
390
391    if (count > total_count)
392        return -EINVAL;
393
394    memset(&ev, 0, sizeof(ev));
395    ev.id.numid = ctl->info->id.numid;
396
397    switch (ctl->info->type) {
398    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
399    case SNDRV_CTL_ELEM_TYPE_INTEGER:
400        ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
401        if (ret < 0)
402            return ret;
403        size = sizeof(ev.value.integer.value[0]);
404        source = ev.value.integer.value;
405        break;
406
407    case SNDRV_CTL_ELEM_TYPE_BYTES:
408        /* check if this is new bytes TLV */
409        if (mixer_ctl_is_access_tlv_rw(ctl)) {
410            struct snd_ctl_tlv *tlv;
411            int ret;
412
413            if (count > SIZE_MAX - sizeof(*tlv))
414                return -EINVAL;
415            tlv = calloc(1, sizeof(*tlv) + count);
416            if (!tlv)
417                return -ENOMEM;
418            tlv->numid = ctl->info->id.numid;
419            tlv->length = count;
420            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
421
422            source = tlv->tlv;
423            memcpy(array, source, count);
424
425            free(tlv);
426
427            return ret;
428        } else {
429            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
430            if (ret < 0)
431                return ret;
432            size = sizeof(ev.value.bytes.data[0]);
433            source = ev.value.bytes.data;
434            break;
435        }
436
437    case SNDRV_CTL_ELEM_TYPE_IEC958:
438        size = sizeof(ev.value.iec958);
439        source = &ev.value.iec958;
440        break;
441
442    default:
443        return -EINVAL;
444    }
445
446    memcpy(array, source, size * count);
447
448    return 0;
449}
450
451int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
452{
453    struct snd_ctl_elem_value ev;
454    int ret;
455
456    if (!ctl || (id >= ctl->info->count))
457        return -EINVAL;
458
459    memset(&ev, 0, sizeof(ev));
460    ev.id.numid = ctl->info->id.numid;
461    ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
462    if (ret < 0)
463        return ret;
464
465    switch (ctl->info->type) {
466    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
467        ev.value.integer.value[id] = !!value;
468        break;
469
470    case SNDRV_CTL_ELEM_TYPE_INTEGER:
471        ev.value.integer.value[id] = value;
472        break;
473
474    case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
475        ev.value.enumerated.item[id] = value;
476        break;
477
478    case SNDRV_CTL_ELEM_TYPE_BYTES:
479        ev.value.bytes.data[id] = value;
480        break;
481
482    default:
483        return -EINVAL;
484    }
485
486    return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
487}
488
489int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
490{
491    struct snd_ctl_elem_value ev;
492    size_t size;
493    void *dest;
494    size_t total_count;
495
496    if ((!ctl) || !count || !array)
497        return -EINVAL;
498
499    total_count = ctl->info->count;
500
501    if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
502        mixer_ctl_is_access_tlv_rw(ctl)) {
503            /* Additional two words is for the TLV header */
504            total_count += TLV_HEADER_SIZE;
505    }
506
507    if (count > total_count)
508        return -EINVAL;
509
510    memset(&ev, 0, sizeof(ev));
511    ev.id.numid = ctl->info->id.numid;
512
513    switch (ctl->info->type) {
514    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
515    case SNDRV_CTL_ELEM_TYPE_INTEGER:
516        size = sizeof(ev.value.integer.value[0]);
517        dest = ev.value.integer.value;
518        break;
519
520    case SNDRV_CTL_ELEM_TYPE_BYTES:
521        /* check if this is new bytes TLV */
522        if (mixer_ctl_is_access_tlv_rw(ctl)) {
523            struct snd_ctl_tlv *tlv;
524            int ret = 0;
525            if (count > SIZE_MAX - sizeof(*tlv))
526                return -EINVAL;
527            tlv = calloc(1, sizeof(*tlv) + count);
528            if (!tlv)
529                return -ENOMEM;
530            tlv->numid = ctl->info->id.numid;
531            tlv->length = count;
532            memcpy(tlv->tlv, array, count);
533
534            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
535            free(tlv);
536
537            return ret;
538        } else {
539            size = sizeof(ev.value.bytes.data[0]);
540            dest = ev.value.bytes.data;
541        }
542        break;
543
544    case SNDRV_CTL_ELEM_TYPE_IEC958:
545        size = sizeof(ev.value.iec958);
546        dest = &ev.value.iec958;
547        break;
548
549    default:
550        return -EINVAL;
551    }
552
553    memcpy(dest, array, size * count);
554
555    return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
556}
557
558int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
559{
560    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
561        return -EINVAL;
562
563    return ctl->info->value.integer.min;
564}
565
566int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
567{
568    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
569        return -EINVAL;
570
571    return ctl->info->value.integer.max;
572}
573
574unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
575{
576    if (!ctl)
577        return 0;
578
579    return ctl->info->value.enumerated.items;
580}
581
582const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
583                                      unsigned int enum_id)
584{
585    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
586        (enum_id >= ctl->info->value.enumerated.items))
587        return NULL;
588
589    return (const char *)ctl->ename[enum_id];
590}
591
592int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
593{
594    unsigned int i, num_enums;
595    struct snd_ctl_elem_value ev;
596    int ret;
597
598    if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
599        return -EINVAL;
600
601    num_enums = ctl->info->value.enumerated.items;
602    for (i = 0; i < num_enums; i++) {
603        if (!strcmp(string, ctl->ename[i])) {
604            memset(&ev, 0, sizeof(ev));
605            ev.value.enumerated.item[0] = i;
606            ev.id.numid = ctl->info->id.numid;
607            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
608            if (ret < 0)
609                return ret;
610            return 0;
611        }
612    }
613
614    return -EINVAL;
615}
616
617/** Subscribes for the mixer events.
618 * @param mixer A mixer handle.
619 * @param subscribe value indicating subscribe or unsubscribe for events
620 * @returns On success, zero.
621 *  On failure, non-zero.
622 * @ingroup libtinyalsa-mixer
623 */
624int mixer_subscribe_events(struct mixer *mixer, int subscribe)
625{
626    if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
627        return -1;
628    }
629    return 0;
630}
631
632/** Wait for mixer events.
633 * @param mixer A mixer handle.
634 * @param timeout timout value
635 * @returns On success, 1.
636 *  On failure, -errno.
637 *  On timeout, 0
638 * @ingroup libtinyalsa-mixer
639 */
640int mixer_wait_event(struct mixer *mixer, int timeout)
641{
642    struct pollfd pfd;
643
644    pfd.fd = mixer->fd;
645    pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
646
647    for (;;) {
648        int err;
649        err = poll(&pfd, 1, timeout);
650        if (err < 0)
651            return -errno;
652        if (!err)
653            return 0;
654        if (pfd.revents & (POLLERR | POLLNVAL))
655            return -EIO;
656        if (pfd.revents & (POLLIN | POLLOUT))
657            return 1;
658    }
659}
660
661/** Consume a mixer event.
662 * If mixer_subscribe_events has been called,
663 * mixer_wait_event will identify when a control value has changed.
664 * This function will clear a single event from the mixer so that
665 * further events can be alerted.
666 *
667 * @param mixer A mixer handle.
668 * @returns 0 on success.  -errno on failure.
669 * @ingroup libtinyalsa-mixer
670 */
671int mixer_consume_event(struct mixer *mixer) {
672    struct snd_ctl_event ev;
673    ssize_t count = read(mixer->fd, &ev, sizeof(ev));
674    // Exporting the actual event would require exposing snd_ctl_event
675    // via the header file, and all associated structs.
676    // The events generally tell you exactly which value changed,
677    // but reading values you're interested isn't hard and simplifies
678    // the interface greatly.
679    return (count >= 0) ? 0 : -errno;
680}
681