audio_route.c revision 63b31c18b37409e1b024c50df94798aac9d0fa7d
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};
42
43struct mixer_setting {
44    unsigned int ctl_index;
45    unsigned int num_values;
46    int *value;
47};
48
49struct mixer_value {
50    unsigned int ctl_index;
51    int index;
52    int value;
53};
54
55struct mixer_path {
56    char *name;
57    unsigned int size;
58    unsigned int length;
59    struct mixer_setting *setting;
60};
61
62struct audio_route {
63    struct mixer *mixer;
64    unsigned int num_mixer_ctls;
65    struct mixer_state *mixer_state;
66
67    unsigned int mixer_path_size;
68    unsigned int num_mixer_paths;
69    struct mixer_path *mixer_path;
70};
71
72struct config_parse_state {
73    struct audio_route *ar;
74    struct mixer_path *path;
75    int level;
76};
77
78/* path functions */
79
80static inline struct mixer_ctl *index_to_ctl(struct audio_route *ar,
81                                             unsigned int ctl_index)
82{
83    return ar->mixer_state[ctl_index].ctl;
84}
85
86static void path_print(struct audio_route *ar, 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        struct mixer_ctl *ctl = index_to_ctl(ar, path->setting[i].ctl_index);
94
95        ALOGE("  id=%d: ctl=%s", i, mixer_ctl_get_name(ctl));
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_index_in_path(struct mixer_path *path,
166                                  unsigned int ctl_index)
167{
168    unsigned int i;
169
170    for (i = 0; i < path->length; i++)
171        if (path->setting[i].ctl_index == ctl_index)
172            return i;
173
174    return -1;
175}
176
177static int alloc_path_setting(struct mixer_path *path)
178{
179    struct mixer_setting *new_path_setting;
180    int path_index;
181
182    /* check if we need to allocate more space for path settings */
183    if (path->size <= path->length) {
184        if (path->size == 0)
185            path->size = INITIAL_MIXER_PATH_SIZE;
186        else
187            path->size *= 2;
188
189        new_path_setting = realloc(path->setting,
190                                   path->size * sizeof(struct mixer_setting));
191        if (new_path_setting == NULL) {
192            ALOGE("Unable to allocate more path settings");
193            return -1;
194        } else {
195            path->setting = new_path_setting;
196        }
197    }
198
199    path_index = path->length;
200    path->length++;
201
202    return path_index;
203}
204
205static int path_add_setting(struct audio_route *ar, struct mixer_path *path,
206                            struct mixer_setting *setting)
207{
208    int path_index;
209
210    if (find_ctl_index_in_path(path, setting->ctl_index) != -1) {
211        struct mixer_ctl *ctl = index_to_ctl(ar, setting->ctl_index);
212
213        ALOGE("Control '%s' already exists in path '%s'",
214              mixer_ctl_get_name(ctl), path->name);
215        return -1;
216    }
217
218    path_index = alloc_path_setting(path);
219    if (path_index < 0)
220        return -1;
221
222    path->setting[path_index].ctl_index = setting->ctl_index;
223    path->setting[path_index].num_values = setting->num_values;
224    path->setting[path_index].value = malloc(setting->num_values * sizeof(int));
225    /* copy all values */
226    memcpy(path->setting[path_index].value, setting->value,
227           setting->num_values * sizeof(int));
228
229    return 0;
230}
231
232static int path_add_value(struct audio_route *ar, struct mixer_path *path,
233                          struct mixer_value *mixer_value)
234{
235    unsigned int i;
236    int path_index;
237    unsigned int num_values;
238    struct mixer_ctl *ctl;
239
240    /* Check that mixer value index is within range */
241    ctl = index_to_ctl(ar, mixer_value->ctl_index);
242    num_values = mixer_ctl_get_num_values(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(ctl));
246        return -1;
247    }
248
249    path_index = find_ctl_index_in_path(path, mixer_value->ctl_index);
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_index = mixer_value->ctl_index;
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].value[0] = mixer_value->value;
262    }
263
264    if (mixer_value->index == -1) {
265        /* set all values the same */
266        for (i = 0; i < num_values; i++)
267            path->setting[path_index].value[i] = mixer_value->value;
268    } else {
269        /* set only one value */
270        path->setting[path_index].value[mixer_value->index] = mixer_value->value;
271    }
272
273    return 0;
274}
275
276static int path_add_path(struct audio_route *ar, struct mixer_path *path,
277                         struct mixer_path *sub_path)
278{
279    unsigned int i;
280
281    for (i = 0; i < sub_path->length; i++)
282        if (path_add_setting(ar, path, &sub_path->setting[i]) < 0)
283            return -1;
284
285    return 0;
286}
287
288static int path_apply(struct audio_route *ar, struct mixer_path *path)
289{
290    unsigned int i;
291    unsigned int ctl_index;
292
293    for (i = 0; i < path->length; i++) {
294        ctl_index = path->setting[i].ctl_index;
295
296        /* apply the new value(s) */
297        memcpy(ar->mixer_state[ctl_index].new_value, path->setting[i].value,
298               path->setting[i].num_values * sizeof(int));
299    }
300
301    return 0;
302}
303
304static int path_reset(struct audio_route *ar, struct mixer_path *path)
305{
306    unsigned int i;
307    unsigned int j;
308    unsigned int ctl_index;
309
310    for (i = 0; i < path->length; i++) {
311        ctl_index = path->setting[i].ctl_index;
312
313        /* reset the value(s) */
314        memcpy(ar->mixer_state[ctl_index].new_value,
315               ar->mixer_state[ctl_index].reset_value,
316               ar->mixer_state[ctl_index].num_values * sizeof(int));
317    }
318
319    return 0;
320}
321
322/* mixer helper function */
323static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
324{
325    unsigned int i;
326
327    /* Search the enum strings for a particular one */
328    for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) {
329        if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
330            break;
331    }
332
333    return i;
334}
335
336static void start_tag(void *data, const XML_Char *tag_name,
337                      const XML_Char **attr)
338{
339    const XML_Char *attr_name = NULL;
340    const XML_Char *attr_id = NULL;
341    const XML_Char *attr_value = NULL;
342    struct config_parse_state *state = data;
343    struct audio_route *ar = state->ar;
344    unsigned int i;
345    unsigned int ctl_index;
346    struct mixer_ctl *ctl;
347    int value;
348    unsigned int id;
349    struct mixer_value mixer_value;
350
351    /* Get name, id and value attributes (these may be empty) */
352    for (i = 0; attr[i]; i += 2) {
353        if (strcmp(attr[i], "name") == 0)
354            attr_name = attr[i + 1];
355        if (strcmp(attr[i], "id") == 0)
356            attr_id = attr[i + 1];
357        else if (strcmp(attr[i], "value") == 0)
358            attr_value = attr[i + 1];
359    }
360
361    /* Look at tags */
362    if (strcmp(tag_name, "path") == 0) {
363        if (attr_name == NULL) {
364            ALOGE("Unnamed path!");
365        } else {
366            if (state->level == 1) {
367                /* top level path: create and stash the path */
368                state->path = path_create(ar, (char *)attr_name);
369            } else {
370                /* nested path */
371                struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
372                path_add_path(ar, state->path, sub_path);
373            }
374        }
375    }
376
377    else if (strcmp(tag_name, "ctl") == 0) {
378        /* Obtain the mixer ctl and value */
379        ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
380        if (ctl == NULL) {
381            ALOGE("Control '%s' doesn't exist - skipping", attr_name);
382            goto done;
383        }
384
385        switch (mixer_ctl_get_type(ctl)) {
386        case MIXER_CTL_TYPE_BOOL:
387        case MIXER_CTL_TYPE_INT:
388            value = atoi((char *)attr_value);
389            break;
390        case MIXER_CTL_TYPE_ENUM:
391            value = mixer_enum_string_to_value(ctl, (char *)attr_value);
392            break;
393        default:
394            value = 0;
395            break;
396        }
397
398        /* locate the mixer ctl in the list */
399        for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
400            if (ar->mixer_state[ctl_index].ctl == ctl)
401                break;
402        }
403
404        if (state->level == 1) {
405            /* top level ctl (initial setting) */
406
407            /* apply the new value */
408            if (attr_id) {
409                /* set only one value */
410                id = atoi((char *)attr_id);
411                if (id < ar->mixer_state[ctl_index].num_values)
412                    ar->mixer_state[ctl_index].new_value[id] = value;
413                else
414                    ALOGE("value id out of range for mixer ctl '%s'",
415                          mixer_ctl_get_name(ctl));
416            } else {
417                /* set all values the same */
418                for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
419                    ar->mixer_state[ctl_index].new_value[i] = value;
420            }
421        } else {
422            /* nested ctl (within a path) */
423            mixer_value.ctl_index = ctl_index;
424            mixer_value.value = value;
425            if (attr_id)
426                mixer_value.index = atoi((char *)attr_id);
427            else
428                mixer_value.index = -1;
429            path_add_value(ar, state->path, &mixer_value);
430        }
431    }
432
433done:
434    state->level++;
435}
436
437static void end_tag(void *data, const XML_Char *tag_name)
438{
439    struct config_parse_state *state = data;
440
441    state->level--;
442}
443
444static int alloc_mixer_state(struct audio_route *ar)
445{
446    unsigned int i;
447    unsigned int j;
448    unsigned int num_values;
449    struct mixer_ctl *ctl;
450    enum mixer_ctl_type type;
451
452    ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
453    ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state));
454    if (!ar->mixer_state)
455        return -1;
456
457    for (i = 0; i < ar->num_mixer_ctls; i++) {
458        ctl = mixer_get_ctl(ar->mixer, i);
459        num_values = mixer_ctl_get_num_values(ctl);
460
461        ar->mixer_state[i].ctl = ctl;
462        ar->mixer_state[i].num_values = num_values;
463
464        /* Skip unsupported types that are not supported yet in XML */
465        type = mixer_ctl_get_type(ctl);
466        if ((type != MIXER_CTL_TYPE_BOOL) && (type != MIXER_CTL_TYPE_INT) &&
467            (type != MIXER_CTL_TYPE_ENUM))
468            continue;
469
470        ar->mixer_state[i].old_value = malloc(num_values * sizeof(int));
471        ar->mixer_state[i].new_value = malloc(num_values * sizeof(int));
472        ar->mixer_state[i].reset_value = malloc(num_values * sizeof(int));
473
474        if (type == MIXER_CTL_TYPE_ENUM)
475            ar->mixer_state[i].old_value[0] = mixer_ctl_get_value(ctl, 0);
476        else
477            mixer_ctl_get_array(ctl, ar->mixer_state[i].old_value, num_values);
478        memcpy(ar->mixer_state[i].new_value, ar->mixer_state[i].old_value,
479               num_values * sizeof(int));
480    }
481
482    return 0;
483}
484
485static void free_mixer_state(struct audio_route *ar)
486{
487    unsigned int i;
488
489    for (i = 0; i < ar->num_mixer_ctls; i++) {
490        free(ar->mixer_state[i].old_value);
491        free(ar->mixer_state[i].new_value);
492        free(ar->mixer_state[i].reset_value);
493    }
494
495    free(ar->mixer_state);
496    ar->mixer_state = NULL;
497}
498
499/* Update the mixer with any changed values */
500int audio_route_update_mixer(struct audio_route *ar)
501{
502    unsigned int i;
503    unsigned int j;
504    struct mixer_ctl *ctl;
505
506    for (i = 0; i < ar->num_mixer_ctls; i++) {
507        unsigned int num_values = ar->mixer_state[i].num_values;
508        enum mixer_ctl_type type;
509
510        ctl = ar->mixer_state[i].ctl;
511
512        /* Skip unsupported types */
513        type = mixer_ctl_get_type(ctl);
514        if ((type != MIXER_CTL_TYPE_BOOL) && (type != MIXER_CTL_TYPE_INT) &&
515            (type != MIXER_CTL_TYPE_ENUM))
516            continue;
517
518        /* if the value has changed, update the mixer */
519        bool changed = false;
520        for (j = 0; j < num_values; j++) {
521            if (ar->mixer_state[i].old_value[j] != ar->mixer_state[i].new_value[j]) {
522                changed = true;
523                break;
524            }
525        }
526        if (changed) {
527            if (type == MIXER_CTL_TYPE_ENUM)
528                mixer_ctl_set_value(ctl, 0, ar->mixer_state[i].new_value[0]);
529            else
530                mixer_ctl_set_array(ctl, ar->mixer_state[i].new_value, num_values);
531            memcpy(ar->mixer_state[i].old_value, ar->mixer_state[i].new_value,
532                   num_values * sizeof(int));
533        }
534    }
535
536    return 0;
537}
538
539/* saves the current state of the mixer, for resetting all controls */
540static void save_mixer_state(struct audio_route *ar)
541{
542    unsigned int i;
543
544    for (i = 0; i < ar->num_mixer_ctls; i++) {
545        memcpy(ar->mixer_state[i].reset_value, ar->mixer_state[i].new_value,
546               ar->mixer_state[i].num_values * sizeof(int));
547    }
548}
549
550/* Reset the audio routes back to the initial state */
551void audio_route_reset(struct audio_route *ar)
552{
553    unsigned int i;
554
555    /* load all of the saved values */
556    for (i = 0; i < ar->num_mixer_ctls; i++) {
557        memcpy(ar->mixer_state[i].new_value, ar->mixer_state[i].reset_value,
558               ar->mixer_state[i].num_values * sizeof(int));
559    }
560}
561
562/* Apply an audio route path by name */
563int audio_route_apply_path(struct audio_route *ar, const char *name)
564{
565    struct mixer_path *path;
566
567    if (!ar) {
568        ALOGE("invalid audio_route");
569        return -1;
570    }
571
572    path = path_get_by_name(ar, name);
573    if (!path) {
574        ALOGE("unable to find path '%s'", name);
575        return -1;
576    }
577
578    path_apply(ar, path);
579
580    return 0;
581}
582
583/* Reset an audio route path by name */
584int audio_route_reset_path(struct audio_route *ar, const char *name)
585{
586    struct mixer_path *path;
587
588    if (!ar) {
589        ALOGE("invalid audio_route");
590        return -1;
591    }
592
593    path = path_get_by_name(ar, name);
594    if (!path) {
595        ALOGE("unable to find path '%s'", name);
596        return -1;
597    }
598
599    path_reset(ar, path);
600
601    return 0;
602}
603
604struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
605{
606    struct config_parse_state state;
607    XML_Parser parser;
608    FILE *file;
609    int bytes_read;
610    void *buf;
611    int i;
612    struct audio_route *ar;
613
614    ar = calloc(1, sizeof(struct audio_route));
615    if (!ar)
616        goto err_calloc;
617
618    ar->mixer = mixer_open(card);
619    if (!ar->mixer) {
620        ALOGE("Unable to open the mixer, aborting.");
621        goto err_mixer_open;
622    }
623
624    ar->mixer_path = NULL;
625    ar->mixer_path_size = 0;
626    ar->num_mixer_paths = 0;
627
628    /* allocate space for and read current mixer settings */
629    if (alloc_mixer_state(ar) < 0)
630        goto err_mixer_state;
631
632    /* use the default XML path if none is provided */
633    if (xml_path == NULL)
634        xml_path = MIXER_XML_PATH;
635
636    file = fopen(xml_path, "r");
637
638    if (!file) {
639        ALOGE("Failed to open %s", xml_path);
640        goto err_fopen;
641    }
642
643    parser = XML_ParserCreate(NULL);
644    if (!parser) {
645        ALOGE("Failed to create XML parser");
646        goto err_parser_create;
647    }
648
649    memset(&state, 0, sizeof(state));
650    state.ar = ar;
651    XML_SetUserData(parser, &state);
652    XML_SetElementHandler(parser, start_tag, end_tag);
653
654    for (;;) {
655        buf = XML_GetBuffer(parser, BUF_SIZE);
656        if (buf == NULL)
657            goto err_parse;
658
659        bytes_read = fread(buf, 1, BUF_SIZE, file);
660        if (bytes_read < 0)
661            goto err_parse;
662
663        if (XML_ParseBuffer(parser, bytes_read,
664                            bytes_read == 0) == XML_STATUS_ERROR) {
665            ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
666            goto err_parse;
667        }
668
669        if (bytes_read == 0)
670            break;
671    }
672
673    /* apply the initial mixer values, and save them so we can reset the
674       mixer to the original values */
675    audio_route_update_mixer(ar);
676    save_mixer_state(ar);
677
678    XML_ParserFree(parser);
679    fclose(file);
680    return ar;
681
682err_parse:
683    XML_ParserFree(parser);
684err_parser_create:
685    fclose(file);
686err_fopen:
687    free_mixer_state(ar);
688err_mixer_state:
689    mixer_close(ar->mixer);
690err_mixer_open:
691    free(ar);
692    ar = NULL;
693err_calloc:
694    return NULL;
695}
696
697void audio_route_free(struct audio_route *ar)
698{
699    free_mixer_state(ar);
700    mixer_close(ar->mixer);
701    free(ar);
702}
703