1cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/*
2cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * Copyright (C) 2013 The Android Open Source Project
3cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * Inspired by TinyHW, written by Mark Brown at Wolfson Micro
4cef935958069ffba745cd091e6e1687095ea6785Simon Wilson *
5cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * Licensed under the Apache License, Version 2.0 (the "License");
6cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * you may not use this file except in compliance with the License.
7cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * You may obtain a copy of the License at
8cef935958069ffba745cd091e6e1687095ea6785Simon Wilson *
9cef935958069ffba745cd091e6e1687095ea6785Simon Wilson *      http://www.apache.org/licenses/LICENSE-2.0
10cef935958069ffba745cd091e6e1687095ea6785Simon Wilson *
11cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * Unless required by applicable law or agreed to in writing, software
12cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * distributed under the License is distributed on an "AS IS" BASIS,
13cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * See the License for the specific language governing permissions and
15cef935958069ffba745cd091e6e1687095ea6785Simon Wilson * limitations under the License.
16cef935958069ffba745cd091e6e1687095ea6785Simon Wilson */
17cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
18cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#define LOG_TAG "audio_route"
19cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/*#define LOG_NDEBUG 0*/
20cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
21cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#include <errno.h>
22cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#include <expat.h>
23cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#include <stdbool.h>
24cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#include <stdio.h>
25cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#include <string.h>
26cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
27cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#include <cutils/log.h>
28cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
29cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#include <tinyalsa/asoundlib.h>
30cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
31cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#define BUF_SIZE 1024
32cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
33cef935958069ffba745cd091e6e1687095ea6785Simon Wilson#define INITIAL_MIXER_PATH_SIZE 8
34cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
35cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstruct mixer_state {
36cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_ctl *ctl;
37cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int num_values;
38cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int *old_value;
39cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int *new_value;
40cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int *reset_value;
41cef935958069ffba745cd091e6e1687095ea6785Simon Wilson};
42cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
43cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstruct mixer_setting {
44f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    unsigned int ctl_index;
45cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int num_values;
46cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int *value;
47cef935958069ffba745cd091e6e1687095ea6785Simon Wilson};
48cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
49cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstruct mixer_value {
50f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    unsigned int ctl_index;
51cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int index;
52cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int value;
53cef935958069ffba745cd091e6e1687095ea6785Simon Wilson};
54cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
55cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstruct mixer_path {
56cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    char *name;
57cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int size;
58cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int length;
59cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_setting *setting;
60cef935958069ffba745cd091e6e1687095ea6785Simon Wilson};
61cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
62cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstruct audio_route {
63cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer *mixer;
64cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int num_mixer_ctls;
65cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_state *mixer_state;
66cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
67cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int mixer_path_size;
68cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int num_mixer_paths;
69cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_path *mixer_path;
70cef935958069ffba745cd091e6e1687095ea6785Simon Wilson};
71cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
72cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstruct config_parse_state {
73cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct audio_route *ar;
74cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_path *path;
75cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int level;
76cef935958069ffba745cd091e6e1687095ea6785Simon Wilson};
77cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
78cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/* path functions */
79cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
80f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilsonstatic inline struct mixer_ctl *index_to_ctl(struct audio_route *ar,
81f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson                                             unsigned int ctl_index)
82f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson{
83f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    return ar->mixer_state[ctl_index].ctl;
84f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson}
85f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson
86f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilsonstatic void path_print(struct audio_route *ar, struct mixer_path *path)
87cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
88cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
89cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int j;
90cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
91cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ALOGE("Path: %s, length: %d", path->name, path->length);
92cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < path->length; i++) {
93f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        struct mixer_ctl *ctl = index_to_ctl(ar, path->setting[i].ctl_index);
94f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson
9563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        ALOGE("  id=%d: ctl=%s", i, mixer_ctl_get_name(ctl));
96cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        for (j = 0; j < path->setting[i].num_values; j++)
97cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ALOGE("    id=%d value=%d", j, path->setting[i].value[j]);
98cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
99cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
100cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
101cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic void path_free(struct audio_route *ar)
102cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
103cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
104cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
105cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < ar->num_mixer_paths; i++) {
106cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (ar->mixer_path[i].name)
107cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            free(ar->mixer_path[i].name);
108cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (ar->mixer_path[i].setting) {
109cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            if (ar->mixer_path[i].setting->value)
110cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                free(ar->mixer_path[i].setting->value);
111cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            free(ar->mixer_path[i].setting);
112cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
113cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
114cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    free(ar->mixer_path);
115cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
116cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
117cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic struct mixer_path *path_get_by_name(struct audio_route *ar,
118cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                                           const char *name)
119cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
120cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
121cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
122cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < ar->num_mixer_paths; i++)
123cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (strcmp(ar->mixer_path[i].name, name) == 0)
124cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            return &ar->mixer_path[i];
125cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
126cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return NULL;
127cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
128cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
129cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic struct mixer_path *path_create(struct audio_route *ar, const char *name)
130cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
131cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_path *new_mixer_path = NULL;
132cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
133cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (path_get_by_name(ar, name)) {
134cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("Path name '%s' already exists", name);
135cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        return NULL;
136cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
137cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
138cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* check if we need to allocate more space for mixer paths */
139cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (ar->mixer_path_size <= ar->num_mixer_paths) {
140cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (ar->mixer_path_size == 0)
141cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
142cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        else
143cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ar->mixer_path_size *= 2;
144cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
145cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
146cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                                 sizeof(struct mixer_path));
147cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (new_mixer_path == NULL) {
148cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ALOGE("Unable to allocate more paths");
149cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            return NULL;
150cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        } else {
151cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ar->mixer_path = new_mixer_path;
152cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
153cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
154cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
155cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* initialise the new mixer path */
156cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
157cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_path[ar->num_mixer_paths].size = 0;
158cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_path[ar->num_mixer_paths].length = 0;
159cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_path[ar->num_mixer_paths].setting = NULL;
160cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
161cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* return the mixer path just added, then increment number of them */
162cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return &ar->mixer_path[ar->num_mixer_paths++];
163cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
164cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
165f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilsonstatic int find_ctl_index_in_path(struct mixer_path *path,
166f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson                                  unsigned int ctl_index)
167cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
168cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
169cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
170cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < path->length; i++)
171f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        if (path->setting[i].ctl_index == ctl_index)
172cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            return i;
173cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
174cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return -1;
175cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
176cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
177cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic int alloc_path_setting(struct mixer_path *path)
178cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
179cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_setting *new_path_setting;
180cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int path_index;
181cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
182cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* check if we need to allocate more space for path settings */
183cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (path->size <= path->length) {
184cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (path->size == 0)
185cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            path->size = INITIAL_MIXER_PATH_SIZE;
186cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        else
187cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            path->size *= 2;
188cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
189cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        new_path_setting = realloc(path->setting,
190cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                                   path->size * sizeof(struct mixer_setting));
191cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (new_path_setting == NULL) {
192cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ALOGE("Unable to allocate more path settings");
193cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            return -1;
194cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        } else {
195cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            path->setting = new_path_setting;
196cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
197cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
198cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
199cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    path_index = path->length;
200cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    path->length++;
201cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
202cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return path_index;
203cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
204cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
205f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilsonstatic int path_add_setting(struct audio_route *ar, struct mixer_path *path,
206cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                            struct mixer_setting *setting)
207cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
208cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int path_index;
209cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
210f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    if (find_ctl_index_in_path(path, setting->ctl_index) != -1) {
211f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        struct mixer_ctl *ctl = index_to_ctl(ar, setting->ctl_index);
212f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson
213cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("Control '%s' already exists in path '%s'",
214f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson              mixer_ctl_get_name(ctl), path->name);
215cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        return -1;
216cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
217cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
218cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    path_index = alloc_path_setting(path);
219cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (path_index < 0)
220cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        return -1;
221cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
222f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    path->setting[path_index].ctl_index = setting->ctl_index;
223cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    path->setting[path_index].num_values = setting->num_values;
224cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    path->setting[path_index].value = malloc(setting->num_values * sizeof(int));
22563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda    /* copy all values */
22663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda    memcpy(path->setting[path_index].value, setting->value,
22763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda           setting->num_values * sizeof(int));
228cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
229cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return 0;
230cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
231cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
232f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilsonstatic int path_add_value(struct audio_route *ar, struct mixer_path *path,
233cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                          struct mixer_value *mixer_value)
234cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
235cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
236cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int path_index;
237cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int num_values;
238f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    struct mixer_ctl *ctl;
239cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
240cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* Check that mixer value index is within range */
241f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    ctl = index_to_ctl(ar, mixer_value->ctl_index);
242f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    num_values = mixer_ctl_get_num_values(ctl);
243cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (mixer_value->index >= (int)num_values) {
244cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("mixer index %d is out of range for '%s'", mixer_value->index,
245f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson              mixer_ctl_get_name(ctl));
246cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        return -1;
247cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
248cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
249f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson    path_index = find_ctl_index_in_path(path, mixer_value->ctl_index);
250cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (path_index < 0) {
251cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        /* New path */
252cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
253cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        path_index = alloc_path_setting(path);
254cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (path_index < 0)
255cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            return -1;
256cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
257cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        /* initialise the new path setting */
258f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        path->setting[path_index].ctl_index = mixer_value->ctl_index;
259cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        path->setting[path_index].num_values = num_values;
260cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        path->setting[path_index].value = malloc(num_values * sizeof(int));
261cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        path->setting[path_index].value[0] = mixer_value->value;
262cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
263cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
264cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (mixer_value->index == -1) {
26563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        /* set all values the same */
26663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        for (i = 0; i < num_values; i++)
26763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            path->setting[path_index].value[i] = mixer_value->value;
268cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    } else {
26963b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        /* set only one value */
270cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        path->setting[path_index].value[mixer_value->index] = mixer_value->value;
271cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
272cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
273cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return 0;
274cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
275cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
276f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilsonstatic int path_add_path(struct audio_route *ar, struct mixer_path *path,
277f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson                         struct mixer_path *sub_path)
278cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
279cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
280cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
281cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < sub_path->length; i++)
282f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        if (path_add_setting(ar, path, &sub_path->setting[i]) < 0)
283cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            return -1;
284cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
285cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return 0;
286cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
287cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
288cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic int path_apply(struct audio_route *ar, struct mixer_path *path)
289cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
290cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
291cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int ctl_index;
292cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
293cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < path->length; i++) {
294f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        ctl_index = path->setting[i].ctl_index;
295cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
296cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        /* apply the new value(s) */
29763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        memcpy(ar->mixer_state[ctl_index].new_value, path->setting[i].value,
29863b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda               path->setting[i].num_values * sizeof(int));
299cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
300cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
301cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return 0;
302cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
303cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
30472c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamandastatic int path_reset(struct audio_route *ar, struct mixer_path *path)
30572c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda{
30672c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    unsigned int i;
30772c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    unsigned int j;
30872c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    unsigned int ctl_index;
30972c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
31072c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    for (i = 0; i < path->length; i++) {
311f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        ctl_index = path->setting[i].ctl_index;
31272c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
31372c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda        /* reset the value(s) */
31463b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        memcpy(ar->mixer_state[ctl_index].new_value,
31563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda               ar->mixer_state[ctl_index].reset_value,
31663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda               ar->mixer_state[ctl_index].num_values * sizeof(int));
31772c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    }
31872c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
31972c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    return 0;
32072c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda}
32172c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
322cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/* mixer helper function */
323cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
324cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
325cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
326cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
327cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* Search the enum strings for a particular one */
328cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) {
329cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
330cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            break;
331cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
332cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
333cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return i;
334cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
335cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
336cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic void start_tag(void *data, const XML_Char *tag_name,
337cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                      const XML_Char **attr)
338cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
339cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    const XML_Char *attr_name = NULL;
340cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    const XML_Char *attr_id = NULL;
341cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    const XML_Char *attr_value = NULL;
342cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct config_parse_state *state = data;
343cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct audio_route *ar = state->ar;
344cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
345cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int ctl_index;
346cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_ctl *ctl;
347cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int value;
348cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int id;
349cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_value mixer_value;
350cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
351cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* Get name, id and value attributes (these may be empty) */
352cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; attr[i]; i += 2) {
353cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (strcmp(attr[i], "name") == 0)
354cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            attr_name = attr[i + 1];
355cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (strcmp(attr[i], "id") == 0)
356cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            attr_id = attr[i + 1];
357cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        else if (strcmp(attr[i], "value") == 0)
358cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            attr_value = attr[i + 1];
359cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
360cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
361cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* Look at tags */
362cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (strcmp(tag_name, "path") == 0) {
363cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (attr_name == NULL) {
364cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ALOGE("Unnamed path!");
365cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        } else {
366cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            if (state->level == 1) {
367cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                /* top level path: create and stash the path */
368cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                state->path = path_create(ar, (char *)attr_name);
369cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            } else {
370cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                /* nested path */
371cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
372f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson                path_add_path(ar, state->path, sub_path);
373cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            }
374cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
375cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
376cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
377cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    else if (strcmp(tag_name, "ctl") == 0) {
378cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        /* Obtain the mixer ctl and value */
379cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
380d35bd63c74452b83eef34cff4f0831493d0781faSimon Wilson        if (ctl == NULL) {
381d35bd63c74452b83eef34cff4f0831493d0781faSimon Wilson            ALOGE("Control '%s' doesn't exist - skipping", attr_name);
382d35bd63c74452b83eef34cff4f0831493d0781faSimon Wilson            goto done;
383d35bd63c74452b83eef34cff4f0831493d0781faSimon Wilson        }
384d35bd63c74452b83eef34cff4f0831493d0781faSimon Wilson
385cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        switch (mixer_ctl_get_type(ctl)) {
386cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        case MIXER_CTL_TYPE_BOOL:
387cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        case MIXER_CTL_TYPE_INT:
388cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            value = atoi((char *)attr_value);
389cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            break;
390cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        case MIXER_CTL_TYPE_ENUM:
391cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            value = mixer_enum_string_to_value(ctl, (char *)attr_value);
392cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            break;
393cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        default:
394cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            value = 0;
395cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            break;
396cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
397cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
398f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        /* locate the mixer ctl in the list */
399f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
400f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson            if (ar->mixer_state[ctl_index].ctl == ctl)
401f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson                break;
402f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson        }
403f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson
404cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (state->level == 1) {
405cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            /* top level ctl (initial setting) */
406cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
407cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            /* apply the new value */
408cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            if (attr_id) {
409cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                /* set only one value */
410cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                id = atoi((char *)attr_id);
41163b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                if (id < ar->mixer_state[ctl_index].num_values)
412cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                    ar->mixer_state[ctl_index].new_value[id] = value;
41363b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                else
414cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                    ALOGE("value id out of range for mixer ctl '%s'",
415cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                          mixer_ctl_get_name(ctl));
416cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            } else {
41763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                /* set all values the same */
41863b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
41963b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                    ar->mixer_state[ctl_index].new_value[i] = value;
420cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            }
421cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        } else {
422cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            /* nested ctl (within a path) */
423f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson            mixer_value.ctl_index = ctl_index;
424cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            mixer_value.value = value;
425cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            if (attr_id)
426cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                mixer_value.index = atoi((char *)attr_id);
427cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            else
428cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                mixer_value.index = -1;
429f3090fa9ce87c45aecb95a2c98a5cfcc2d3ee99aSimon Wilson            path_add_value(ar, state->path, &mixer_value);
430cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
431cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
432cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
433d35bd63c74452b83eef34cff4f0831493d0781faSimon Wilsondone:
434cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    state->level++;
435cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
436cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
437cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic void end_tag(void *data, const XML_Char *tag_name)
438cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
439cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct config_parse_state *state = data;
440cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
441cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    state->level--;
442cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
443cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
444cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic int alloc_mixer_state(struct audio_route *ar)
445cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
446cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
447cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int j;
448cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int num_values;
449cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_ctl *ctl;
45063b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda    enum mixer_ctl_type type;
451cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
452cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
453cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state));
454cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (!ar->mixer_state)
455cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        return -1;
456cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
457cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < ar->num_mixer_ctls; i++) {
458cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ctl = mixer_get_ctl(ar->mixer, i);
459cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        num_values = mixer_ctl_get_num_values(ctl);
460cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
46163b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        ar->mixer_state[i].ctl = ctl;
46263b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        ar->mixer_state[i].num_values = num_values;
46363b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda
46463b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        /* Skip unsupported types that are not supported yet in XML */
46563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        type = mixer_ctl_get_type(ctl);
46663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        if ((type != MIXER_CTL_TYPE_BOOL) && (type != MIXER_CTL_TYPE_INT) &&
46763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            (type != MIXER_CTL_TYPE_ENUM))
46863b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            continue;
46963b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda
470cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ar->mixer_state[i].old_value = malloc(num_values * sizeof(int));
471cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ar->mixer_state[i].new_value = malloc(num_values * sizeof(int));
472cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ar->mixer_state[i].reset_value = malloc(num_values * sizeof(int));
473cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
47463b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        if (type == MIXER_CTL_TYPE_ENUM)
47563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            ar->mixer_state[i].old_value[0] = mixer_ctl_get_value(ctl, 0);
47663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        else
47763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            mixer_ctl_get_array(ctl, ar->mixer_state[i].old_value, num_values);
47863b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        memcpy(ar->mixer_state[i].new_value, ar->mixer_state[i].old_value,
47963b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda               num_values * sizeof(int));
480cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
481cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
482cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return 0;
483cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
484cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
485cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic void free_mixer_state(struct audio_route *ar)
486cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
487cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
488cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
489cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < ar->num_mixer_ctls; i++) {
490cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        free(ar->mixer_state[i].old_value);
491cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        free(ar->mixer_state[i].new_value);
492cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        free(ar->mixer_state[i].reset_value);
493cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
494cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
495cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    free(ar->mixer_state);
496cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_state = NULL;
497cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
498cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
499cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/* Update the mixer with any changed values */
500cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonint audio_route_update_mixer(struct audio_route *ar)
501cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
502cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
503cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int j;
50463b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda    struct mixer_ctl *ctl;
505cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
506cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < ar->num_mixer_ctls; i++) {
507cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        unsigned int num_values = ar->mixer_state[i].num_values;
50863b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        enum mixer_ctl_type type;
50963b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda
51063b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        ctl = ar->mixer_state[i].ctl;
51163b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda
51263b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        /* Skip unsupported types */
51363b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        type = mixer_ctl_get_type(ctl);
51463b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        if ((type != MIXER_CTL_TYPE_BOOL) && (type != MIXER_CTL_TYPE_INT) &&
51563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            (type != MIXER_CTL_TYPE_ENUM))
51663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            continue;
517cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
518cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        /* if the value has changed, update the mixer */
51963b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        bool changed = false;
52063b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        for (j = 0; j < num_values; j++) {
52163b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            if (ar->mixer_state[i].old_value[j] != ar->mixer_state[i].new_value[j]) {
52263b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                changed = true;
52363b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                break;
524cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            }
525cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
52663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        if (changed) {
52763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            if (type == MIXER_CTL_TYPE_ENUM)
52863b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                mixer_ctl_set_value(ctl, 0, ar->mixer_state[i].new_value[0]);
52963b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            else
53063b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                mixer_ctl_set_array(ctl, ar->mixer_state[i].new_value, num_values);
53163b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda            memcpy(ar->mixer_state[i].old_value, ar->mixer_state[i].new_value,
53263b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda                   num_values * sizeof(int));
53363b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        }
534cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
535cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
536cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return 0;
537cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
538cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
539cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/* saves the current state of the mixer, for resetting all controls */
540cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstatic void save_mixer_state(struct audio_route *ar)
541cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
542cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
543cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
544cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < ar->num_mixer_ctls; i++) {
54563b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        memcpy(ar->mixer_state[i].reset_value, ar->mixer_state[i].new_value,
54663b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda               ar->mixer_state[i].num_values * sizeof(int));
547cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
548cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
549cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
550cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/* Reset the audio routes back to the initial state */
551cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonvoid audio_route_reset(struct audio_route *ar)
552cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
553cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    unsigned int i;
554cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
555cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* load all of the saved values */
556cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (i = 0; i < ar->num_mixer_ctls; i++) {
55763b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda        memcpy(ar->mixer_state[i].new_value, ar->mixer_state[i].reset_value,
55863b31c18b37409e1b024c50df94798aac9d0fa7dRavi Kumar Alamanda               ar->mixer_state[i].num_values * sizeof(int));
559cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
560cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
561cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
562cef935958069ffba745cd091e6e1687095ea6785Simon Wilson/* Apply an audio route path by name */
563cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonint audio_route_apply_path(struct audio_route *ar, const char *name)
564cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
565cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct mixer_path *path;
566cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
567cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (!ar) {
568cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("invalid audio_route");
569cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        return -1;
570cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
571cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
572cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    path = path_get_by_name(ar, name);
573cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (!path) {
574cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("unable to find path '%s'", name);
575cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        return -1;
576cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
577cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
578cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    path_apply(ar, path);
579cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
580cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return 0;
581cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
582cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
58372c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda/* Reset an audio route path by name */
58472c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamandaint audio_route_reset_path(struct audio_route *ar, const char *name)
58572c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda{
58672c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    struct mixer_path *path;
58772c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
58872c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    if (!ar) {
58972c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda        ALOGE("invalid audio_route");
59072c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda        return -1;
59172c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    }
59272c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
59372c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    path = path_get_by_name(ar, name);
59472c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    if (!path) {
59572c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda        ALOGE("unable to find path '%s'", name);
59672c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda        return -1;
59772c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    }
59872c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
59972c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    path_reset(ar, path);
60072c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
60172c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda    return 0;
60272c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda}
60372c277ef4859f7aa07ac43b3b9cc6754d6e3d505Ravi Kumar Alamanda
604cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonstruct audio_route *audio_route_init(unsigned int card, const char *xml_path)
605cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
606cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct config_parse_state state;
607cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    XML_Parser parser;
608cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    FILE *file;
609cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int bytes_read;
610cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    void *buf;
611cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    int i;
612cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    struct audio_route *ar;
613cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
614cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar = calloc(1, sizeof(struct audio_route));
615cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (!ar)
616cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        goto err_calloc;
617cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
618cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer = mixer_open(card);
619cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (!ar->mixer) {
620cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("Unable to open the mixer, aborting.");
621cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        goto err_mixer_open;
622cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
623cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
624cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_path = NULL;
625cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->mixer_path_size = 0;
626cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar->num_mixer_paths = 0;
627cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
628cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* allocate space for and read current mixer settings */
629cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (alloc_mixer_state(ar) < 0)
630cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        goto err_mixer_state;
631cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
632cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* use the default XML path if none is provided */
633cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (xml_path == NULL)
634cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        xml_path = MIXER_XML_PATH;
635cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
636cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    file = fopen(xml_path, "r");
637cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
638cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (!file) {
639cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("Failed to open %s", xml_path);
640cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        goto err_fopen;
641cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
642cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
643cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    parser = XML_ParserCreate(NULL);
644cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    if (!parser) {
645cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        ALOGE("Failed to create XML parser");
646cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        goto err_parser_create;
647cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
648cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
649cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    memset(&state, 0, sizeof(state));
650cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    state.ar = ar;
651cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    XML_SetUserData(parser, &state);
652cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    XML_SetElementHandler(parser, start_tag, end_tag);
653cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
654cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    for (;;) {
655cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        buf = XML_GetBuffer(parser, BUF_SIZE);
656cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (buf == NULL)
657cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            goto err_parse;
658cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
659cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        bytes_read = fread(buf, 1, BUF_SIZE, file);
660cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (bytes_read < 0)
661cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            goto err_parse;
662cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
663cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (XML_ParseBuffer(parser, bytes_read,
664cef935958069ffba745cd091e6e1687095ea6785Simon Wilson                            bytes_read == 0) == XML_STATUS_ERROR) {
665cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
666cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            goto err_parse;
667cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        }
668cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
669cef935958069ffba745cd091e6e1687095ea6785Simon Wilson        if (bytes_read == 0)
670cef935958069ffba745cd091e6e1687095ea6785Simon Wilson            break;
671cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    }
672cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
673cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    /* apply the initial mixer values, and save them so we can reset the
674cef935958069ffba745cd091e6e1687095ea6785Simon Wilson       mixer to the original values */
675cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    audio_route_update_mixer(ar);
676cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    save_mixer_state(ar);
677cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
678cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    XML_ParserFree(parser);
679cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    fclose(file);
680cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return ar;
681cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
682cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonerr_parse:
683cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    XML_ParserFree(parser);
684cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonerr_parser_create:
685cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    fclose(file);
686cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonerr_fopen:
687cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    free_mixer_state(ar);
688cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonerr_mixer_state:
689cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    mixer_close(ar->mixer);
690cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonerr_mixer_open:
691cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    free(ar);
692cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    ar = NULL;
693cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonerr_calloc:
694cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    return NULL;
695cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
696cef935958069ffba745cd091e6e1687095ea6785Simon Wilson
697cef935958069ffba745cd091e6e1687095ea6785Simon Wilsonvoid audio_route_free(struct audio_route *ar)
698cef935958069ffba745cd091e6e1687095ea6785Simon Wilson{
699cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    free_mixer_state(ar);
700cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    mixer_close(ar->mixer);
701cef935958069ffba745cd091e6e1687095ea6785Simon Wilson    free(ar);
702cef935958069ffba745cd091e6e1687095ea6785Simon Wilson}
703