audio_route.c revision cef935958069ffba745cd091e6e1687095ea6785
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 * Inspired by TinyHW, written by Mark Brown at Wolfson Micro
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#define LOG_TAG "audio_route"
19/*#define LOG_NDEBUG 0*/
20
21#include <errno.h>
22#include <expat.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <string.h>
26
27#include <cutils/log.h>
28
29#include <tinyalsa/asoundlib.h>
30
31#define BUF_SIZE 1024
32#define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
33#define INITIAL_MIXER_PATH_SIZE 8
34
35struct mixer_state {
36    struct mixer_ctl *ctl;
37    unsigned int num_values;
38    int *old_value;
39    int *new_value;
40    int *reset_value;
41    /* If linked is true, only the first element in each array is valid */
42    bool old_linked;
43    bool new_linked;
44    bool reset_linked;
45};
46
47struct mixer_setting {
48    struct mixer_ctl *ctl;
49    unsigned int num_values;
50    int *value;
51    /* If linked is true, only the first element in each array is valid */
52    bool linked;
53};
54
55struct mixer_value {
56    struct mixer_ctl *ctl;
57    int index;
58    int value;
59};
60
61struct mixer_path {
62    char *name;
63    unsigned int size;
64    unsigned int length;
65    struct mixer_setting *setting;
66};
67
68struct audio_route {
69    struct mixer *mixer;
70    unsigned int num_mixer_ctls;
71    struct mixer_state *mixer_state;
72
73    unsigned int mixer_path_size;
74    unsigned int num_mixer_paths;
75    struct mixer_path *mixer_path;
76};
77
78struct config_parse_state {
79    struct audio_route *ar;
80    struct mixer_path *path;
81    int level;
82};
83
84/* path functions */
85
86static void path_print(struct mixer_path *path)
87{
88    unsigned int i;
89    unsigned int j;
90
91    ALOGE("Path: %s, length: %d", path->name, path->length);
92    for (i = 0; i < path->length; i++) {
93        ALOGE("  id=%d: ctl=%s linked=%c", i,
94              mixer_ctl_get_name(path->setting[i].ctl),
95              path->setting[i].linked ? 'y' : 'n');
96        for (j = 0; j < path->setting[i].num_values; j++)
97            ALOGE("    id=%d value=%d", j, path->setting[i].value[j]);
98    }
99}
100
101static void path_free(struct audio_route *ar)
102{
103    unsigned int i;
104
105    for (i = 0; i < ar->num_mixer_paths; i++) {
106        if (ar->mixer_path[i].name)
107            free(ar->mixer_path[i].name);
108        if (ar->mixer_path[i].setting) {
109            if (ar->mixer_path[i].setting->value)
110                free(ar->mixer_path[i].setting->value);
111            free(ar->mixer_path[i].setting);
112        }
113    }
114    free(ar->mixer_path);
115}
116
117static struct mixer_path *path_get_by_name(struct audio_route *ar,
118                                           const char *name)
119{
120    unsigned int i;
121
122    for (i = 0; i < ar->num_mixer_paths; i++)
123        if (strcmp(ar->mixer_path[i].name, name) == 0)
124            return &ar->mixer_path[i];
125
126    return NULL;
127}
128
129static struct mixer_path *path_create(struct audio_route *ar, const char *name)
130{
131    struct mixer_path *new_mixer_path = NULL;
132
133    if (path_get_by_name(ar, name)) {
134        ALOGE("Path name '%s' already exists", name);
135        return NULL;
136    }
137
138    /* check if we need to allocate more space for mixer paths */
139    if (ar->mixer_path_size <= ar->num_mixer_paths) {
140        if (ar->mixer_path_size == 0)
141            ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
142        else
143            ar->mixer_path_size *= 2;
144
145        new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
146                                 sizeof(struct mixer_path));
147        if (new_mixer_path == NULL) {
148            ALOGE("Unable to allocate more paths");
149            return NULL;
150        } else {
151            ar->mixer_path = new_mixer_path;
152        }
153    }
154
155    /* initialise the new mixer path */
156    ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
157    ar->mixer_path[ar->num_mixer_paths].size = 0;
158    ar->mixer_path[ar->num_mixer_paths].length = 0;
159    ar->mixer_path[ar->num_mixer_paths].setting = NULL;
160
161    /* return the mixer path just added, then increment number of them */
162    return &ar->mixer_path[ar->num_mixer_paths++];
163}
164
165static int find_ctl_in_path(struct mixer_path *path, struct mixer_ctl *ctl)
166{
167    unsigned int i;
168
169    for (i = 0; i < path->length; i++)
170        if (path->setting[i].ctl == ctl)
171            return i;
172
173    return -1;
174}
175
176static int alloc_path_setting(struct mixer_path *path)
177{
178    struct mixer_setting *new_path_setting;
179    int path_index;
180
181    /* check if we need to allocate more space for path settings */
182    if (path->size <= path->length) {
183        if (path->size == 0)
184            path->size = INITIAL_MIXER_PATH_SIZE;
185        else
186            path->size *= 2;
187
188        new_path_setting = realloc(path->setting,
189                                   path->size * sizeof(struct mixer_setting));
190        if (new_path_setting == NULL) {
191            ALOGE("Unable to allocate more path settings");
192            return -1;
193        } else {
194            path->setting = new_path_setting;
195        }
196    }
197
198    path_index = path->length;
199    path->length++;
200
201    return path_index;
202}
203
204static int path_add_setting(struct mixer_path *path,
205                            struct mixer_setting *setting)
206{
207    unsigned int i;
208    int path_index;
209
210    if (find_ctl_in_path(path, setting->ctl) != -1) {
211        ALOGE("Control '%s' already exists in path '%s'",
212              mixer_ctl_get_name(setting->ctl), path->name);
213        return -1;
214    }
215
216    path_index = alloc_path_setting(path);
217    if (path_index < 0)
218        return -1;
219
220    path->setting[path_index].ctl = setting->ctl;
221    path->setting[path_index].num_values = setting->num_values;
222    path->setting[path_index].value = malloc(setting->num_values * sizeof(int));
223    path->setting[path_index].linked = setting->linked;
224    if (setting->linked) {
225        path->setting[path_index].value[0] = setting->value[0];
226    } else {
227        for (i = 0; i < setting->num_values; i++)
228            path->setting[path_index].value[i] = setting->value[i];
229    }
230
231    return 0;
232}
233
234static int path_add_value(struct mixer_path *path,
235                          struct mixer_value *mixer_value)
236{
237    unsigned int i;
238    int path_index;
239    unsigned int num_values;
240
241    /* Check that mixer value index is within range */
242    num_values = mixer_ctl_get_num_values(mixer_value->ctl);
243    if (mixer_value->index >= (int)num_values) {
244        ALOGE("mixer index %d is out of range for '%s'", mixer_value->index,
245              mixer_ctl_get_name(mixer_value->ctl));
246        return -1;
247    }
248
249    path_index = find_ctl_in_path(path, mixer_value->ctl);
250    if (path_index < 0) {
251        /* New path */
252
253        path_index = alloc_path_setting(path);
254        if (path_index < 0)
255            return -1;
256
257        /* initialise the new path setting */
258        path->setting[path_index].ctl = mixer_value->ctl;
259        path->setting[path_index].num_values = num_values;
260        path->setting[path_index].value = malloc(num_values * sizeof(int));
261        path->setting[path_index].linked = true;
262        path->setting[path_index].value[0] = mixer_value->value;
263    }
264
265    if (mixer_value->index == -1) {
266        /* Linked, so only set the first value */
267        path->setting[path_index].linked = true;
268        path->setting[path_index].value[0] = mixer_value->value;
269    } else {
270        if (path->setting[path_index].linked && (num_values > 1)) {
271            /* Unlinking the values, so duplicate them across */
272            for (i = 1; i < num_values; i++) {
273                path->setting[path_index].value[i] =
274                        path->setting[path_index].value[0];
275            }
276            path->setting[path_index].linked = false;
277        }
278        path->setting[path_index].value[mixer_value->index] = mixer_value->value;
279    }
280
281    return 0;
282}
283
284static int path_add_path(struct mixer_path *path, struct mixer_path *sub_path)
285{
286    unsigned int i;
287
288    for (i = 0; i < sub_path->length; i++)
289        if (path_add_setting(path, &sub_path->setting[i]) < 0)
290            return -1;
291
292    return 0;
293}
294
295static int path_apply(struct audio_route *ar, struct mixer_path *path)
296{
297    unsigned int i;
298    unsigned int j;
299    unsigned int ctl_index;
300
301    for (i = 0; i < path->length; i++) {
302        struct mixer_ctl *ctl = path->setting[i].ctl;
303
304        /* locate the mixer ctl in the list */
305        for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++)
306            if (ar->mixer_state[ctl_index].ctl == ctl)
307                break;
308
309        /* apply the new value(s) */
310        for (j = 0; j < ar->mixer_state[ctl_index].num_values; j++) {
311            ar->mixer_state[ctl_index].new_value[j] = path->setting[i].value[j];
312            if (path->setting[i].linked)
313                break;
314        }
315        ar->mixer_state[ctl_index].new_linked = path->setting[i].linked;
316    }
317
318    return 0;
319}
320
321/* mixer helper function */
322static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
323{
324    unsigned int i;
325
326    /* Search the enum strings for a particular one */
327    for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) {
328        if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
329            break;
330    }
331
332    return i;
333}
334
335static void start_tag(void *data, const XML_Char *tag_name,
336                      const XML_Char **attr)
337{
338    const XML_Char *attr_name = NULL;
339    const XML_Char *attr_id = NULL;
340    const XML_Char *attr_value = NULL;
341    struct config_parse_state *state = data;
342    struct audio_route *ar = state->ar;
343    unsigned int i;
344    unsigned int ctl_index;
345    struct mixer_ctl *ctl;
346    int value;
347    unsigned int id;
348    struct mixer_value mixer_value;
349
350    /* Get name, id and value attributes (these may be empty) */
351    for (i = 0; attr[i]; i += 2) {
352        if (strcmp(attr[i], "name") == 0)
353            attr_name = attr[i + 1];
354        if (strcmp(attr[i], "id") == 0)
355            attr_id = attr[i + 1];
356        else if (strcmp(attr[i], "value") == 0)
357            attr_value = attr[i + 1];
358    }
359
360    /* Look at tags */
361    if (strcmp(tag_name, "path") == 0) {
362        if (attr_name == NULL) {
363            ALOGE("Unnamed path!");
364        } else {
365            if (state->level == 1) {
366                /* top level path: create and stash the path */
367                state->path = path_create(ar, (char *)attr_name);
368            } else {
369                /* nested path */
370                struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
371                path_add_path(state->path, sub_path);
372            }
373        }
374    }
375
376    else if (strcmp(tag_name, "ctl") == 0) {
377        /* Obtain the mixer ctl and value */
378        ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
379        switch (mixer_ctl_get_type(ctl)) {
380        case MIXER_CTL_TYPE_BOOL:
381        case MIXER_CTL_TYPE_INT:
382            value = atoi((char *)attr_value);
383            break;
384        case MIXER_CTL_TYPE_ENUM:
385            value = mixer_enum_string_to_value(ctl, (char *)attr_value);
386            break;
387        default:
388            value = 0;
389            break;
390        }
391
392        if (state->level == 1) {
393            /* top level ctl (initial setting) */
394
395            /* locate the mixer ctl in the list */
396            for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
397                if (ar->mixer_state[ctl_index].ctl == ctl)
398                    break;
399            }
400
401            /* apply the new value */
402            if (attr_id) {
403                /* set only one value */
404                id = atoi((char *)attr_id);
405                if (id < ar->mixer_state[ctl_index].num_values) {
406                    if (ar->mixer_state[ctl_index].new_linked) {
407                        /*
408                         * We're unlinking the values, so copy old_value[0] into
409                         * all the new_value elements.
410                         */
411                        for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++) {
412                            ar->mixer_state[ctl_index].new_value[i] =
413                                    ar->mixer_state[ctl_index].old_value[0];
414                        }
415                        ar->mixer_state[ctl_index].new_linked = false;
416                    }
417                    ar->mixer_state[ctl_index].new_value[id] = value;
418                } else {
419                    ALOGE("value id out of range for mixer ctl '%s'",
420                          mixer_ctl_get_name(ctl));
421                }
422            } else {
423                ar->mixer_state[ctl_index].new_value[0] = value;
424                ar->mixer_state[ctl_index].new_linked = true;
425            }
426        } else {
427            /* nested ctl (within a path) */
428            mixer_value.ctl = ctl;
429            mixer_value.value = value;
430            if (attr_id)
431                mixer_value.index = atoi((char *)attr_id);
432            else
433                mixer_value.index = -1;
434            path_add_value(state->path, &mixer_value);
435        }
436    }
437
438    state->level++;
439}
440
441static void end_tag(void *data, const XML_Char *tag_name)
442{
443    struct config_parse_state *state = data;
444
445    state->level--;
446}
447
448static int alloc_mixer_state(struct audio_route *ar)
449{
450    unsigned int i;
451    unsigned int j;
452    unsigned int num_values;
453    struct mixer_ctl *ctl;
454    bool linked;
455
456    ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
457    ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state));
458    if (!ar->mixer_state)
459        return -1;
460
461    for (i = 0; i < ar->num_mixer_ctls; i++) {
462        ctl = mixer_get_ctl(ar->mixer, i);
463        num_values = mixer_ctl_get_num_values(ctl);
464
465        ar->mixer_state[i].old_value = malloc(num_values * sizeof(int));
466        ar->mixer_state[i].new_value = malloc(num_values * sizeof(int));
467        ar->mixer_state[i].reset_value = malloc(num_values * sizeof(int));
468
469        /*
470         * Get all mixer values for controls with multiple values. If all
471         * values are the same, set the linked flag.
472         */
473        linked = true;
474        for (j = 0; j < num_values; j++) {
475            ar->mixer_state[i].old_value[j] = mixer_ctl_get_value(ctl, j);
476            ar->mixer_state[i].new_value[j] = ar->mixer_state[i].old_value[j];
477
478            /*
479             * If the next value is different from the last, set linked to
480             * false.
481             */
482            if ((j > 0) && (ar->mixer_state[i].old_value[j - 1] !=
483                            ar->mixer_state[i].old_value[j])) {
484                linked = false;
485            }
486        }
487        ar->mixer_state[i].ctl = ctl;
488        ar->mixer_state[i].old_linked = linked;
489        ar->mixer_state[i].new_linked = linked;
490        ar->mixer_state[i].num_values = num_values;
491    }
492
493    return 0;
494}
495
496static void free_mixer_state(struct audio_route *ar)
497{
498    unsigned int i;
499
500    for (i = 0; i < ar->num_mixer_ctls; i++) {
501        free(ar->mixer_state[i].old_value);
502        free(ar->mixer_state[i].new_value);
503        free(ar->mixer_state[i].reset_value);
504    }
505
506    free(ar->mixer_state);
507    ar->mixer_state = NULL;
508}
509
510/* Update the mixer with any changed values */
511int audio_route_update_mixer(struct audio_route *ar)
512{
513    unsigned int i;
514    unsigned int j;
515
516    for (i = 0; i < ar->num_mixer_ctls; i++) {
517        unsigned int num_values = ar->mixer_state[i].num_values;
518
519        /* if the value has changed, update the mixer */
520        if (ar->mixer_state[i].new_linked) {
521            if (ar->mixer_state[i].old_value[0] != ar->mixer_state[i].new_value[0]) {
522                /* linked ctl, so set all ctl values the same */
523                for (j = 0; j < num_values; j++)
524                    mixer_ctl_set_value(ar->mixer_state[i].ctl, j,
525                                        ar->mixer_state[i].new_value[0]);
526                ar->mixer_state[i].old_value[0] = ar->mixer_state[i].new_value[0];
527            }
528        } else {
529            for (j = 0; j < num_values; j++) {
530                /*
531                 * unlinked ctl, so set each value if necessary.
532                 * Note that if the new value is unlinked but the old is
533                 * linked, only value 0 is valid, so we always have to
534                 * update the mixer for the other values.
535                 */
536                if (ar->mixer_state[i].old_linked ||
537                    (ar->mixer_state[i].old_value[j] !=
538                            ar->mixer_state[i].new_value[j])) {
539                    mixer_ctl_set_value(ar->mixer_state[i].ctl, j,
540                                        ar->mixer_state[i].new_value[j]);
541                    ar->mixer_state[i].old_value[j] = ar->mixer_state[i].new_value[j];
542                }
543            }
544        }
545        ar->mixer_state[i].old_linked = ar->mixer_state[i].new_linked;
546    }
547
548    return 0;
549}
550
551/* saves the current state of the mixer, for resetting all controls */
552static void save_mixer_state(struct audio_route *ar)
553{
554    unsigned int i;
555    unsigned int j;
556
557    for (i = 0; i < ar->num_mixer_ctls; i++) {
558        for (j = 0; j < ar->mixer_state[i].num_values; j++) {
559            ar->mixer_state[i].reset_value[j] = ar->mixer_state[i].new_value[j];
560
561            /* if the values are linked, only need to save value 0 */
562            if (ar->mixer_state[i].new_linked)
563                break;
564        }
565        ar->mixer_state[i].reset_linked = ar->mixer_state[i].new_linked;
566    }
567}
568
569/* Reset the audio routes back to the initial state */
570void audio_route_reset(struct audio_route *ar)
571{
572    unsigned int i;
573    unsigned int j;
574
575    /* load all of the saved values */
576    for (i = 0; i < ar->num_mixer_ctls; i++) {
577        for (j = 0; j < ar->mixer_state[i].num_values; j++) {
578            ar->mixer_state[i].new_value[j] = ar->mixer_state[i].reset_value[j];
579
580            /* if the values are linked, only need to save value 0 */
581            if (ar->mixer_state[i].reset_linked)
582                break;
583        }
584        ar->mixer_state[i].new_linked = ar->mixer_state[i].reset_linked;
585    }
586}
587
588/* Apply an audio route path by name */
589int audio_route_apply_path(struct audio_route *ar, const char *name)
590{
591    struct mixer_path *path;
592
593    if (!ar) {
594        ALOGE("invalid audio_route");
595        return -1;
596    }
597
598    path = path_get_by_name(ar, name);
599    if (!path) {
600        ALOGE("unable to find path '%s'", name);
601        return -1;
602    }
603
604    path_apply(ar, path);
605
606    return 0;
607}
608
609struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
610{
611    struct config_parse_state state;
612    XML_Parser parser;
613    FILE *file;
614    int bytes_read;
615    void *buf;
616    int i;
617    struct audio_route *ar;
618
619    ar = calloc(1, sizeof(struct audio_route));
620    if (!ar)
621        goto err_calloc;
622
623    ar->mixer = mixer_open(card);
624    if (!ar->mixer) {
625        ALOGE("Unable to open the mixer, aborting.");
626        goto err_mixer_open;
627    }
628
629    ar->mixer_path = NULL;
630    ar->mixer_path_size = 0;
631    ar->num_mixer_paths = 0;
632
633    /* allocate space for and read current mixer settings */
634    if (alloc_mixer_state(ar) < 0)
635        goto err_mixer_state;
636
637    /* use the default XML path if none is provided */
638    if (xml_path == NULL)
639        xml_path = MIXER_XML_PATH;
640
641    file = fopen(xml_path, "r");
642
643    if (!file) {
644        ALOGE("Failed to open %s", xml_path);
645        goto err_fopen;
646    }
647
648    parser = XML_ParserCreate(NULL);
649    if (!parser) {
650        ALOGE("Failed to create XML parser");
651        goto err_parser_create;
652    }
653
654    memset(&state, 0, sizeof(state));
655    state.ar = ar;
656    XML_SetUserData(parser, &state);
657    XML_SetElementHandler(parser, start_tag, end_tag);
658
659    for (;;) {
660        buf = XML_GetBuffer(parser, BUF_SIZE);
661        if (buf == NULL)
662            goto err_parse;
663
664        bytes_read = fread(buf, 1, BUF_SIZE, file);
665        if (bytes_read < 0)
666            goto err_parse;
667
668        if (XML_ParseBuffer(parser, bytes_read,
669                            bytes_read == 0) == XML_STATUS_ERROR) {
670            ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
671            goto err_parse;
672        }
673
674        if (bytes_read == 0)
675            break;
676    }
677
678    /* apply the initial mixer values, and save them so we can reset the
679       mixer to the original values */
680    audio_route_update_mixer(ar);
681    save_mixer_state(ar);
682
683    XML_ParserFree(parser);
684    fclose(file);
685    return ar;
686
687err_parse:
688    XML_ParserFree(parser);
689err_parser_create:
690    fclose(file);
691err_fopen:
692    free_mixer_state(ar);
693err_mixer_state:
694    mixer_close(ar->mixer);
695err_mixer_open:
696    free(ar);
697    ar = NULL;
698err_calloc:
699    return NULL;
700}
701
702void audio_route_free(struct audio_route *ar)
703{
704    free_mixer_state(ar);
705    mixer_close(ar->mixer);
706    free(ar);
707}
708