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