xref: /aosp_15_r20/system/media/audio_route/audio_route.c (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
1*b9df5ad1SAndroid Build Coastguard Worker /*
2*b9df5ad1SAndroid Build Coastguard Worker  * Copyright (C) 2013 The Android Open Source Project
3*b9df5ad1SAndroid Build Coastguard Worker  * Inspired by TinyHW, written by Mark Brown at Wolfson Micro
4*b9df5ad1SAndroid Build Coastguard Worker  *
5*b9df5ad1SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
6*b9df5ad1SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
7*b9df5ad1SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
8*b9df5ad1SAndroid Build Coastguard Worker  *
9*b9df5ad1SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
10*b9df5ad1SAndroid Build Coastguard Worker  *
11*b9df5ad1SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
12*b9df5ad1SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
13*b9df5ad1SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*b9df5ad1SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
15*b9df5ad1SAndroid Build Coastguard Worker  * limitations under the License.
16*b9df5ad1SAndroid Build Coastguard Worker  */
17*b9df5ad1SAndroid Build Coastguard Worker 
18*b9df5ad1SAndroid Build Coastguard Worker #define LOG_TAG "audio_route"
19*b9df5ad1SAndroid Build Coastguard Worker /*#define LOG_NDEBUG 0*/
20*b9df5ad1SAndroid Build Coastguard Worker 
21*b9df5ad1SAndroid Build Coastguard Worker #include <errno.h>
22*b9df5ad1SAndroid Build Coastguard Worker #include <expat.h>
23*b9df5ad1SAndroid Build Coastguard Worker #include <stdbool.h>
24*b9df5ad1SAndroid Build Coastguard Worker #include <stdio.h>
25*b9df5ad1SAndroid Build Coastguard Worker #include <string.h>
26*b9df5ad1SAndroid Build Coastguard Worker 
27*b9df5ad1SAndroid Build Coastguard Worker #include <log/log.h>
28*b9df5ad1SAndroid Build Coastguard Worker 
29*b9df5ad1SAndroid Build Coastguard Worker #include <tinyalsa/asoundlib.h>
30*b9df5ad1SAndroid Build Coastguard Worker 
31*b9df5ad1SAndroid Build Coastguard Worker #define BUF_SIZE 1024
32*b9df5ad1SAndroid Build Coastguard Worker #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
33*b9df5ad1SAndroid Build Coastguard Worker #define INITIAL_MIXER_PATH_SIZE 8
34*b9df5ad1SAndroid Build Coastguard Worker 
35*b9df5ad1SAndroid Build Coastguard Worker enum update_direction {
36*b9df5ad1SAndroid Build Coastguard Worker     DIRECTION_FORWARD,
37*b9df5ad1SAndroid Build Coastguard Worker     DIRECTION_REVERSE,
38*b9df5ad1SAndroid Build Coastguard Worker     DIRECTION_REVERSE_RESET
39*b9df5ad1SAndroid Build Coastguard Worker };
40*b9df5ad1SAndroid Build Coastguard Worker 
41*b9df5ad1SAndroid Build Coastguard Worker union ctl_values {
42*b9df5ad1SAndroid Build Coastguard Worker     int *enumerated;
43*b9df5ad1SAndroid Build Coastguard Worker     long *integer;
44*b9df5ad1SAndroid Build Coastguard Worker     void *ptr;
45*b9df5ad1SAndroid Build Coastguard Worker     unsigned char *bytes;
46*b9df5ad1SAndroid Build Coastguard Worker };
47*b9df5ad1SAndroid Build Coastguard Worker 
48*b9df5ad1SAndroid Build Coastguard Worker struct mixer_state {
49*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_ctl *ctl;
50*b9df5ad1SAndroid Build Coastguard Worker     unsigned int num_values;
51*b9df5ad1SAndroid Build Coastguard Worker     union ctl_values old_value;
52*b9df5ad1SAndroid Build Coastguard Worker     union ctl_values new_value;
53*b9df5ad1SAndroid Build Coastguard Worker     union ctl_values reset_value;
54*b9df5ad1SAndroid Build Coastguard Worker     unsigned int active_count;
55*b9df5ad1SAndroid Build Coastguard Worker };
56*b9df5ad1SAndroid Build Coastguard Worker 
57*b9df5ad1SAndroid Build Coastguard Worker struct mixer_setting {
58*b9df5ad1SAndroid Build Coastguard Worker     unsigned int ctl_index;
59*b9df5ad1SAndroid Build Coastguard Worker     unsigned int num_values;
60*b9df5ad1SAndroid Build Coastguard Worker     unsigned int type;
61*b9df5ad1SAndroid Build Coastguard Worker     union ctl_values value;
62*b9df5ad1SAndroid Build Coastguard Worker };
63*b9df5ad1SAndroid Build Coastguard Worker 
64*b9df5ad1SAndroid Build Coastguard Worker struct mixer_value {
65*b9df5ad1SAndroid Build Coastguard Worker     unsigned int ctl_index;
66*b9df5ad1SAndroid Build Coastguard Worker     int index;
67*b9df5ad1SAndroid Build Coastguard Worker     long value;
68*b9df5ad1SAndroid Build Coastguard Worker     /*
69*b9df5ad1SAndroid Build Coastguard Worker      memory pointed by this is allocated in start_tag during parsing ctl of
70*b9df5ad1SAndroid Build Coastguard Worker      MIXER_CTL_TYPE_BYTE or MIXER_CTL_TYPE_INT, and is released after the
71*b9df5ad1SAndroid Build Coastguard Worker      parsed values are updated to either setting value within a path,
72*b9df5ad1SAndroid Build Coastguard Worker      or top level initial setting value
73*b9df5ad1SAndroid Build Coastguard Worker      */
74*b9df5ad1SAndroid Build Coastguard Worker     long *values;
75*b9df5ad1SAndroid Build Coastguard Worker };
76*b9df5ad1SAndroid Build Coastguard Worker 
77*b9df5ad1SAndroid Build Coastguard Worker struct mixer_path {
78*b9df5ad1SAndroid Build Coastguard Worker     char *name;
79*b9df5ad1SAndroid Build Coastguard Worker     unsigned int size;
80*b9df5ad1SAndroid Build Coastguard Worker     unsigned int length;
81*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_setting *setting;
82*b9df5ad1SAndroid Build Coastguard Worker };
83*b9df5ad1SAndroid Build Coastguard Worker 
84*b9df5ad1SAndroid Build Coastguard Worker struct audio_route {
85*b9df5ad1SAndroid Build Coastguard Worker     struct mixer *mixer;
86*b9df5ad1SAndroid Build Coastguard Worker     unsigned int num_mixer_ctls;
87*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_state *mixer_state;
88*b9df5ad1SAndroid Build Coastguard Worker 
89*b9df5ad1SAndroid Build Coastguard Worker     unsigned int mixer_path_size;
90*b9df5ad1SAndroid Build Coastguard Worker     unsigned int num_mixer_paths;
91*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_path *mixer_path;
92*b9df5ad1SAndroid Build Coastguard Worker };
93*b9df5ad1SAndroid Build Coastguard Worker 
94*b9df5ad1SAndroid Build Coastguard Worker struct config_parse_state {
95*b9df5ad1SAndroid Build Coastguard Worker     struct audio_route *ar;
96*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_path *path;
97*b9df5ad1SAndroid Build Coastguard Worker     int level;
98*b9df5ad1SAndroid Build Coastguard Worker     bool enum_mixer_numeric_fallback;
99*b9df5ad1SAndroid Build Coastguard Worker };
100*b9df5ad1SAndroid Build Coastguard Worker 
101*b9df5ad1SAndroid Build Coastguard Worker /* path functions */
102*b9df5ad1SAndroid Build Coastguard Worker 
is_supported_ctl_type(enum mixer_ctl_type type)103*b9df5ad1SAndroid Build Coastguard Worker static bool is_supported_ctl_type(enum mixer_ctl_type type)
104*b9df5ad1SAndroid Build Coastguard Worker {
105*b9df5ad1SAndroid Build Coastguard Worker     switch (type) {
106*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_BOOL:
107*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_INT:
108*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_ENUM:
109*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_BYTE:
110*b9df5ad1SAndroid Build Coastguard Worker         return true;
111*b9df5ad1SAndroid Build Coastguard Worker     default:
112*b9df5ad1SAndroid Build Coastguard Worker         return false;
113*b9df5ad1SAndroid Build Coastguard Worker     }
114*b9df5ad1SAndroid Build Coastguard Worker }
115*b9df5ad1SAndroid Build Coastguard Worker 
116*b9df5ad1SAndroid Build Coastguard Worker /* as they match in alsa */
sizeof_ctl_type(enum mixer_ctl_type type)117*b9df5ad1SAndroid Build Coastguard Worker static size_t sizeof_ctl_type(enum mixer_ctl_type type) {
118*b9df5ad1SAndroid Build Coastguard Worker     switch (type) {
119*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_BOOL:
120*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_INT:
121*b9df5ad1SAndroid Build Coastguard Worker         return sizeof(long);
122*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_ENUM:
123*b9df5ad1SAndroid Build Coastguard Worker         return sizeof(int);
124*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_BYTE:
125*b9df5ad1SAndroid Build Coastguard Worker         return sizeof(unsigned char);
126*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_INT64:
127*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_IEC958:
128*b9df5ad1SAndroid Build Coastguard Worker     case MIXER_CTL_TYPE_UNKNOWN:
129*b9df5ad1SAndroid Build Coastguard Worker     default:
130*b9df5ad1SAndroid Build Coastguard Worker         LOG_ALWAYS_FATAL("Unsupported mixer ctl type: %d, check type before calling", (int)type);
131*b9df5ad1SAndroid Build Coastguard Worker         return 0;
132*b9df5ad1SAndroid Build Coastguard Worker     }
133*b9df5ad1SAndroid Build Coastguard Worker }
134*b9df5ad1SAndroid Build Coastguard Worker 
index_to_ctl(struct audio_route * ar,unsigned int ctl_index)135*b9df5ad1SAndroid Build Coastguard Worker static inline struct mixer_ctl *index_to_ctl(struct audio_route *ar,
136*b9df5ad1SAndroid Build Coastguard Worker                                              unsigned int ctl_index)
137*b9df5ad1SAndroid Build Coastguard Worker {
138*b9df5ad1SAndroid Build Coastguard Worker     return ar->mixer_state[ctl_index].ctl;
139*b9df5ad1SAndroid Build Coastguard Worker }
140*b9df5ad1SAndroid Build Coastguard Worker 
141*b9df5ad1SAndroid Build Coastguard Worker #if 0
142*b9df5ad1SAndroid Build Coastguard Worker static void path_print(struct audio_route *ar, struct mixer_path *path)
143*b9df5ad1SAndroid Build Coastguard Worker {
144*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
145*b9df5ad1SAndroid Build Coastguard Worker     unsigned int j;
146*b9df5ad1SAndroid Build Coastguard Worker 
147*b9df5ad1SAndroid Build Coastguard Worker     ALOGE("Path: %s, length: %d", path->name, path->length);
148*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < path->length; i++) {
149*b9df5ad1SAndroid Build Coastguard Worker         struct mixer_ctl *ctl = index_to_ctl(ar, path->setting[i].ctl_index);
150*b9df5ad1SAndroid Build Coastguard Worker 
151*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("  id=%d: ctl=%s", i, mixer_ctl_get_name(ctl));
152*b9df5ad1SAndroid Build Coastguard Worker         if (mixer_ctl_get_type(ctl) == MIXER_CTL_TYPE_BYTE) {
153*b9df5ad1SAndroid Build Coastguard Worker             for (j = 0; j < path->setting[i].num_values; j++)
154*b9df5ad1SAndroid Build Coastguard Worker                 ALOGE("    id=%d value=0x%02x", j, path->setting[i].value.bytes[j]);
155*b9df5ad1SAndroid Build Coastguard Worker         } else if (mixer_ctl_get_type(ctl) == MIXER_CTL_TYPE_ENUM) {
156*b9df5ad1SAndroid Build Coastguard Worker             for (j = 0; j < path->setting[i].num_values; j++)
157*b9df5ad1SAndroid Build Coastguard Worker                 ALOGE("    id=%d value=%d", j, path->setting[i].value.enumerated[j]);
158*b9df5ad1SAndroid Build Coastguard Worker         } else {
159*b9df5ad1SAndroid Build Coastguard Worker             for (j = 0; j < path->setting[i].num_values; j++)
160*b9df5ad1SAndroid Build Coastguard Worker                 ALOGE("    id=%d value=%ld", j, path->setting[i].value.integer[j]);
161*b9df5ad1SAndroid Build Coastguard Worker         }
162*b9df5ad1SAndroid Build Coastguard Worker     }
163*b9df5ad1SAndroid Build Coastguard Worker }
164*b9df5ad1SAndroid Build Coastguard Worker #endif
165*b9df5ad1SAndroid Build Coastguard Worker 
path_free(struct audio_route * ar)166*b9df5ad1SAndroid Build Coastguard Worker static void path_free(struct audio_route *ar)
167*b9df5ad1SAndroid Build Coastguard Worker {
168*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
169*b9df5ad1SAndroid Build Coastguard Worker 
170*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < ar->num_mixer_paths; i++) {
171*b9df5ad1SAndroid Build Coastguard Worker         free(ar->mixer_path[i].name);
172*b9df5ad1SAndroid Build Coastguard Worker         if (ar->mixer_path[i].setting) {
173*b9df5ad1SAndroid Build Coastguard Worker             size_t j;
174*b9df5ad1SAndroid Build Coastguard Worker             for (j = 0; j < ar->mixer_path[i].length; j++) {
175*b9df5ad1SAndroid Build Coastguard Worker                 free(ar->mixer_path[i].setting[j].value.ptr);
176*b9df5ad1SAndroid Build Coastguard Worker             }
177*b9df5ad1SAndroid Build Coastguard Worker             free(ar->mixer_path[i].setting);
178*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_path[i].size = 0;
179*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_path[i].length = 0;
180*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_path[i].setting = NULL;
181*b9df5ad1SAndroid Build Coastguard Worker         }
182*b9df5ad1SAndroid Build Coastguard Worker     }
183*b9df5ad1SAndroid Build Coastguard Worker     free(ar->mixer_path);
184*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path = NULL;
185*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path_size = 0;
186*b9df5ad1SAndroid Build Coastguard Worker     ar->num_mixer_paths = 0;
187*b9df5ad1SAndroid Build Coastguard Worker }
188*b9df5ad1SAndroid Build Coastguard Worker 
path_get_by_name(struct audio_route * ar,const char * name)189*b9df5ad1SAndroid Build Coastguard Worker static struct mixer_path *path_get_by_name(struct audio_route *ar,
190*b9df5ad1SAndroid Build Coastguard Worker                                            const char *name)
191*b9df5ad1SAndroid Build Coastguard Worker {
192*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
193*b9df5ad1SAndroid Build Coastguard Worker 
194*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < ar->num_mixer_paths; i++)
195*b9df5ad1SAndroid Build Coastguard Worker         if (strcmp(ar->mixer_path[i].name, name) == 0)
196*b9df5ad1SAndroid Build Coastguard Worker             return &ar->mixer_path[i];
197*b9df5ad1SAndroid Build Coastguard Worker 
198*b9df5ad1SAndroid Build Coastguard Worker     return NULL;
199*b9df5ad1SAndroid Build Coastguard Worker }
200*b9df5ad1SAndroid Build Coastguard Worker 
path_create(struct audio_route * ar,const char * name)201*b9df5ad1SAndroid Build Coastguard Worker static struct mixer_path *path_create(struct audio_route *ar, const char *name)
202*b9df5ad1SAndroid Build Coastguard Worker {
203*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_path *new_mixer_path = NULL;
204*b9df5ad1SAndroid Build Coastguard Worker 
205*b9df5ad1SAndroid Build Coastguard Worker     if (path_get_by_name(ar, name)) {
206*b9df5ad1SAndroid Build Coastguard Worker         ALOGW("Path name '%s' already exists", name);
207*b9df5ad1SAndroid Build Coastguard Worker         return NULL;
208*b9df5ad1SAndroid Build Coastguard Worker     }
209*b9df5ad1SAndroid Build Coastguard Worker 
210*b9df5ad1SAndroid Build Coastguard Worker     /* check if we need to allocate more space for mixer paths */
211*b9df5ad1SAndroid Build Coastguard Worker     if (ar->mixer_path_size <= ar->num_mixer_paths) {
212*b9df5ad1SAndroid Build Coastguard Worker         if (ar->mixer_path_size == 0)
213*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
214*b9df5ad1SAndroid Build Coastguard Worker         else
215*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_path_size *= 2;
216*b9df5ad1SAndroid Build Coastguard Worker 
217*b9df5ad1SAndroid Build Coastguard Worker         new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
218*b9df5ad1SAndroid Build Coastguard Worker                                  sizeof(struct mixer_path));
219*b9df5ad1SAndroid Build Coastguard Worker         if (new_mixer_path == NULL) {
220*b9df5ad1SAndroid Build Coastguard Worker             ALOGE("Unable to allocate more paths");
221*b9df5ad1SAndroid Build Coastguard Worker             return NULL;
222*b9df5ad1SAndroid Build Coastguard Worker         } else {
223*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_path = new_mixer_path;
224*b9df5ad1SAndroid Build Coastguard Worker         }
225*b9df5ad1SAndroid Build Coastguard Worker     }
226*b9df5ad1SAndroid Build Coastguard Worker 
227*b9df5ad1SAndroid Build Coastguard Worker     /* initialise the new mixer path */
228*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
229*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path[ar->num_mixer_paths].size = 0;
230*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path[ar->num_mixer_paths].length = 0;
231*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path[ar->num_mixer_paths].setting = NULL;
232*b9df5ad1SAndroid Build Coastguard Worker 
233*b9df5ad1SAndroid Build Coastguard Worker     /* return the mixer path just added, then increment number of them */
234*b9df5ad1SAndroid Build Coastguard Worker     return &ar->mixer_path[ar->num_mixer_paths++];
235*b9df5ad1SAndroid Build Coastguard Worker }
236*b9df5ad1SAndroid Build Coastguard Worker 
find_ctl_index_in_path(struct mixer_path * path,unsigned int ctl_index)237*b9df5ad1SAndroid Build Coastguard Worker static int find_ctl_index_in_path(struct mixer_path *path,
238*b9df5ad1SAndroid Build Coastguard Worker                                   unsigned int ctl_index)
239*b9df5ad1SAndroid Build Coastguard Worker {
240*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
241*b9df5ad1SAndroid Build Coastguard Worker 
242*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < path->length; i++)
243*b9df5ad1SAndroid Build Coastguard Worker         if (path->setting[i].ctl_index == ctl_index)
244*b9df5ad1SAndroid Build Coastguard Worker             return i;
245*b9df5ad1SAndroid Build Coastguard Worker 
246*b9df5ad1SAndroid Build Coastguard Worker     return -1;
247*b9df5ad1SAndroid Build Coastguard Worker }
248*b9df5ad1SAndroid Build Coastguard Worker 
alloc_path_setting(struct mixer_path * path)249*b9df5ad1SAndroid Build Coastguard Worker static int alloc_path_setting(struct mixer_path *path)
250*b9df5ad1SAndroid Build Coastguard Worker {
251*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_setting *new_path_setting;
252*b9df5ad1SAndroid Build Coastguard Worker     int path_index;
253*b9df5ad1SAndroid Build Coastguard Worker 
254*b9df5ad1SAndroid Build Coastguard Worker     /* check if we need to allocate more space for path settings */
255*b9df5ad1SAndroid Build Coastguard Worker     if (path->size <= path->length) {
256*b9df5ad1SAndroid Build Coastguard Worker         if (path->size == 0)
257*b9df5ad1SAndroid Build Coastguard Worker             path->size = INITIAL_MIXER_PATH_SIZE;
258*b9df5ad1SAndroid Build Coastguard Worker         else
259*b9df5ad1SAndroid Build Coastguard Worker             path->size *= 2;
260*b9df5ad1SAndroid Build Coastguard Worker 
261*b9df5ad1SAndroid Build Coastguard Worker         new_path_setting = realloc(path->setting,
262*b9df5ad1SAndroid Build Coastguard Worker                                    path->size * sizeof(struct mixer_setting));
263*b9df5ad1SAndroid Build Coastguard Worker         if (new_path_setting == NULL) {
264*b9df5ad1SAndroid Build Coastguard Worker             ALOGE("Unable to allocate more path settings");
265*b9df5ad1SAndroid Build Coastguard Worker             return -1;
266*b9df5ad1SAndroid Build Coastguard Worker         } else {
267*b9df5ad1SAndroid Build Coastguard Worker             path->setting = new_path_setting;
268*b9df5ad1SAndroid Build Coastguard Worker         }
269*b9df5ad1SAndroid Build Coastguard Worker     }
270*b9df5ad1SAndroid Build Coastguard Worker 
271*b9df5ad1SAndroid Build Coastguard Worker     path_index = path->length;
272*b9df5ad1SAndroid Build Coastguard Worker     path->length++;
273*b9df5ad1SAndroid Build Coastguard Worker 
274*b9df5ad1SAndroid Build Coastguard Worker     return path_index;
275*b9df5ad1SAndroid Build Coastguard Worker }
276*b9df5ad1SAndroid Build Coastguard Worker 
path_add_setting(struct audio_route * ar,struct mixer_path * path,struct mixer_setting * setting)277*b9df5ad1SAndroid Build Coastguard Worker static int path_add_setting(struct audio_route *ar, struct mixer_path *path,
278*b9df5ad1SAndroid Build Coastguard Worker                             struct mixer_setting *setting)
279*b9df5ad1SAndroid Build Coastguard Worker {
280*b9df5ad1SAndroid Build Coastguard Worker     int path_index;
281*b9df5ad1SAndroid Build Coastguard Worker 
282*b9df5ad1SAndroid Build Coastguard Worker     if (find_ctl_index_in_path(path, setting->ctl_index) != -1) {
283*b9df5ad1SAndroid Build Coastguard Worker         struct mixer_ctl *ctl = index_to_ctl(ar, setting->ctl_index);
284*b9df5ad1SAndroid Build Coastguard Worker 
285*b9df5ad1SAndroid Build Coastguard Worker         ALOGW("Control '%s' already exists in path '%s' - Ignore one in the new sub path",
286*b9df5ad1SAndroid Build Coastguard Worker               mixer_ctl_get_name(ctl), path->name);
287*b9df5ad1SAndroid Build Coastguard Worker         return -2;
288*b9df5ad1SAndroid Build Coastguard Worker     }
289*b9df5ad1SAndroid Build Coastguard Worker 
290*b9df5ad1SAndroid Build Coastguard Worker     if (!is_supported_ctl_type(setting->type)) {
291*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("unsupported type %d", (int)setting->type);
292*b9df5ad1SAndroid Build Coastguard Worker         return -1;
293*b9df5ad1SAndroid Build Coastguard Worker     }
294*b9df5ad1SAndroid Build Coastguard Worker 
295*b9df5ad1SAndroid Build Coastguard Worker     path_index = alloc_path_setting(path);
296*b9df5ad1SAndroid Build Coastguard Worker     if (path_index < 0)
297*b9df5ad1SAndroid Build Coastguard Worker         return -1;
298*b9df5ad1SAndroid Build Coastguard Worker 
299*b9df5ad1SAndroid Build Coastguard Worker     path->setting[path_index].ctl_index = setting->ctl_index;
300*b9df5ad1SAndroid Build Coastguard Worker     path->setting[path_index].type = setting->type;
301*b9df5ad1SAndroid Build Coastguard Worker     path->setting[path_index].num_values = setting->num_values;
302*b9df5ad1SAndroid Build Coastguard Worker 
303*b9df5ad1SAndroid Build Coastguard Worker     size_t value_sz = sizeof_ctl_type(setting->type);
304*b9df5ad1SAndroid Build Coastguard Worker 
305*b9df5ad1SAndroid Build Coastguard Worker     path->setting[path_index].value.ptr = calloc(setting->num_values, value_sz);
306*b9df5ad1SAndroid Build Coastguard Worker     /* copy all values */
307*b9df5ad1SAndroid Build Coastguard Worker     memcpy(path->setting[path_index].value.ptr, setting->value.ptr,
308*b9df5ad1SAndroid Build Coastguard Worker            setting->num_values * value_sz);
309*b9df5ad1SAndroid Build Coastguard Worker 
310*b9df5ad1SAndroid Build Coastguard Worker     return 0;
311*b9df5ad1SAndroid Build Coastguard Worker }
312*b9df5ad1SAndroid Build Coastguard Worker 
path_add_value(struct audio_route * ar,struct mixer_path * path,struct mixer_value * mixer_value)313*b9df5ad1SAndroid Build Coastguard Worker static int path_add_value(struct audio_route *ar, struct mixer_path *path,
314*b9df5ad1SAndroid Build Coastguard Worker                           struct mixer_value *mixer_value)
315*b9df5ad1SAndroid Build Coastguard Worker {
316*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
317*b9df5ad1SAndroid Build Coastguard Worker     int path_index;
318*b9df5ad1SAndroid Build Coastguard Worker     unsigned int num_values;
319*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_ctl *ctl;
320*b9df5ad1SAndroid Build Coastguard Worker 
321*b9df5ad1SAndroid Build Coastguard Worker     /* Check that mixer value index is within range */
322*b9df5ad1SAndroid Build Coastguard Worker     ctl = index_to_ctl(ar, mixer_value->ctl_index);
323*b9df5ad1SAndroid Build Coastguard Worker     num_values = mixer_ctl_get_num_values(ctl);
324*b9df5ad1SAndroid Build Coastguard Worker     if (mixer_value->index >= (int)num_values) {
325*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("mixer index %d is out of range for '%s'", mixer_value->index,
326*b9df5ad1SAndroid Build Coastguard Worker               mixer_ctl_get_name(ctl));
327*b9df5ad1SAndroid Build Coastguard Worker         return -1;
328*b9df5ad1SAndroid Build Coastguard Worker     }
329*b9df5ad1SAndroid Build Coastguard Worker 
330*b9df5ad1SAndroid Build Coastguard Worker     path_index = find_ctl_index_in_path(path, mixer_value->ctl_index);
331*b9df5ad1SAndroid Build Coastguard Worker     if (path_index < 0) {
332*b9df5ad1SAndroid Build Coastguard Worker         /* New path */
333*b9df5ad1SAndroid Build Coastguard Worker 
334*b9df5ad1SAndroid Build Coastguard Worker         enum mixer_ctl_type type = mixer_ctl_get_type(ctl);
335*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type)) {
336*b9df5ad1SAndroid Build Coastguard Worker             ALOGE("unsupported type %d", (int)type);
337*b9df5ad1SAndroid Build Coastguard Worker             return -1;
338*b9df5ad1SAndroid Build Coastguard Worker         }
339*b9df5ad1SAndroid Build Coastguard Worker         path_index = alloc_path_setting(path);
340*b9df5ad1SAndroid Build Coastguard Worker         if (path_index < 0)
341*b9df5ad1SAndroid Build Coastguard Worker             return -1;
342*b9df5ad1SAndroid Build Coastguard Worker 
343*b9df5ad1SAndroid Build Coastguard Worker         /* initialise the new path setting */
344*b9df5ad1SAndroid Build Coastguard Worker         path->setting[path_index].ctl_index = mixer_value->ctl_index;
345*b9df5ad1SAndroid Build Coastguard Worker         path->setting[path_index].num_values = num_values;
346*b9df5ad1SAndroid Build Coastguard Worker         path->setting[path_index].type = type;
347*b9df5ad1SAndroid Build Coastguard Worker 
348*b9df5ad1SAndroid Build Coastguard Worker         size_t value_sz = sizeof_ctl_type(type);
349*b9df5ad1SAndroid Build Coastguard Worker         path->setting[path_index].value.ptr = calloc(num_values, value_sz);
350*b9df5ad1SAndroid Build Coastguard Worker         if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE)
351*b9df5ad1SAndroid Build Coastguard Worker             path->setting[path_index].value.bytes[0] = mixer_value->value;
352*b9df5ad1SAndroid Build Coastguard Worker         else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM)
353*b9df5ad1SAndroid Build Coastguard Worker             path->setting[path_index].value.enumerated[0] = mixer_value->value;
354*b9df5ad1SAndroid Build Coastguard Worker         else
355*b9df5ad1SAndroid Build Coastguard Worker             path->setting[path_index].value.integer[0] = mixer_value->value;
356*b9df5ad1SAndroid Build Coastguard Worker     }
357*b9df5ad1SAndroid Build Coastguard Worker 
358*b9df5ad1SAndroid Build Coastguard Worker     if (mixer_value->index == -1) {
359*b9df5ad1SAndroid Build Coastguard Worker         /* set all values the same except for CTL_TYPE_BYTE and CTL_TYPE_INT */
360*b9df5ad1SAndroid Build Coastguard Worker         if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE) {
361*b9df5ad1SAndroid Build Coastguard Worker             for (i = 0; i < num_values; i++)
362*b9df5ad1SAndroid Build Coastguard Worker                 path->setting[path_index].value.bytes[i] = mixer_value->values[i];
363*b9df5ad1SAndroid Build Coastguard Worker         } else if (path->setting[path_index].type == MIXER_CTL_TYPE_INT) {
364*b9df5ad1SAndroid Build Coastguard Worker             for (i = 0; i < num_values; i++)
365*b9df5ad1SAndroid Build Coastguard Worker                 path->setting[path_index].value.integer[i] = mixer_value->values[i];
366*b9df5ad1SAndroid Build Coastguard Worker         } else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM) {
367*b9df5ad1SAndroid Build Coastguard Worker             for (i = 0; i < num_values; i++)
368*b9df5ad1SAndroid Build Coastguard Worker                 path->setting[path_index].value.enumerated[i] = mixer_value->value;
369*b9df5ad1SAndroid Build Coastguard Worker         } else {
370*b9df5ad1SAndroid Build Coastguard Worker             for (i = 0; i < num_values; i++)
371*b9df5ad1SAndroid Build Coastguard Worker                 path->setting[path_index].value.integer[i] = mixer_value->value;
372*b9df5ad1SAndroid Build Coastguard Worker         }
373*b9df5ad1SAndroid Build Coastguard Worker     } else {
374*b9df5ad1SAndroid Build Coastguard Worker         /* set only one value */
375*b9df5ad1SAndroid Build Coastguard Worker         if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE)
376*b9df5ad1SAndroid Build Coastguard Worker             path->setting[path_index].value.bytes[mixer_value->index] = mixer_value->value;
377*b9df5ad1SAndroid Build Coastguard Worker         else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM)
378*b9df5ad1SAndroid Build Coastguard Worker             path->setting[path_index].value.enumerated[mixer_value->index] = mixer_value->value;
379*b9df5ad1SAndroid Build Coastguard Worker         else
380*b9df5ad1SAndroid Build Coastguard Worker             path->setting[path_index].value.integer[mixer_value->index] = mixer_value->value;
381*b9df5ad1SAndroid Build Coastguard Worker     }
382*b9df5ad1SAndroid Build Coastguard Worker 
383*b9df5ad1SAndroid Build Coastguard Worker     return 0;
384*b9df5ad1SAndroid Build Coastguard Worker }
385*b9df5ad1SAndroid Build Coastguard Worker 
path_add_path(struct audio_route * ar,struct mixer_path * path,struct mixer_path * sub_path)386*b9df5ad1SAndroid Build Coastguard Worker static int path_add_path(struct audio_route *ar, struct mixer_path *path,
387*b9df5ad1SAndroid Build Coastguard Worker                          struct mixer_path *sub_path)
388*b9df5ad1SAndroid Build Coastguard Worker {
389*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
390*b9df5ad1SAndroid Build Coastguard Worker 
391*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < sub_path->length; i++) {
392*b9df5ad1SAndroid Build Coastguard Worker         int retVal = path_add_setting(ar, path, &sub_path->setting[i]);
393*b9df5ad1SAndroid Build Coastguard Worker         if (retVal < 0) {
394*b9df5ad1SAndroid Build Coastguard Worker             if (retVal == -2)
395*b9df5ad1SAndroid Build Coastguard Worker                 continue;
396*b9df5ad1SAndroid Build Coastguard Worker             else
397*b9df5ad1SAndroid Build Coastguard Worker                 return -1;
398*b9df5ad1SAndroid Build Coastguard Worker         }
399*b9df5ad1SAndroid Build Coastguard Worker     }
400*b9df5ad1SAndroid Build Coastguard Worker     return 0;
401*b9df5ad1SAndroid Build Coastguard Worker }
402*b9df5ad1SAndroid Build Coastguard Worker 
path_apply(struct audio_route * ar,struct mixer_path * path)403*b9df5ad1SAndroid Build Coastguard Worker static int path_apply(struct audio_route *ar, struct mixer_path *path)
404*b9df5ad1SAndroid Build Coastguard Worker {
405*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
406*b9df5ad1SAndroid Build Coastguard Worker     unsigned int ctl_index;
407*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_ctl *ctl;
408*b9df5ad1SAndroid Build Coastguard Worker     enum mixer_ctl_type type;
409*b9df5ad1SAndroid Build Coastguard Worker 
410*b9df5ad1SAndroid Build Coastguard Worker     ALOGD("Apply path: %s", path->name != NULL ? path->name : "none");
411*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < path->length; i++) {
412*b9df5ad1SAndroid Build Coastguard Worker         ctl_index = path->setting[i].ctl_index;
413*b9df5ad1SAndroid Build Coastguard Worker         ctl = index_to_ctl(ar, ctl_index);
414*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ctl);
415*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type))
416*b9df5ad1SAndroid Build Coastguard Worker             continue;
417*b9df5ad1SAndroid Build Coastguard Worker         size_t value_sz = sizeof_ctl_type(type);
418*b9df5ad1SAndroid Build Coastguard Worker         memcpy(ar->mixer_state[ctl_index].new_value.ptr, path->setting[i].value.ptr,
419*b9df5ad1SAndroid Build Coastguard Worker                    path->setting[i].num_values * value_sz);
420*b9df5ad1SAndroid Build Coastguard Worker     }
421*b9df5ad1SAndroid Build Coastguard Worker 
422*b9df5ad1SAndroid Build Coastguard Worker     return 0;
423*b9df5ad1SAndroid Build Coastguard Worker }
424*b9df5ad1SAndroid Build Coastguard Worker 
path_reset(struct audio_route * ar,struct mixer_path * path)425*b9df5ad1SAndroid Build Coastguard Worker static int path_reset(struct audio_route *ar, struct mixer_path *path)
426*b9df5ad1SAndroid Build Coastguard Worker {
427*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
428*b9df5ad1SAndroid Build Coastguard Worker     unsigned int ctl_index;
429*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_ctl *ctl;
430*b9df5ad1SAndroid Build Coastguard Worker     enum mixer_ctl_type type;
431*b9df5ad1SAndroid Build Coastguard Worker 
432*b9df5ad1SAndroid Build Coastguard Worker     ALOGV("Reset path: %s", path->name != NULL ? path->name : "none");
433*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < path->length; i++) {
434*b9df5ad1SAndroid Build Coastguard Worker         ctl_index = path->setting[i].ctl_index;
435*b9df5ad1SAndroid Build Coastguard Worker         ctl = index_to_ctl(ar, ctl_index);
436*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ctl);
437*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type))
438*b9df5ad1SAndroid Build Coastguard Worker             continue;
439*b9df5ad1SAndroid Build Coastguard Worker         size_t value_sz = sizeof_ctl_type(type);
440*b9df5ad1SAndroid Build Coastguard Worker         /* reset the value(s) */
441*b9df5ad1SAndroid Build Coastguard Worker         memcpy(ar->mixer_state[ctl_index].new_value.ptr,
442*b9df5ad1SAndroid Build Coastguard Worker                ar->mixer_state[ctl_index].reset_value.ptr,
443*b9df5ad1SAndroid Build Coastguard Worker                ar->mixer_state[ctl_index].num_values * value_sz);
444*b9df5ad1SAndroid Build Coastguard Worker     }
445*b9df5ad1SAndroid Build Coastguard Worker 
446*b9df5ad1SAndroid Build Coastguard Worker     return 0;
447*b9df5ad1SAndroid Build Coastguard Worker }
448*b9df5ad1SAndroid Build Coastguard Worker 
safe_strtol(const char * str,long * val)449*b9df5ad1SAndroid Build Coastguard Worker static bool safe_strtol(const char *str, long *val)
450*b9df5ad1SAndroid Build Coastguard Worker {
451*b9df5ad1SAndroid Build Coastguard Worker     char *end;
452*b9df5ad1SAndroid Build Coastguard Worker     long v;
453*b9df5ad1SAndroid Build Coastguard Worker     if (str == NULL || strlen(str) == 0)
454*b9df5ad1SAndroid Build Coastguard Worker         return false;
455*b9df5ad1SAndroid Build Coastguard Worker     errno = 0;
456*b9df5ad1SAndroid Build Coastguard Worker     v = strtol(str, &end, 0);
457*b9df5ad1SAndroid Build Coastguard Worker     if (errno || *end)
458*b9df5ad1SAndroid Build Coastguard Worker         return false;
459*b9df5ad1SAndroid Build Coastguard Worker     *val = v;
460*b9df5ad1SAndroid Build Coastguard Worker     return true;
461*b9df5ad1SAndroid Build Coastguard Worker }
462*b9df5ad1SAndroid Build Coastguard Worker 
463*b9df5ad1SAndroid Build Coastguard Worker /* mixer helper function */
mixer_enum_string_to_value(struct mixer_ctl * ctl,const char * string,bool allow_numeric_fallback)464*b9df5ad1SAndroid Build Coastguard Worker static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string,
465*b9df5ad1SAndroid Build Coastguard Worker                                       bool allow_numeric_fallback)
466*b9df5ad1SAndroid Build Coastguard Worker {
467*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
468*b9df5ad1SAndroid Build Coastguard Worker     unsigned int num_values = mixer_ctl_get_num_enums(ctl);
469*b9df5ad1SAndroid Build Coastguard Worker 
470*b9df5ad1SAndroid Build Coastguard Worker     if (string == NULL) {
471*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("NULL enum value string passed to mixer_enum_string_to_value() for ctl %s",
472*b9df5ad1SAndroid Build Coastguard Worker               mixer_ctl_get_name(ctl));
473*b9df5ad1SAndroid Build Coastguard Worker         return 0;
474*b9df5ad1SAndroid Build Coastguard Worker     }
475*b9df5ad1SAndroid Build Coastguard Worker 
476*b9df5ad1SAndroid Build Coastguard Worker     /* Search the enum strings for a particular one */
477*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < num_values; i++) {
478*b9df5ad1SAndroid Build Coastguard Worker         if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
479*b9df5ad1SAndroid Build Coastguard Worker             break;
480*b9df5ad1SAndroid Build Coastguard Worker     }
481*b9df5ad1SAndroid Build Coastguard Worker     if (i == num_values) {
482*b9df5ad1SAndroid Build Coastguard Worker         /* No enum string match. Check the flag before numeric parsing. */
483*b9df5ad1SAndroid Build Coastguard Worker         if (allow_numeric_fallback) {
484*b9df5ad1SAndroid Build Coastguard Worker             long value = 0;
485*b9df5ad1SAndroid Build Coastguard Worker             if (safe_strtol(string, &value) && value >= 0 && value < num_values) {
486*b9df5ad1SAndroid Build Coastguard Worker                 return value;
487*b9df5ad1SAndroid Build Coastguard Worker             }
488*b9df5ad1SAndroid Build Coastguard Worker         }
489*b9df5ad1SAndroid Build Coastguard Worker         ALOGW("unknown enum value string %s for ctl %s",
490*b9df5ad1SAndroid Build Coastguard Worker               string, mixer_ctl_get_name(ctl));
491*b9df5ad1SAndroid Build Coastguard Worker         return 0;
492*b9df5ad1SAndroid Build Coastguard Worker     }
493*b9df5ad1SAndroid Build Coastguard Worker     return i;
494*b9df5ad1SAndroid Build Coastguard Worker }
495*b9df5ad1SAndroid Build Coastguard Worker 
start_tag(void * data,const XML_Char * tag_name,const XML_Char ** attr)496*b9df5ad1SAndroid Build Coastguard Worker static void start_tag(void *data, const XML_Char *tag_name,
497*b9df5ad1SAndroid Build Coastguard Worker                       const XML_Char **attr)
498*b9df5ad1SAndroid Build Coastguard Worker {
499*b9df5ad1SAndroid Build Coastguard Worker     const XML_Char *attr_name = NULL;
500*b9df5ad1SAndroid Build Coastguard Worker     const XML_Char *attr_id = NULL;
501*b9df5ad1SAndroid Build Coastguard Worker     const XML_Char *attr_value = NULL;
502*b9df5ad1SAndroid Build Coastguard Worker     const XML_Char *attr_enum_mixer_numeric_fallback = NULL;
503*b9df5ad1SAndroid Build Coastguard Worker     struct config_parse_state *state = data;
504*b9df5ad1SAndroid Build Coastguard Worker     struct audio_route *ar = state->ar;
505*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
506*b9df5ad1SAndroid Build Coastguard Worker     unsigned int ctl_index;
507*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_ctl *ctl;
508*b9df5ad1SAndroid Build Coastguard Worker     long value;
509*b9df5ad1SAndroid Build Coastguard Worker     unsigned int id;
510*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_value mixer_value;
511*b9df5ad1SAndroid Build Coastguard Worker     enum mixer_ctl_type type;
512*b9df5ad1SAndroid Build Coastguard Worker     long* value_array = NULL;
513*b9df5ad1SAndroid Build Coastguard Worker 
514*b9df5ad1SAndroid Build Coastguard Worker     /* Get name, id and value attributes (these may be empty) */
515*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; attr[i]; i += 2) {
516*b9df5ad1SAndroid Build Coastguard Worker         if (strcmp(attr[i], "name") == 0)
517*b9df5ad1SAndroid Build Coastguard Worker             attr_name = attr[i + 1];
518*b9df5ad1SAndroid Build Coastguard Worker         else if (strcmp(attr[i], "id") == 0)
519*b9df5ad1SAndroid Build Coastguard Worker             attr_id = attr[i + 1];
520*b9df5ad1SAndroid Build Coastguard Worker         else if (strcmp(attr[i], "value") == 0)
521*b9df5ad1SAndroid Build Coastguard Worker             attr_value = attr[i + 1];
522*b9df5ad1SAndroid Build Coastguard Worker         else if (strcmp(attr[i], "enum_mixer_numeric_fallback") == 0)
523*b9df5ad1SAndroid Build Coastguard Worker             attr_enum_mixer_numeric_fallback = attr[i + 1];
524*b9df5ad1SAndroid Build Coastguard Worker     }
525*b9df5ad1SAndroid Build Coastguard Worker 
526*b9df5ad1SAndroid Build Coastguard Worker     /* Look at tags */
527*b9df5ad1SAndroid Build Coastguard Worker     if (strcmp(tag_name, "mixer") == 0) {
528*b9df5ad1SAndroid Build Coastguard Worker         state->enum_mixer_numeric_fallback =
529*b9df5ad1SAndroid Build Coastguard Worker                 attr_enum_mixer_numeric_fallback != NULL &&
530*b9df5ad1SAndroid Build Coastguard Worker                 strcmp(attr_enum_mixer_numeric_fallback, "true") == 0 ;
531*b9df5ad1SAndroid Build Coastguard Worker     } else if (strcmp(tag_name, "path") == 0) {
532*b9df5ad1SAndroid Build Coastguard Worker         if (attr_name == NULL) {
533*b9df5ad1SAndroid Build Coastguard Worker             ALOGE("Unnamed path!");
534*b9df5ad1SAndroid Build Coastguard Worker         } else {
535*b9df5ad1SAndroid Build Coastguard Worker             if (state->level == 1) {
536*b9df5ad1SAndroid Build Coastguard Worker                 /* top level path: create and stash the path */
537*b9df5ad1SAndroid Build Coastguard Worker                 state->path = path_create(ar, (char *)attr_name);
538*b9df5ad1SAndroid Build Coastguard Worker                 if (state->path == NULL)
539*b9df5ad1SAndroid Build Coastguard Worker                     ALOGW("path creation failed, please check if the path exists");
540*b9df5ad1SAndroid Build Coastguard Worker             } else {
541*b9df5ad1SAndroid Build Coastguard Worker                 /* nested path */
542*b9df5ad1SAndroid Build Coastguard Worker                 struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
543*b9df5ad1SAndroid Build Coastguard Worker                 if (!sub_path) {
544*b9df5ad1SAndroid Build Coastguard Worker                     ALOGW("unable to find sub path '%s'", attr_name);
545*b9df5ad1SAndroid Build Coastguard Worker                 } else if (state->path != NULL) {
546*b9df5ad1SAndroid Build Coastguard Worker                     path_add_path(ar, state->path, sub_path);
547*b9df5ad1SAndroid Build Coastguard Worker                 }
548*b9df5ad1SAndroid Build Coastguard Worker             }
549*b9df5ad1SAndroid Build Coastguard Worker         }
550*b9df5ad1SAndroid Build Coastguard Worker     } else if (strcmp(tag_name, "ctl") == 0) {
551*b9df5ad1SAndroid Build Coastguard Worker         /* Obtain the mixer ctl and value */
552*b9df5ad1SAndroid Build Coastguard Worker         ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
553*b9df5ad1SAndroid Build Coastguard Worker         if (ctl == NULL) {
554*b9df5ad1SAndroid Build Coastguard Worker             ALOGW("Control '%s' doesn't exist - skipping", attr_name);
555*b9df5ad1SAndroid Build Coastguard Worker             goto done;
556*b9df5ad1SAndroid Build Coastguard Worker         }
557*b9df5ad1SAndroid Build Coastguard Worker 
558*b9df5ad1SAndroid Build Coastguard Worker         switch (mixer_ctl_get_type(ctl)) {
559*b9df5ad1SAndroid Build Coastguard Worker         case MIXER_CTL_TYPE_BOOL:
560*b9df5ad1SAndroid Build Coastguard Worker             if (attr_value == NULL) {
561*b9df5ad1SAndroid Build Coastguard Worker                 ALOGE("No value specified for ctl %s", attr_name);
562*b9df5ad1SAndroid Build Coastguard Worker                 goto done;
563*b9df5ad1SAndroid Build Coastguard Worker             }
564*b9df5ad1SAndroid Build Coastguard Worker             value = strtol((char *)attr_value, NULL, 0);
565*b9df5ad1SAndroid Build Coastguard Worker             break;
566*b9df5ad1SAndroid Build Coastguard Worker         case MIXER_CTL_TYPE_INT:
567*b9df5ad1SAndroid Build Coastguard Worker         case MIXER_CTL_TYPE_BYTE: {
568*b9df5ad1SAndroid Build Coastguard Worker                 char *attr_sub_value, *test_r;
569*b9df5ad1SAndroid Build Coastguard Worker 
570*b9df5ad1SAndroid Build Coastguard Worker                 if (attr_value == NULL) {
571*b9df5ad1SAndroid Build Coastguard Worker                     ALOGE("No value specified for ctl %s", attr_name);
572*b9df5ad1SAndroid Build Coastguard Worker                     goto done;
573*b9df5ad1SAndroid Build Coastguard Worker                 }
574*b9df5ad1SAndroid Build Coastguard Worker                 unsigned int num_values = mixer_ctl_get_num_values(ctl);
575*b9df5ad1SAndroid Build Coastguard Worker                 value_array = calloc(num_values, sizeof(long));
576*b9df5ad1SAndroid Build Coastguard Worker                 if (!value_array) {
577*b9df5ad1SAndroid Build Coastguard Worker                     ALOGE("failed to allocate mem for ctl %s", attr_name);
578*b9df5ad1SAndroid Build Coastguard Worker                     goto done;
579*b9df5ad1SAndroid Build Coastguard Worker                 }
580*b9df5ad1SAndroid Build Coastguard Worker                 for (i = 0; i < num_values; i++) {
581*b9df5ad1SAndroid Build Coastguard Worker                     attr_sub_value = strtok_r((char *)attr_value, " ", &test_r);
582*b9df5ad1SAndroid Build Coastguard Worker                     if (attr_sub_value == NULL) {
583*b9df5ad1SAndroid Build Coastguard Worker                         ALOGE("expect %d values but only %d specified for ctl %s",
584*b9df5ad1SAndroid Build Coastguard Worker                             num_values, i, attr_name);
585*b9df5ad1SAndroid Build Coastguard Worker                         goto done;
586*b9df5ad1SAndroid Build Coastguard Worker                     }
587*b9df5ad1SAndroid Build Coastguard Worker                     if (mixer_ctl_get_type(ctl) == MIXER_CTL_TYPE_INT)
588*b9df5ad1SAndroid Build Coastguard Worker                         value_array[i] = strtol((char *)attr_sub_value, NULL, 0);
589*b9df5ad1SAndroid Build Coastguard Worker                     else
590*b9df5ad1SAndroid Build Coastguard Worker                         value_array[i] =
591*b9df5ad1SAndroid Build Coastguard Worker                            (unsigned char) strtol((char *)attr_sub_value, NULL, 16);
592*b9df5ad1SAndroid Build Coastguard Worker 
593*b9df5ad1SAndroid Build Coastguard Worker                     if (attr_id)
594*b9df5ad1SAndroid Build Coastguard Worker                         break;
595*b9df5ad1SAndroid Build Coastguard Worker 
596*b9df5ad1SAndroid Build Coastguard Worker                     attr_value = NULL;
597*b9df5ad1SAndroid Build Coastguard Worker                 }
598*b9df5ad1SAndroid Build Coastguard Worker             } break;
599*b9df5ad1SAndroid Build Coastguard Worker         case MIXER_CTL_TYPE_ENUM:
600*b9df5ad1SAndroid Build Coastguard Worker             if (attr_value == NULL) {
601*b9df5ad1SAndroid Build Coastguard Worker                 ALOGE("No value specified for ctl %s", attr_name);
602*b9df5ad1SAndroid Build Coastguard Worker                 goto done;
603*b9df5ad1SAndroid Build Coastguard Worker             }
604*b9df5ad1SAndroid Build Coastguard Worker             value = mixer_enum_string_to_value(ctl, (char *)attr_value,
605*b9df5ad1SAndroid Build Coastguard Worker                                                state->enum_mixer_numeric_fallback);
606*b9df5ad1SAndroid Build Coastguard Worker             break;
607*b9df5ad1SAndroid Build Coastguard Worker         default:
608*b9df5ad1SAndroid Build Coastguard Worker             value = 0;
609*b9df5ad1SAndroid Build Coastguard Worker             break;
610*b9df5ad1SAndroid Build Coastguard Worker         }
611*b9df5ad1SAndroid Build Coastguard Worker 
612*b9df5ad1SAndroid Build Coastguard Worker         /* locate the mixer ctl in the list */
613*b9df5ad1SAndroid Build Coastguard Worker         for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
614*b9df5ad1SAndroid Build Coastguard Worker             if (ar->mixer_state[ctl_index].ctl == ctl)
615*b9df5ad1SAndroid Build Coastguard Worker                 break;
616*b9df5ad1SAndroid Build Coastguard Worker         }
617*b9df5ad1SAndroid Build Coastguard Worker 
618*b9df5ad1SAndroid Build Coastguard Worker         if (state->level == 1) {
619*b9df5ad1SAndroid Build Coastguard Worker             /* top level ctl (initial setting) */
620*b9df5ad1SAndroid Build Coastguard Worker 
621*b9df5ad1SAndroid Build Coastguard Worker             type = mixer_ctl_get_type(ctl);
622*b9df5ad1SAndroid Build Coastguard Worker             if (is_supported_ctl_type(type)) {
623*b9df5ad1SAndroid Build Coastguard Worker                 /* apply the new value */
624*b9df5ad1SAndroid Build Coastguard Worker                 if (attr_id) {
625*b9df5ad1SAndroid Build Coastguard Worker                     /* set only one value */
626*b9df5ad1SAndroid Build Coastguard Worker                     id = atoi((char *)attr_id);
627*b9df5ad1SAndroid Build Coastguard Worker                     if (id < ar->mixer_state[ctl_index].num_values)
628*b9df5ad1SAndroid Build Coastguard Worker                         if (type == MIXER_CTL_TYPE_BYTE)
629*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.bytes[id] = value_array[0];
630*b9df5ad1SAndroid Build Coastguard Worker                         else if (type == MIXER_CTL_TYPE_INT)
631*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.integer[id] = value_array[0];
632*b9df5ad1SAndroid Build Coastguard Worker                         else if (type == MIXER_CTL_TYPE_ENUM)
633*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.enumerated[id] = value;
634*b9df5ad1SAndroid Build Coastguard Worker                         else
635*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.integer[id] = value;
636*b9df5ad1SAndroid Build Coastguard Worker                     else
637*b9df5ad1SAndroid Build Coastguard Worker                         ALOGW("value id out of range for mixer ctl '%s'",
638*b9df5ad1SAndroid Build Coastguard Worker                               mixer_ctl_get_name(ctl));
639*b9df5ad1SAndroid Build Coastguard Worker                 } else {
640*b9df5ad1SAndroid Build Coastguard Worker                     /* set all values the same except for CTL_TYPE_BYTE and CTL_TYPE_INT */
641*b9df5ad1SAndroid Build Coastguard Worker                     for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
642*b9df5ad1SAndroid Build Coastguard Worker                         if (type == MIXER_CTL_TYPE_BYTE)
643*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.bytes[i] = value_array[i];
644*b9df5ad1SAndroid Build Coastguard Worker                         else if (type == MIXER_CTL_TYPE_INT)
645*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.integer[i] = value_array[i];
646*b9df5ad1SAndroid Build Coastguard Worker                         else if (type == MIXER_CTL_TYPE_ENUM)
647*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.enumerated[i] = value;
648*b9df5ad1SAndroid Build Coastguard Worker                         else
649*b9df5ad1SAndroid Build Coastguard Worker                             ar->mixer_state[ctl_index].new_value.integer[i] = value;
650*b9df5ad1SAndroid Build Coastguard Worker                 }
651*b9df5ad1SAndroid Build Coastguard Worker             }
652*b9df5ad1SAndroid Build Coastguard Worker         } else {
653*b9df5ad1SAndroid Build Coastguard Worker             /* nested ctl (within a path) */
654*b9df5ad1SAndroid Build Coastguard Worker             mixer_value.ctl_index = ctl_index;
655*b9df5ad1SAndroid Build Coastguard Worker             if (mixer_ctl_get_type(ctl) == MIXER_CTL_TYPE_BYTE ||
656*b9df5ad1SAndroid Build Coastguard Worker                 mixer_ctl_get_type(ctl) == MIXER_CTL_TYPE_INT) {
657*b9df5ad1SAndroid Build Coastguard Worker                 mixer_value.values = value_array;
658*b9df5ad1SAndroid Build Coastguard Worker                 mixer_value.value = value_array[0];
659*b9df5ad1SAndroid Build Coastguard Worker             } else {
660*b9df5ad1SAndroid Build Coastguard Worker                 mixer_value.value = value;
661*b9df5ad1SAndroid Build Coastguard Worker             }
662*b9df5ad1SAndroid Build Coastguard Worker             if (attr_id)
663*b9df5ad1SAndroid Build Coastguard Worker                 mixer_value.index = atoi((char *)attr_id);
664*b9df5ad1SAndroid Build Coastguard Worker             else
665*b9df5ad1SAndroid Build Coastguard Worker                 mixer_value.index = -1;
666*b9df5ad1SAndroid Build Coastguard Worker             if (state->path != NULL)
667*b9df5ad1SAndroid Build Coastguard Worker                 path_add_value(ar, state->path, &mixer_value);
668*b9df5ad1SAndroid Build Coastguard Worker         }
669*b9df5ad1SAndroid Build Coastguard Worker     }
670*b9df5ad1SAndroid Build Coastguard Worker 
671*b9df5ad1SAndroid Build Coastguard Worker done:
672*b9df5ad1SAndroid Build Coastguard Worker     free(value_array);
673*b9df5ad1SAndroid Build Coastguard Worker     state->level++;
674*b9df5ad1SAndroid Build Coastguard Worker }
675*b9df5ad1SAndroid Build Coastguard Worker 
end_tag(void * data,const XML_Char * tag_name)676*b9df5ad1SAndroid Build Coastguard Worker static void end_tag(void *data, const XML_Char *tag_name)
677*b9df5ad1SAndroid Build Coastguard Worker {
678*b9df5ad1SAndroid Build Coastguard Worker     struct config_parse_state *state = data;
679*b9df5ad1SAndroid Build Coastguard Worker     (void)tag_name;
680*b9df5ad1SAndroid Build Coastguard Worker 
681*b9df5ad1SAndroid Build Coastguard Worker     state->level--;
682*b9df5ad1SAndroid Build Coastguard Worker }
683*b9df5ad1SAndroid Build Coastguard Worker 
alloc_mixer_state(struct audio_route * ar)684*b9df5ad1SAndroid Build Coastguard Worker static int alloc_mixer_state(struct audio_route *ar)
685*b9df5ad1SAndroid Build Coastguard Worker {
686*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
687*b9df5ad1SAndroid Build Coastguard Worker     unsigned int num_values;
688*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_ctl *ctl;
689*b9df5ad1SAndroid Build Coastguard Worker     enum mixer_ctl_type type;
690*b9df5ad1SAndroid Build Coastguard Worker 
691*b9df5ad1SAndroid Build Coastguard Worker     ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
692*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_state = calloc(ar->num_mixer_ctls, sizeof(struct mixer_state));
693*b9df5ad1SAndroid Build Coastguard Worker     if (!ar->mixer_state)
694*b9df5ad1SAndroid Build Coastguard Worker         return -1;
695*b9df5ad1SAndroid Build Coastguard Worker 
696*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < ar->num_mixer_ctls; i++) {
697*b9df5ad1SAndroid Build Coastguard Worker         ctl = mixer_get_ctl(ar->mixer, i);
698*b9df5ad1SAndroid Build Coastguard Worker         num_values = mixer_ctl_get_num_values(ctl);
699*b9df5ad1SAndroid Build Coastguard Worker 
700*b9df5ad1SAndroid Build Coastguard Worker         ar->mixer_state[i].ctl = ctl;
701*b9df5ad1SAndroid Build Coastguard Worker         ar->mixer_state[i].num_values = num_values;
702*b9df5ad1SAndroid Build Coastguard Worker         ar->mixer_state[i].active_count = 0;
703*b9df5ad1SAndroid Build Coastguard Worker 
704*b9df5ad1SAndroid Build Coastguard Worker         /* Skip unsupported types that are not supported yet in XML */
705*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ctl);
706*b9df5ad1SAndroid Build Coastguard Worker 
707*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type))
708*b9df5ad1SAndroid Build Coastguard Worker             continue;
709*b9df5ad1SAndroid Build Coastguard Worker 
710*b9df5ad1SAndroid Build Coastguard Worker         size_t value_sz = sizeof_ctl_type(type);
711*b9df5ad1SAndroid Build Coastguard Worker         ar->mixer_state[i].old_value.ptr = calloc(num_values, value_sz);
712*b9df5ad1SAndroid Build Coastguard Worker         ar->mixer_state[i].new_value.ptr = calloc(num_values, value_sz);
713*b9df5ad1SAndroid Build Coastguard Worker         ar->mixer_state[i].reset_value.ptr = calloc(num_values, value_sz);
714*b9df5ad1SAndroid Build Coastguard Worker 
715*b9df5ad1SAndroid Build Coastguard Worker         if (type == MIXER_CTL_TYPE_ENUM)
716*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_state[i].old_value.enumerated[0] = mixer_ctl_get_value(ctl, 0);
717*b9df5ad1SAndroid Build Coastguard Worker         else
718*b9df5ad1SAndroid Build Coastguard Worker             mixer_ctl_get_array(ctl, ar->mixer_state[i].old_value.ptr, num_values);
719*b9df5ad1SAndroid Build Coastguard Worker 
720*b9df5ad1SAndroid Build Coastguard Worker         memcpy(ar->mixer_state[i].new_value.ptr, ar->mixer_state[i].old_value.ptr,
721*b9df5ad1SAndroid Build Coastguard Worker                num_values * value_sz);
722*b9df5ad1SAndroid Build Coastguard Worker     }
723*b9df5ad1SAndroid Build Coastguard Worker 
724*b9df5ad1SAndroid Build Coastguard Worker     return 0;
725*b9df5ad1SAndroid Build Coastguard Worker }
726*b9df5ad1SAndroid Build Coastguard Worker 
free_mixer_state(struct audio_route * ar)727*b9df5ad1SAndroid Build Coastguard Worker static void free_mixer_state(struct audio_route *ar)
728*b9df5ad1SAndroid Build Coastguard Worker {
729*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
730*b9df5ad1SAndroid Build Coastguard Worker     enum mixer_ctl_type type;
731*b9df5ad1SAndroid Build Coastguard Worker 
732*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < ar->num_mixer_ctls; i++) {
733*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
734*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type))
735*b9df5ad1SAndroid Build Coastguard Worker             continue;
736*b9df5ad1SAndroid Build Coastguard Worker 
737*b9df5ad1SAndroid Build Coastguard Worker         free(ar->mixer_state[i].old_value.ptr);
738*b9df5ad1SAndroid Build Coastguard Worker         free(ar->mixer_state[i].new_value.ptr);
739*b9df5ad1SAndroid Build Coastguard Worker         free(ar->mixer_state[i].reset_value.ptr);
740*b9df5ad1SAndroid Build Coastguard Worker     }
741*b9df5ad1SAndroid Build Coastguard Worker 
742*b9df5ad1SAndroid Build Coastguard Worker     free(ar->mixer_state);
743*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_state = NULL;
744*b9df5ad1SAndroid Build Coastguard Worker }
745*b9df5ad1SAndroid Build Coastguard Worker 
746*b9df5ad1SAndroid Build Coastguard Worker /* Update the mixer with any changed values */
audio_route_update_mixer(struct audio_route * ar)747*b9df5ad1SAndroid Build Coastguard Worker int audio_route_update_mixer(struct audio_route *ar)
748*b9df5ad1SAndroid Build Coastguard Worker {
749*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
750*b9df5ad1SAndroid Build Coastguard Worker     unsigned int j;
751*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_ctl *ctl;
752*b9df5ad1SAndroid Build Coastguard Worker 
753*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < ar->num_mixer_ctls; i++) {
754*b9df5ad1SAndroid Build Coastguard Worker         unsigned int num_values = ar->mixer_state[i].num_values;
755*b9df5ad1SAndroid Build Coastguard Worker         enum mixer_ctl_type type;
756*b9df5ad1SAndroid Build Coastguard Worker 
757*b9df5ad1SAndroid Build Coastguard Worker         ctl = ar->mixer_state[i].ctl;
758*b9df5ad1SAndroid Build Coastguard Worker 
759*b9df5ad1SAndroid Build Coastguard Worker         /* Skip unsupported types */
760*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ctl);
761*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type))
762*b9df5ad1SAndroid Build Coastguard Worker             continue;
763*b9df5ad1SAndroid Build Coastguard Worker 
764*b9df5ad1SAndroid Build Coastguard Worker         /* if the value has changed, update the mixer */
765*b9df5ad1SAndroid Build Coastguard Worker         bool changed = false;
766*b9df5ad1SAndroid Build Coastguard Worker         if (type == MIXER_CTL_TYPE_BYTE) {
767*b9df5ad1SAndroid Build Coastguard Worker             for (j = 0; j < num_values; j++) {
768*b9df5ad1SAndroid Build Coastguard Worker                 if (ar->mixer_state[i].old_value.bytes[j] != ar->mixer_state[i].new_value.bytes[j]) {
769*b9df5ad1SAndroid Build Coastguard Worker                     changed = true;
770*b9df5ad1SAndroid Build Coastguard Worker                     break;
771*b9df5ad1SAndroid Build Coastguard Worker                 }
772*b9df5ad1SAndroid Build Coastguard Worker             }
773*b9df5ad1SAndroid Build Coastguard Worker          } else if (type == MIXER_CTL_TYPE_ENUM) {
774*b9df5ad1SAndroid Build Coastguard Worker              for (j = 0; j < num_values; j++) {
775*b9df5ad1SAndroid Build Coastguard Worker                  if (ar->mixer_state[i].old_value.enumerated[j]
776*b9df5ad1SAndroid Build Coastguard Worker                          != ar->mixer_state[i].new_value.enumerated[j]) {
777*b9df5ad1SAndroid Build Coastguard Worker                      changed = true;
778*b9df5ad1SAndroid Build Coastguard Worker                      break;
779*b9df5ad1SAndroid Build Coastguard Worker                  }
780*b9df5ad1SAndroid Build Coastguard Worker              }
781*b9df5ad1SAndroid Build Coastguard Worker          } else {
782*b9df5ad1SAndroid Build Coastguard Worker             for (j = 0; j < num_values; j++) {
783*b9df5ad1SAndroid Build Coastguard Worker                 if (ar->mixer_state[i].old_value.integer[j] != ar->mixer_state[i].new_value.integer[j]) {
784*b9df5ad1SAndroid Build Coastguard Worker                     changed = true;
785*b9df5ad1SAndroid Build Coastguard Worker                     break;
786*b9df5ad1SAndroid Build Coastguard Worker                 }
787*b9df5ad1SAndroid Build Coastguard Worker             }
788*b9df5ad1SAndroid Build Coastguard Worker         }
789*b9df5ad1SAndroid Build Coastguard Worker         if (changed) {
790*b9df5ad1SAndroid Build Coastguard Worker             if (type == MIXER_CTL_TYPE_ENUM)
791*b9df5ad1SAndroid Build Coastguard Worker                 mixer_ctl_set_value(ctl, 0, ar->mixer_state[i].new_value.enumerated[0]);
792*b9df5ad1SAndroid Build Coastguard Worker             else
793*b9df5ad1SAndroid Build Coastguard Worker                 mixer_ctl_set_array(ctl, ar->mixer_state[i].new_value.ptr, num_values);
794*b9df5ad1SAndroid Build Coastguard Worker 
795*b9df5ad1SAndroid Build Coastguard Worker             size_t value_sz = sizeof_ctl_type(type);
796*b9df5ad1SAndroid Build Coastguard Worker             memcpy(ar->mixer_state[i].old_value.ptr, ar->mixer_state[i].new_value.ptr,
797*b9df5ad1SAndroid Build Coastguard Worker                    num_values * value_sz);
798*b9df5ad1SAndroid Build Coastguard Worker         }
799*b9df5ad1SAndroid Build Coastguard Worker     }
800*b9df5ad1SAndroid Build Coastguard Worker 
801*b9df5ad1SAndroid Build Coastguard Worker     return 0;
802*b9df5ad1SAndroid Build Coastguard Worker }
803*b9df5ad1SAndroid Build Coastguard Worker 
804*b9df5ad1SAndroid Build Coastguard Worker /* saves the current state of the mixer, for resetting all controls */
save_mixer_state(struct audio_route * ar)805*b9df5ad1SAndroid Build Coastguard Worker static void save_mixer_state(struct audio_route *ar)
806*b9df5ad1SAndroid Build Coastguard Worker {
807*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
808*b9df5ad1SAndroid Build Coastguard Worker     enum mixer_ctl_type type;
809*b9df5ad1SAndroid Build Coastguard Worker 
810*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < ar->num_mixer_ctls; i++) {
811*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
812*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type))
813*b9df5ad1SAndroid Build Coastguard Worker             continue;
814*b9df5ad1SAndroid Build Coastguard Worker 
815*b9df5ad1SAndroid Build Coastguard Worker         size_t value_sz = sizeof_ctl_type(type);
816*b9df5ad1SAndroid Build Coastguard Worker         memcpy(ar->mixer_state[i].reset_value.ptr, ar->mixer_state[i].new_value.ptr,
817*b9df5ad1SAndroid Build Coastguard Worker                ar->mixer_state[i].num_values * value_sz);
818*b9df5ad1SAndroid Build Coastguard Worker     }
819*b9df5ad1SAndroid Build Coastguard Worker }
820*b9df5ad1SAndroid Build Coastguard Worker 
821*b9df5ad1SAndroid Build Coastguard Worker /* Reset the audio routes back to the initial state */
audio_route_reset(struct audio_route * ar)822*b9df5ad1SAndroid Build Coastguard Worker void audio_route_reset(struct audio_route *ar)
823*b9df5ad1SAndroid Build Coastguard Worker {
824*b9df5ad1SAndroid Build Coastguard Worker     unsigned int i;
825*b9df5ad1SAndroid Build Coastguard Worker     enum mixer_ctl_type type;
826*b9df5ad1SAndroid Build Coastguard Worker 
827*b9df5ad1SAndroid Build Coastguard Worker     /* load all of the saved values */
828*b9df5ad1SAndroid Build Coastguard Worker     for (i = 0; i < ar->num_mixer_ctls; i++) {
829*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
830*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type))
831*b9df5ad1SAndroid Build Coastguard Worker             continue;
832*b9df5ad1SAndroid Build Coastguard Worker 
833*b9df5ad1SAndroid Build Coastguard Worker         size_t value_sz = sizeof_ctl_type(type);
834*b9df5ad1SAndroid Build Coastguard Worker         memcpy(ar->mixer_state[i].new_value.ptr, ar->mixer_state[i].reset_value.ptr,
835*b9df5ad1SAndroid Build Coastguard Worker             ar->mixer_state[i].num_values * value_sz);
836*b9df5ad1SAndroid Build Coastguard Worker     }
837*b9df5ad1SAndroid Build Coastguard Worker }
838*b9df5ad1SAndroid Build Coastguard Worker 
839*b9df5ad1SAndroid Build Coastguard Worker /* Apply an audio route path by name */
audio_route_apply_path(struct audio_route * ar,const char * name)840*b9df5ad1SAndroid Build Coastguard Worker int audio_route_apply_path(struct audio_route *ar, const char *name)
841*b9df5ad1SAndroid Build Coastguard Worker {
842*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_path *path;
843*b9df5ad1SAndroid Build Coastguard Worker 
844*b9df5ad1SAndroid Build Coastguard Worker     if (!ar) {
845*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("invalid audio_route");
846*b9df5ad1SAndroid Build Coastguard Worker         return -1;
847*b9df5ad1SAndroid Build Coastguard Worker     }
848*b9df5ad1SAndroid Build Coastguard Worker 
849*b9df5ad1SAndroid Build Coastguard Worker     path = path_get_by_name(ar, name);
850*b9df5ad1SAndroid Build Coastguard Worker     if (!path) {
851*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("unable to find path '%s'", name);
852*b9df5ad1SAndroid Build Coastguard Worker         return -1;
853*b9df5ad1SAndroid Build Coastguard Worker     }
854*b9df5ad1SAndroid Build Coastguard Worker 
855*b9df5ad1SAndroid Build Coastguard Worker     path_apply(ar, path);
856*b9df5ad1SAndroid Build Coastguard Worker 
857*b9df5ad1SAndroid Build Coastguard Worker     return 0;
858*b9df5ad1SAndroid Build Coastguard Worker }
859*b9df5ad1SAndroid Build Coastguard Worker 
860*b9df5ad1SAndroid Build Coastguard Worker /* Reset an audio route path by name */
audio_route_reset_path(struct audio_route * ar,const char * name)861*b9df5ad1SAndroid Build Coastguard Worker int audio_route_reset_path(struct audio_route *ar, const char *name)
862*b9df5ad1SAndroid Build Coastguard Worker {
863*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_path *path;
864*b9df5ad1SAndroid Build Coastguard Worker 
865*b9df5ad1SAndroid Build Coastguard Worker     if (!ar) {
866*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("invalid audio_route");
867*b9df5ad1SAndroid Build Coastguard Worker         return -1;
868*b9df5ad1SAndroid Build Coastguard Worker     }
869*b9df5ad1SAndroid Build Coastguard Worker 
870*b9df5ad1SAndroid Build Coastguard Worker     path = path_get_by_name(ar, name);
871*b9df5ad1SAndroid Build Coastguard Worker     if (!path) {
872*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("unable to find path '%s'", name);
873*b9df5ad1SAndroid Build Coastguard Worker         return -1;
874*b9df5ad1SAndroid Build Coastguard Worker     }
875*b9df5ad1SAndroid Build Coastguard Worker 
876*b9df5ad1SAndroid Build Coastguard Worker     path_reset(ar, path);
877*b9df5ad1SAndroid Build Coastguard Worker 
878*b9df5ad1SAndroid Build Coastguard Worker     return 0;
879*b9df5ad1SAndroid Build Coastguard Worker }
880*b9df5ad1SAndroid Build Coastguard Worker 
881*b9df5ad1SAndroid Build Coastguard Worker /*
882*b9df5ad1SAndroid Build Coastguard Worker  * Operates on the specified path .. controls will be updated in the
883*b9df5ad1SAndroid Build Coastguard Worker  * order listed in the XML file
884*b9df5ad1SAndroid Build Coastguard Worker  */
audio_route_update_path(struct audio_route * ar,const char * name,int direction)885*b9df5ad1SAndroid Build Coastguard Worker static int audio_route_update_path(struct audio_route *ar, const char *name, int direction)
886*b9df5ad1SAndroid Build Coastguard Worker {
887*b9df5ad1SAndroid Build Coastguard Worker     struct mixer_path *path;
888*b9df5ad1SAndroid Build Coastguard Worker     unsigned int j;
889*b9df5ad1SAndroid Build Coastguard Worker     bool reverse = direction != DIRECTION_FORWARD;
890*b9df5ad1SAndroid Build Coastguard Worker     bool force_reset = direction == DIRECTION_REVERSE_RESET;
891*b9df5ad1SAndroid Build Coastguard Worker 
892*b9df5ad1SAndroid Build Coastguard Worker     if (!ar) {
893*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("invalid audio_route");
894*b9df5ad1SAndroid Build Coastguard Worker         return -1;
895*b9df5ad1SAndroid Build Coastguard Worker     }
896*b9df5ad1SAndroid Build Coastguard Worker 
897*b9df5ad1SAndroid Build Coastguard Worker     path = path_get_by_name(ar, name);
898*b9df5ad1SAndroid Build Coastguard Worker     if (!path) {
899*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("unable to find path '%s'", name);
900*b9df5ad1SAndroid Build Coastguard Worker         return -1;
901*b9df5ad1SAndroid Build Coastguard Worker     }
902*b9df5ad1SAndroid Build Coastguard Worker 
903*b9df5ad1SAndroid Build Coastguard Worker     for (size_t i = 0; i < path->length; ++i) {
904*b9df5ad1SAndroid Build Coastguard Worker         unsigned int ctl_index;
905*b9df5ad1SAndroid Build Coastguard Worker         enum mixer_ctl_type type;
906*b9df5ad1SAndroid Build Coastguard Worker 
907*b9df5ad1SAndroid Build Coastguard Worker         ctl_index = path->setting[reverse ? path->length - 1 - i : i].ctl_index;
908*b9df5ad1SAndroid Build Coastguard Worker 
909*b9df5ad1SAndroid Build Coastguard Worker         struct mixer_state * ms = &ar->mixer_state[ctl_index];
910*b9df5ad1SAndroid Build Coastguard Worker 
911*b9df5ad1SAndroid Build Coastguard Worker         type = mixer_ctl_get_type(ms->ctl);
912*b9df5ad1SAndroid Build Coastguard Worker         if (!is_supported_ctl_type(type)) {
913*b9df5ad1SAndroid Build Coastguard Worker             continue;
914*b9df5ad1SAndroid Build Coastguard Worker         }
915*b9df5ad1SAndroid Build Coastguard Worker 
916*b9df5ad1SAndroid Build Coastguard Worker         if (reverse && ms->active_count > 0) {
917*b9df5ad1SAndroid Build Coastguard Worker             if (force_reset)
918*b9df5ad1SAndroid Build Coastguard Worker                 ms->active_count = 0;
919*b9df5ad1SAndroid Build Coastguard Worker             else
920*b9df5ad1SAndroid Build Coastguard Worker                 ms->active_count--;
921*b9df5ad1SAndroid Build Coastguard Worker         } else if (!reverse) {
922*b9df5ad1SAndroid Build Coastguard Worker             ms->active_count++;
923*b9df5ad1SAndroid Build Coastguard Worker         }
924*b9df5ad1SAndroid Build Coastguard Worker 
925*b9df5ad1SAndroid Build Coastguard Worker        size_t value_sz = sizeof_ctl_type(type);
926*b9df5ad1SAndroid Build Coastguard Worker         /* if any value has changed, update the mixer */
927*b9df5ad1SAndroid Build Coastguard Worker         for (j = 0; j < ms->num_values; j++) {
928*b9df5ad1SAndroid Build Coastguard Worker             if (type == MIXER_CTL_TYPE_BYTE) {
929*b9df5ad1SAndroid Build Coastguard Worker                 if (ms->old_value.bytes[j] != ms->new_value.bytes[j]) {
930*b9df5ad1SAndroid Build Coastguard Worker                     if (reverse && ms->active_count > 0) {
931*b9df5ad1SAndroid Build Coastguard Worker                         ALOGD("%s: skip to reset mixer control '%s' in path '%s' "
932*b9df5ad1SAndroid Build Coastguard Worker                             "because it is still needed by other paths", __func__,
933*b9df5ad1SAndroid Build Coastguard Worker                             mixer_ctl_get_name(ms->ctl), name);
934*b9df5ad1SAndroid Build Coastguard Worker                         memcpy(ms->new_value.bytes, ms->old_value.bytes,
935*b9df5ad1SAndroid Build Coastguard Worker                             ms->num_values * value_sz);
936*b9df5ad1SAndroid Build Coastguard Worker                         break;
937*b9df5ad1SAndroid Build Coastguard Worker                     }
938*b9df5ad1SAndroid Build Coastguard Worker                     mixer_ctl_set_array(ms->ctl, ms->new_value.bytes, ms->num_values);
939*b9df5ad1SAndroid Build Coastguard Worker                     memcpy(ms->old_value.bytes, ms->new_value.bytes, ms->num_values * value_sz);
940*b9df5ad1SAndroid Build Coastguard Worker                     break;
941*b9df5ad1SAndroid Build Coastguard Worker                 }
942*b9df5ad1SAndroid Build Coastguard Worker             } else if (type == MIXER_CTL_TYPE_ENUM) {
943*b9df5ad1SAndroid Build Coastguard Worker                 if (ms->old_value.enumerated[j] != ms->new_value.enumerated[j]) {
944*b9df5ad1SAndroid Build Coastguard Worker                     if (reverse && ms->active_count > 0) {
945*b9df5ad1SAndroid Build Coastguard Worker                         ALOGD("%s: skip to reset mixer control '%s' in path '%s' "
946*b9df5ad1SAndroid Build Coastguard Worker                             "because it is still needed by other paths", __func__,
947*b9df5ad1SAndroid Build Coastguard Worker                             mixer_ctl_get_name(ms->ctl), name);
948*b9df5ad1SAndroid Build Coastguard Worker                         memcpy(ms->new_value.enumerated, ms->old_value.enumerated,
949*b9df5ad1SAndroid Build Coastguard Worker                             ms->num_values * value_sz);
950*b9df5ad1SAndroid Build Coastguard Worker                         break;
951*b9df5ad1SAndroid Build Coastguard Worker                     }
952*b9df5ad1SAndroid Build Coastguard Worker                     mixer_ctl_set_value(ms->ctl, 0, ms->new_value.enumerated[0]);
953*b9df5ad1SAndroid Build Coastguard Worker                     memcpy(ms->old_value.enumerated, ms->new_value.enumerated,
954*b9df5ad1SAndroid Build Coastguard Worker                             ms->num_values * value_sz);
955*b9df5ad1SAndroid Build Coastguard Worker                     break;
956*b9df5ad1SAndroid Build Coastguard Worker                 }
957*b9df5ad1SAndroid Build Coastguard Worker             } else if (ms->old_value.integer[j] != ms->new_value.integer[j]) {
958*b9df5ad1SAndroid Build Coastguard Worker                 if (reverse && ms->active_count > 0) {
959*b9df5ad1SAndroid Build Coastguard Worker                     ALOGD("%s: skip to reset mixer control '%s' in path '%s' "
960*b9df5ad1SAndroid Build Coastguard Worker                         "because it is still needed by other paths", __func__,
961*b9df5ad1SAndroid Build Coastguard Worker                         mixer_ctl_get_name(ms->ctl), name);
962*b9df5ad1SAndroid Build Coastguard Worker                     memcpy(ms->new_value.integer, ms->old_value.integer,
963*b9df5ad1SAndroid Build Coastguard Worker                         ms->num_values * value_sz);
964*b9df5ad1SAndroid Build Coastguard Worker                     break;
965*b9df5ad1SAndroid Build Coastguard Worker                 }
966*b9df5ad1SAndroid Build Coastguard Worker                 mixer_ctl_set_array(ms->ctl, ms->new_value.integer, ms->num_values);
967*b9df5ad1SAndroid Build Coastguard Worker                 memcpy(ms->old_value.integer, ms->new_value.integer, ms->num_values * value_sz);
968*b9df5ad1SAndroid Build Coastguard Worker                 break;
969*b9df5ad1SAndroid Build Coastguard Worker             }
970*b9df5ad1SAndroid Build Coastguard Worker         }
971*b9df5ad1SAndroid Build Coastguard Worker     }
972*b9df5ad1SAndroid Build Coastguard Worker     return 0;
973*b9df5ad1SAndroid Build Coastguard Worker }
974*b9df5ad1SAndroid Build Coastguard Worker 
audio_route_apply_and_update_path(struct audio_route * ar,const char * name)975*b9df5ad1SAndroid Build Coastguard Worker int audio_route_apply_and_update_path(struct audio_route *ar, const char *name)
976*b9df5ad1SAndroid Build Coastguard Worker {
977*b9df5ad1SAndroid Build Coastguard Worker     if (audio_route_apply_path(ar, name) < 0) {
978*b9df5ad1SAndroid Build Coastguard Worker         return -1;
979*b9df5ad1SAndroid Build Coastguard Worker     }
980*b9df5ad1SAndroid Build Coastguard Worker     return audio_route_update_path(ar, name, DIRECTION_FORWARD);
981*b9df5ad1SAndroid Build Coastguard Worker }
982*b9df5ad1SAndroid Build Coastguard Worker 
audio_route_reset_and_update_path(struct audio_route * ar,const char * name)983*b9df5ad1SAndroid Build Coastguard Worker int audio_route_reset_and_update_path(struct audio_route *ar, const char *name)
984*b9df5ad1SAndroid Build Coastguard Worker {
985*b9df5ad1SAndroid Build Coastguard Worker     if (audio_route_reset_path(ar, name) < 0) {
986*b9df5ad1SAndroid Build Coastguard Worker         return -1;
987*b9df5ad1SAndroid Build Coastguard Worker     }
988*b9df5ad1SAndroid Build Coastguard Worker     return audio_route_update_path(ar, name, DIRECTION_REVERSE);
989*b9df5ad1SAndroid Build Coastguard Worker }
990*b9df5ad1SAndroid Build Coastguard Worker 
audio_route_force_reset_and_update_path(struct audio_route * ar,const char * name)991*b9df5ad1SAndroid Build Coastguard Worker int audio_route_force_reset_and_update_path(struct audio_route *ar, const char *name)
992*b9df5ad1SAndroid Build Coastguard Worker {
993*b9df5ad1SAndroid Build Coastguard Worker     if (audio_route_reset_path(ar, name) < 0) {
994*b9df5ad1SAndroid Build Coastguard Worker         return -1;
995*b9df5ad1SAndroid Build Coastguard Worker     }
996*b9df5ad1SAndroid Build Coastguard Worker 
997*b9df5ad1SAndroid Build Coastguard Worker     return audio_route_update_path(ar, name, DIRECTION_REVERSE_RESET);
998*b9df5ad1SAndroid Build Coastguard Worker }
999*b9df5ad1SAndroid Build Coastguard Worker 
audio_route_supports_path(struct audio_route * ar,const char * name)1000*b9df5ad1SAndroid Build Coastguard Worker int audio_route_supports_path(struct audio_route *ar, const char *name)
1001*b9df5ad1SAndroid Build Coastguard Worker {
1002*b9df5ad1SAndroid Build Coastguard Worker     if (!path_get_by_name(ar, name)) {
1003*b9df5ad1SAndroid Build Coastguard Worker         return -1;
1004*b9df5ad1SAndroid Build Coastguard Worker     }
1005*b9df5ad1SAndroid Build Coastguard Worker 
1006*b9df5ad1SAndroid Build Coastguard Worker     return 0;
1007*b9df5ad1SAndroid Build Coastguard Worker }
1008*b9df5ad1SAndroid Build Coastguard Worker 
audio_route_init(unsigned int card,const char * xml_path)1009*b9df5ad1SAndroid Build Coastguard Worker struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
1010*b9df5ad1SAndroid Build Coastguard Worker {
1011*b9df5ad1SAndroid Build Coastguard Worker     struct config_parse_state state;
1012*b9df5ad1SAndroid Build Coastguard Worker     XML_Parser parser;
1013*b9df5ad1SAndroid Build Coastguard Worker     FILE *file;
1014*b9df5ad1SAndroid Build Coastguard Worker     int bytes_read;
1015*b9df5ad1SAndroid Build Coastguard Worker     void *buf;
1016*b9df5ad1SAndroid Build Coastguard Worker     struct audio_route *ar;
1017*b9df5ad1SAndroid Build Coastguard Worker 
1018*b9df5ad1SAndroid Build Coastguard Worker     ar = calloc(1, sizeof(struct audio_route));
1019*b9df5ad1SAndroid Build Coastguard Worker     if (!ar)
1020*b9df5ad1SAndroid Build Coastguard Worker         goto err_calloc;
1021*b9df5ad1SAndroid Build Coastguard Worker 
1022*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer = mixer_open(card);
1023*b9df5ad1SAndroid Build Coastguard Worker     if (!ar->mixer) {
1024*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("Unable to open the mixer, aborting.");
1025*b9df5ad1SAndroid Build Coastguard Worker         goto err_mixer_open;
1026*b9df5ad1SAndroid Build Coastguard Worker     }
1027*b9df5ad1SAndroid Build Coastguard Worker 
1028*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path = NULL;
1029*b9df5ad1SAndroid Build Coastguard Worker     ar->mixer_path_size = 0;
1030*b9df5ad1SAndroid Build Coastguard Worker     ar->num_mixer_paths = 0;
1031*b9df5ad1SAndroid Build Coastguard Worker 
1032*b9df5ad1SAndroid Build Coastguard Worker     /* allocate space for and read current mixer settings */
1033*b9df5ad1SAndroid Build Coastguard Worker     if (alloc_mixer_state(ar) < 0)
1034*b9df5ad1SAndroid Build Coastguard Worker         goto err_mixer_state;
1035*b9df5ad1SAndroid Build Coastguard Worker 
1036*b9df5ad1SAndroid Build Coastguard Worker     /* use the default XML path if none is provided */
1037*b9df5ad1SAndroid Build Coastguard Worker     if (xml_path == NULL)
1038*b9df5ad1SAndroid Build Coastguard Worker         xml_path = MIXER_XML_PATH;
1039*b9df5ad1SAndroid Build Coastguard Worker 
1040*b9df5ad1SAndroid Build Coastguard Worker     file = fopen(xml_path, "r");
1041*b9df5ad1SAndroid Build Coastguard Worker 
1042*b9df5ad1SAndroid Build Coastguard Worker     if (!file) {
1043*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("Failed to open %s: %s", xml_path, strerror(errno));
1044*b9df5ad1SAndroid Build Coastguard Worker         goto err_fopen;
1045*b9df5ad1SAndroid Build Coastguard Worker     }
1046*b9df5ad1SAndroid Build Coastguard Worker 
1047*b9df5ad1SAndroid Build Coastguard Worker     parser = XML_ParserCreate(NULL);
1048*b9df5ad1SAndroid Build Coastguard Worker     if (!parser) {
1049*b9df5ad1SAndroid Build Coastguard Worker         ALOGE("Failed to create XML parser");
1050*b9df5ad1SAndroid Build Coastguard Worker         goto err_parser_create;
1051*b9df5ad1SAndroid Build Coastguard Worker     }
1052*b9df5ad1SAndroid Build Coastguard Worker 
1053*b9df5ad1SAndroid Build Coastguard Worker     memset(&state, 0, sizeof(state));
1054*b9df5ad1SAndroid Build Coastguard Worker     state.ar = ar;
1055*b9df5ad1SAndroid Build Coastguard Worker     XML_SetUserData(parser, &state);
1056*b9df5ad1SAndroid Build Coastguard Worker     XML_SetElementHandler(parser, start_tag, end_tag);
1057*b9df5ad1SAndroid Build Coastguard Worker 
1058*b9df5ad1SAndroid Build Coastguard Worker     for (;;) {
1059*b9df5ad1SAndroid Build Coastguard Worker         buf = XML_GetBuffer(parser, BUF_SIZE);
1060*b9df5ad1SAndroid Build Coastguard Worker         if (buf == NULL)
1061*b9df5ad1SAndroid Build Coastguard Worker             goto err_parse;
1062*b9df5ad1SAndroid Build Coastguard Worker 
1063*b9df5ad1SAndroid Build Coastguard Worker         bytes_read = fread(buf, 1, BUF_SIZE, file);
1064*b9df5ad1SAndroid Build Coastguard Worker         if (bytes_read < 0)
1065*b9df5ad1SAndroid Build Coastguard Worker             goto err_parse;
1066*b9df5ad1SAndroid Build Coastguard Worker 
1067*b9df5ad1SAndroid Build Coastguard Worker         if (XML_ParseBuffer(parser, bytes_read,
1068*b9df5ad1SAndroid Build Coastguard Worker                             bytes_read == 0) == XML_STATUS_ERROR) {
1069*b9df5ad1SAndroid Build Coastguard Worker             ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
1070*b9df5ad1SAndroid Build Coastguard Worker             goto err_parse;
1071*b9df5ad1SAndroid Build Coastguard Worker         }
1072*b9df5ad1SAndroid Build Coastguard Worker 
1073*b9df5ad1SAndroid Build Coastguard Worker         if (bytes_read == 0)
1074*b9df5ad1SAndroid Build Coastguard Worker             break;
1075*b9df5ad1SAndroid Build Coastguard Worker     }
1076*b9df5ad1SAndroid Build Coastguard Worker 
1077*b9df5ad1SAndroid Build Coastguard Worker     /* apply the initial mixer values, and save them so we can reset the
1078*b9df5ad1SAndroid Build Coastguard Worker        mixer to the original values */
1079*b9df5ad1SAndroid Build Coastguard Worker     audio_route_update_mixer(ar);
1080*b9df5ad1SAndroid Build Coastguard Worker     save_mixer_state(ar);
1081*b9df5ad1SAndroid Build Coastguard Worker 
1082*b9df5ad1SAndroid Build Coastguard Worker     XML_ParserFree(parser);
1083*b9df5ad1SAndroid Build Coastguard Worker     fclose(file);
1084*b9df5ad1SAndroid Build Coastguard Worker     return ar;
1085*b9df5ad1SAndroid Build Coastguard Worker 
1086*b9df5ad1SAndroid Build Coastguard Worker err_parse:
1087*b9df5ad1SAndroid Build Coastguard Worker     path_free(ar);
1088*b9df5ad1SAndroid Build Coastguard Worker     XML_ParserFree(parser);
1089*b9df5ad1SAndroid Build Coastguard Worker err_parser_create:
1090*b9df5ad1SAndroid Build Coastguard Worker     fclose(file);
1091*b9df5ad1SAndroid Build Coastguard Worker err_fopen:
1092*b9df5ad1SAndroid Build Coastguard Worker     free_mixer_state(ar);
1093*b9df5ad1SAndroid Build Coastguard Worker err_mixer_state:
1094*b9df5ad1SAndroid Build Coastguard Worker     mixer_close(ar->mixer);
1095*b9df5ad1SAndroid Build Coastguard Worker err_mixer_open:
1096*b9df5ad1SAndroid Build Coastguard Worker     free(ar);
1097*b9df5ad1SAndroid Build Coastguard Worker     ar = NULL;
1098*b9df5ad1SAndroid Build Coastguard Worker err_calloc:
1099*b9df5ad1SAndroid Build Coastguard Worker     return NULL;
1100*b9df5ad1SAndroid Build Coastguard Worker }
1101*b9df5ad1SAndroid Build Coastguard Worker 
audio_route_free(struct audio_route * ar)1102*b9df5ad1SAndroid Build Coastguard Worker void audio_route_free(struct audio_route *ar)
1103*b9df5ad1SAndroid Build Coastguard Worker {
1104*b9df5ad1SAndroid Build Coastguard Worker     free_mixer_state(ar);
1105*b9df5ad1SAndroid Build Coastguard Worker     mixer_close(ar->mixer);
1106*b9df5ad1SAndroid Build Coastguard Worker     path_free(ar);
1107*b9df5ad1SAndroid Build Coastguard Worker     free(ar);
1108*b9df5ad1SAndroid Build Coastguard Worker }
1109