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