blob: c14cbf988ba5680d48322789cbf0d16a6109a11b [file] [log] [blame]
Simon Wilsoncef93592013-01-15 16:35:46 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 * Inspired by TinyHW, written by Mark Brown at Wolfson Micro
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#define LOG_TAG "audio_route"
19/*#define LOG_NDEBUG 0*/
20
21#include <errno.h>
22#include <expat.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <string.h>
26
27#include <cutils/log.h>
28
29#include <tinyalsa/asoundlib.h>
30
31#define BUF_SIZE 1024
32#define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
33#define INITIAL_MIXER_PATH_SIZE 8
34
35struct mixer_state {
36 struct mixer_ctl *ctl;
37 unsigned int num_values;
38 int *old_value;
39 int *new_value;
40 int *reset_value;
Simon Wilsoncef93592013-01-15 16:35:46 -080041};
42
43struct mixer_setting {
Simon Wilsonf3090fa2013-02-12 16:41:52 -080044 unsigned int ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -080045 unsigned int num_values;
46 int *value;
Simon Wilsoncef93592013-01-15 16:35:46 -080047};
48
49struct mixer_value {
Simon Wilsonf3090fa2013-02-12 16:41:52 -080050 unsigned int ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -080051 int index;
52 int value;
53};
54
55struct mixer_path {
56 char *name;
57 unsigned int size;
58 unsigned int length;
59 struct mixer_setting *setting;
60};
61
62struct audio_route {
63 struct mixer *mixer;
64 unsigned int num_mixer_ctls;
65 struct mixer_state *mixer_state;
66
67 unsigned int mixer_path_size;
68 unsigned int num_mixer_paths;
69 struct mixer_path *mixer_path;
70};
71
72struct config_parse_state {
73 struct audio_route *ar;
74 struct mixer_path *path;
75 int level;
76};
77
78/* path functions */
79
Eric Laurentc4b22662014-04-21 09:41:20 -070080static bool is_supported_ctl_type(enum mixer_ctl_type type)
Eric Laurentc5b5f6b2013-12-18 09:58:04 -080081{
82 switch (type) {
83 case MIXER_CTL_TYPE_BOOL:
84 case MIXER_CTL_TYPE_INT:
85 case MIXER_CTL_TYPE_ENUM:
86 return true;
87 default:
88 return false;
89 }
90}
91
Simon Wilsonf3090fa2013-02-12 16:41:52 -080092static inline struct mixer_ctl *index_to_ctl(struct audio_route *ar,
93 unsigned int ctl_index)
94{
95 return ar->mixer_state[ctl_index].ctl;
96}
97
98static void path_print(struct audio_route *ar, struct mixer_path *path)
Simon Wilsoncef93592013-01-15 16:35:46 -080099{
100 unsigned int i;
101 unsigned int j;
102
103 ALOGE("Path: %s, length: %d", path->name, path->length);
104 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800105 struct mixer_ctl *ctl = index_to_ctl(ar, path->setting[i].ctl_index);
106
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700107 ALOGE(" id=%d: ctl=%s", i, mixer_ctl_get_name(ctl));
Simon Wilsoncef93592013-01-15 16:35:46 -0800108 for (j = 0; j < path->setting[i].num_values; j++)
109 ALOGE(" id=%d value=%d", j, path->setting[i].value[j]);
110 }
111}
112
113static void path_free(struct audio_route *ar)
114{
115 unsigned int i;
116
117 for (i = 0; i < ar->num_mixer_paths; i++) {
118 if (ar->mixer_path[i].name)
119 free(ar->mixer_path[i].name);
120 if (ar->mixer_path[i].setting) {
121 if (ar->mixer_path[i].setting->value)
122 free(ar->mixer_path[i].setting->value);
123 free(ar->mixer_path[i].setting);
124 }
125 }
126 free(ar->mixer_path);
127}
128
129static struct mixer_path *path_get_by_name(struct audio_route *ar,
130 const char *name)
131{
132 unsigned int i;
133
134 for (i = 0; i < ar->num_mixer_paths; i++)
135 if (strcmp(ar->mixer_path[i].name, name) == 0)
136 return &ar->mixer_path[i];
137
138 return NULL;
139}
140
141static struct mixer_path *path_create(struct audio_route *ar, const char *name)
142{
143 struct mixer_path *new_mixer_path = NULL;
144
145 if (path_get_by_name(ar, name)) {
146 ALOGE("Path name '%s' already exists", name);
147 return NULL;
148 }
149
150 /* check if we need to allocate more space for mixer paths */
151 if (ar->mixer_path_size <= ar->num_mixer_paths) {
152 if (ar->mixer_path_size == 0)
153 ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
154 else
155 ar->mixer_path_size *= 2;
156
157 new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
158 sizeof(struct mixer_path));
159 if (new_mixer_path == NULL) {
160 ALOGE("Unable to allocate more paths");
161 return NULL;
162 } else {
163 ar->mixer_path = new_mixer_path;
164 }
165 }
166
167 /* initialise the new mixer path */
168 ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
169 ar->mixer_path[ar->num_mixer_paths].size = 0;
170 ar->mixer_path[ar->num_mixer_paths].length = 0;
171 ar->mixer_path[ar->num_mixer_paths].setting = NULL;
172
173 /* return the mixer path just added, then increment number of them */
174 return &ar->mixer_path[ar->num_mixer_paths++];
175}
176
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800177static int find_ctl_index_in_path(struct mixer_path *path,
178 unsigned int ctl_index)
Simon Wilsoncef93592013-01-15 16:35:46 -0800179{
180 unsigned int i;
181
182 for (i = 0; i < path->length; i++)
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800183 if (path->setting[i].ctl_index == ctl_index)
Simon Wilsoncef93592013-01-15 16:35:46 -0800184 return i;
185
186 return -1;
187}
188
189static int alloc_path_setting(struct mixer_path *path)
190{
191 struct mixer_setting *new_path_setting;
192 int path_index;
193
194 /* check if we need to allocate more space for path settings */
195 if (path->size <= path->length) {
196 if (path->size == 0)
197 path->size = INITIAL_MIXER_PATH_SIZE;
198 else
199 path->size *= 2;
200
201 new_path_setting = realloc(path->setting,
202 path->size * sizeof(struct mixer_setting));
203 if (new_path_setting == NULL) {
204 ALOGE("Unable to allocate more path settings");
205 return -1;
206 } else {
207 path->setting = new_path_setting;
208 }
209 }
210
211 path_index = path->length;
212 path->length++;
213
214 return path_index;
215}
216
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800217static int path_add_setting(struct audio_route *ar, struct mixer_path *path,
Simon Wilsoncef93592013-01-15 16:35:46 -0800218 struct mixer_setting *setting)
219{
Simon Wilsoncef93592013-01-15 16:35:46 -0800220 int path_index;
221
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800222 if (find_ctl_index_in_path(path, setting->ctl_index) != -1) {
223 struct mixer_ctl *ctl = index_to_ctl(ar, setting->ctl_index);
224
Simon Wilsoncef93592013-01-15 16:35:46 -0800225 ALOGE("Control '%s' already exists in path '%s'",
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800226 mixer_ctl_get_name(ctl), path->name);
Simon Wilsoncef93592013-01-15 16:35:46 -0800227 return -1;
228 }
229
230 path_index = alloc_path_setting(path);
231 if (path_index < 0)
232 return -1;
233
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800234 path->setting[path_index].ctl_index = setting->ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -0800235 path->setting[path_index].num_values = setting->num_values;
236 path->setting[path_index].value = malloc(setting->num_values * sizeof(int));
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700237 /* copy all values */
238 memcpy(path->setting[path_index].value, setting->value,
239 setting->num_values * sizeof(int));
Simon Wilsoncef93592013-01-15 16:35:46 -0800240
241 return 0;
242}
243
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800244static int path_add_value(struct audio_route *ar, struct mixer_path *path,
Simon Wilsoncef93592013-01-15 16:35:46 -0800245 struct mixer_value *mixer_value)
246{
247 unsigned int i;
248 int path_index;
249 unsigned int num_values;
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800250 struct mixer_ctl *ctl;
Simon Wilsoncef93592013-01-15 16:35:46 -0800251
252 /* Check that mixer value index is within range */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800253 ctl = index_to_ctl(ar, mixer_value->ctl_index);
254 num_values = mixer_ctl_get_num_values(ctl);
Simon Wilsoncef93592013-01-15 16:35:46 -0800255 if (mixer_value->index >= (int)num_values) {
256 ALOGE("mixer index %d is out of range for '%s'", mixer_value->index,
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800257 mixer_ctl_get_name(ctl));
Simon Wilsoncef93592013-01-15 16:35:46 -0800258 return -1;
259 }
260
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800261 path_index = find_ctl_index_in_path(path, mixer_value->ctl_index);
Simon Wilsoncef93592013-01-15 16:35:46 -0800262 if (path_index < 0) {
263 /* New path */
264
265 path_index = alloc_path_setting(path);
266 if (path_index < 0)
267 return -1;
268
269 /* initialise the new path setting */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800270 path->setting[path_index].ctl_index = mixer_value->ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -0800271 path->setting[path_index].num_values = num_values;
272 path->setting[path_index].value = malloc(num_values * sizeof(int));
Simon Wilsoncef93592013-01-15 16:35:46 -0800273 path->setting[path_index].value[0] = mixer_value->value;
274 }
275
276 if (mixer_value->index == -1) {
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700277 /* set all values the same */
278 for (i = 0; i < num_values; i++)
279 path->setting[path_index].value[i] = mixer_value->value;
Simon Wilsoncef93592013-01-15 16:35:46 -0800280 } else {
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700281 /* set only one value */
Simon Wilsoncef93592013-01-15 16:35:46 -0800282 path->setting[path_index].value[mixer_value->index] = mixer_value->value;
283 }
284
285 return 0;
286}
287
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800288static int path_add_path(struct audio_route *ar, struct mixer_path *path,
289 struct mixer_path *sub_path)
Simon Wilsoncef93592013-01-15 16:35:46 -0800290{
291 unsigned int i;
292
293 for (i = 0; i < sub_path->length; i++)
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800294 if (path_add_setting(ar, path, &sub_path->setting[i]) < 0)
Simon Wilsoncef93592013-01-15 16:35:46 -0800295 return -1;
296
297 return 0;
298}
299
300static int path_apply(struct audio_route *ar, struct mixer_path *path)
301{
302 unsigned int i;
Simon Wilsoncef93592013-01-15 16:35:46 -0800303 unsigned int ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800304 struct mixer_ctl *ctl;
305 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800306
307 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800308 ctl_index = path->setting[i].ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800309 ctl = index_to_ctl(ar, ctl_index);
310 type = mixer_ctl_get_type(ctl);
311 if (!is_supported_ctl_type(type))
312 continue;
Simon Wilsoncef93592013-01-15 16:35:46 -0800313
314 /* apply the new value(s) */
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700315 memcpy(ar->mixer_state[ctl_index].new_value, path->setting[i].value,
316 path->setting[i].num_values * sizeof(int));
Simon Wilsoncef93592013-01-15 16:35:46 -0800317 }
318
319 return 0;
320}
321
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800322static int path_reset(struct audio_route *ar, struct mixer_path *path)
323{
324 unsigned int i;
325 unsigned int j;
326 unsigned int ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800327 struct mixer_ctl *ctl;
328 enum mixer_ctl_type type;
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800329
330 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800331 ctl_index = path->setting[i].ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800332 ctl = index_to_ctl(ar, ctl_index);
333 type = mixer_ctl_get_type(ctl);
334 if (!is_supported_ctl_type(type))
335 continue;
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800336
337 /* reset the value(s) */
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700338 memcpy(ar->mixer_state[ctl_index].new_value,
339 ar->mixer_state[ctl_index].reset_value,
340 ar->mixer_state[ctl_index].num_values * sizeof(int));
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800341 }
342
343 return 0;
344}
345
Simon Wilsoncef93592013-01-15 16:35:46 -0800346/* mixer helper function */
347static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
348{
349 unsigned int i;
350
351 /* Search the enum strings for a particular one */
352 for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) {
353 if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
354 break;
355 }
356
357 return i;
358}
359
360static void start_tag(void *data, const XML_Char *tag_name,
361 const XML_Char **attr)
362{
363 const XML_Char *attr_name = NULL;
364 const XML_Char *attr_id = NULL;
365 const XML_Char *attr_value = NULL;
366 struct config_parse_state *state = data;
367 struct audio_route *ar = state->ar;
368 unsigned int i;
369 unsigned int ctl_index;
370 struct mixer_ctl *ctl;
371 int value;
372 unsigned int id;
373 struct mixer_value mixer_value;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800374 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800375
376 /* Get name, id and value attributes (these may be empty) */
377 for (i = 0; attr[i]; i += 2) {
378 if (strcmp(attr[i], "name") == 0)
379 attr_name = attr[i + 1];
380 if (strcmp(attr[i], "id") == 0)
381 attr_id = attr[i + 1];
382 else if (strcmp(attr[i], "value") == 0)
383 attr_value = attr[i + 1];
384 }
385
386 /* Look at tags */
387 if (strcmp(tag_name, "path") == 0) {
388 if (attr_name == NULL) {
389 ALOGE("Unnamed path!");
390 } else {
391 if (state->level == 1) {
392 /* top level path: create and stash the path */
393 state->path = path_create(ar, (char *)attr_name);
394 } else {
395 /* nested path */
396 struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800397 path_add_path(ar, state->path, sub_path);
Simon Wilsoncef93592013-01-15 16:35:46 -0800398 }
399 }
400 }
401
402 else if (strcmp(tag_name, "ctl") == 0) {
403 /* Obtain the mixer ctl and value */
404 ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
Simon Wilsond35bd632013-05-13 12:47:41 -0700405 if (ctl == NULL) {
406 ALOGE("Control '%s' doesn't exist - skipping", attr_name);
407 goto done;
408 }
409
Simon Wilsoncef93592013-01-15 16:35:46 -0800410 switch (mixer_ctl_get_type(ctl)) {
411 case MIXER_CTL_TYPE_BOOL:
412 case MIXER_CTL_TYPE_INT:
Johan Gustavsson76824ba2013-11-08 10:54:12 +0100413 value = (int) strtol((char *)attr_value, NULL, 0);
Simon Wilsoncef93592013-01-15 16:35:46 -0800414 break;
415 case MIXER_CTL_TYPE_ENUM:
416 value = mixer_enum_string_to_value(ctl, (char *)attr_value);
417 break;
418 default:
419 value = 0;
420 break;
421 }
422
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800423 /* locate the mixer ctl in the list */
424 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
425 if (ar->mixer_state[ctl_index].ctl == ctl)
426 break;
427 }
428
Simon Wilsoncef93592013-01-15 16:35:46 -0800429 if (state->level == 1) {
430 /* top level ctl (initial setting) */
431
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800432 type = mixer_ctl_get_type(ctl);
433 if (is_supported_ctl_type(type)) {
434 /* apply the new value */
435 if (attr_id) {
436 /* set only one value */
437 id = atoi((char *)attr_id);
438 if (id < ar->mixer_state[ctl_index].num_values)
439 ar->mixer_state[ctl_index].new_value[id] = value;
440 else
441 ALOGE("value id out of range for mixer ctl '%s'",
442 mixer_ctl_get_name(ctl));
443 } else {
444 /* set all values the same */
445 for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
446 ar->mixer_state[ctl_index].new_value[i] = value;
447 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800448 }
449 } else {
450 /* nested ctl (within a path) */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800451 mixer_value.ctl_index = ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -0800452 mixer_value.value = value;
453 if (attr_id)
454 mixer_value.index = atoi((char *)attr_id);
455 else
456 mixer_value.index = -1;
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800457 path_add_value(ar, state->path, &mixer_value);
Simon Wilsoncef93592013-01-15 16:35:46 -0800458 }
459 }
460
Simon Wilsond35bd632013-05-13 12:47:41 -0700461done:
Simon Wilsoncef93592013-01-15 16:35:46 -0800462 state->level++;
463}
464
465static void end_tag(void *data, const XML_Char *tag_name)
466{
467 struct config_parse_state *state = data;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800468 (void)tag_name;
Simon Wilsoncef93592013-01-15 16:35:46 -0800469
470 state->level--;
471}
472
473static int alloc_mixer_state(struct audio_route *ar)
474{
475 unsigned int i;
476 unsigned int j;
477 unsigned int num_values;
478 struct mixer_ctl *ctl;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700479 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800480
481 ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
482 ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state));
483 if (!ar->mixer_state)
484 return -1;
485
486 for (i = 0; i < ar->num_mixer_ctls; i++) {
487 ctl = mixer_get_ctl(ar->mixer, i);
488 num_values = mixer_ctl_get_num_values(ctl);
489
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700490 ar->mixer_state[i].ctl = ctl;
491 ar->mixer_state[i].num_values = num_values;
492
493 /* Skip unsupported types that are not supported yet in XML */
494 type = mixer_ctl_get_type(ctl);
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800495
Divya Sharmaa6ec7372013-11-02 21:36:11 +0200496 if (!is_supported_ctl_type(type)) {
497 ar->mixer_state[i].old_value = NULL;
498 ar->mixer_state[i].new_value = NULL;
499 ar->mixer_state[i].reset_value = NULL;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700500 continue;
Divya Sharmaa6ec7372013-11-02 21:36:11 +0200501 }
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700502
Simon Wilsoncef93592013-01-15 16:35:46 -0800503 ar->mixer_state[i].old_value = malloc(num_values * sizeof(int));
504 ar->mixer_state[i].new_value = malloc(num_values * sizeof(int));
505 ar->mixer_state[i].reset_value = malloc(num_values * sizeof(int));
506
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700507 if (type == MIXER_CTL_TYPE_ENUM)
508 ar->mixer_state[i].old_value[0] = mixer_ctl_get_value(ctl, 0);
509 else
510 mixer_ctl_get_array(ctl, ar->mixer_state[i].old_value, num_values);
511 memcpy(ar->mixer_state[i].new_value, ar->mixer_state[i].old_value,
512 num_values * sizeof(int));
Simon Wilsoncef93592013-01-15 16:35:46 -0800513 }
514
515 return 0;
516}
517
518static void free_mixer_state(struct audio_route *ar)
519{
520 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800521 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800522
523 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800524 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
525 if (!is_supported_ctl_type(type))
526 continue;
527
Simon Wilsoncef93592013-01-15 16:35:46 -0800528 free(ar->mixer_state[i].old_value);
529 free(ar->mixer_state[i].new_value);
530 free(ar->mixer_state[i].reset_value);
531 }
532
533 free(ar->mixer_state);
534 ar->mixer_state = NULL;
535}
536
537/* Update the mixer with any changed values */
538int audio_route_update_mixer(struct audio_route *ar)
539{
540 unsigned int i;
541 unsigned int j;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700542 struct mixer_ctl *ctl;
Simon Wilsoncef93592013-01-15 16:35:46 -0800543
544 for (i = 0; i < ar->num_mixer_ctls; i++) {
545 unsigned int num_values = ar->mixer_state[i].num_values;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700546 enum mixer_ctl_type type;
547
548 ctl = ar->mixer_state[i].ctl;
549
550 /* Skip unsupported types */
551 type = mixer_ctl_get_type(ctl);
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800552 if (!is_supported_ctl_type(type))
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700553 continue;
Simon Wilsoncef93592013-01-15 16:35:46 -0800554
555 /* if the value has changed, update the mixer */
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700556 bool changed = false;
557 for (j = 0; j < num_values; j++) {
558 if (ar->mixer_state[i].old_value[j] != ar->mixer_state[i].new_value[j]) {
559 changed = true;
560 break;
Simon Wilsoncef93592013-01-15 16:35:46 -0800561 }
562 }
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700563 if (changed) {
564 if (type == MIXER_CTL_TYPE_ENUM)
565 mixer_ctl_set_value(ctl, 0, ar->mixer_state[i].new_value[0]);
566 else
567 mixer_ctl_set_array(ctl, ar->mixer_state[i].new_value, num_values);
568 memcpy(ar->mixer_state[i].old_value, ar->mixer_state[i].new_value,
569 num_values * sizeof(int));
570 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800571 }
572
573 return 0;
574}
575
576/* saves the current state of the mixer, for resetting all controls */
577static void save_mixer_state(struct audio_route *ar)
578{
579 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800580 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800581
582 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800583 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
584 if (!is_supported_ctl_type(type))
585 continue;
586
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700587 memcpy(ar->mixer_state[i].reset_value, ar->mixer_state[i].new_value,
588 ar->mixer_state[i].num_values * sizeof(int));
Simon Wilsoncef93592013-01-15 16:35:46 -0800589 }
590}
591
592/* Reset the audio routes back to the initial state */
593void audio_route_reset(struct audio_route *ar)
594{
595 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800596 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800597
598 /* load all of the saved values */
599 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800600 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
601 if (!is_supported_ctl_type(type))
602 continue;
603
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700604 memcpy(ar->mixer_state[i].new_value, ar->mixer_state[i].reset_value,
605 ar->mixer_state[i].num_values * sizeof(int));
Simon Wilsoncef93592013-01-15 16:35:46 -0800606 }
607}
608
609/* Apply an audio route path by name */
610int audio_route_apply_path(struct audio_route *ar, const char *name)
611{
612 struct mixer_path *path;
613
614 if (!ar) {
615 ALOGE("invalid audio_route");
616 return -1;
617 }
618
619 path = path_get_by_name(ar, name);
620 if (!path) {
621 ALOGE("unable to find path '%s'", name);
622 return -1;
623 }
624
625 path_apply(ar, path);
626
627 return 0;
628}
629
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800630/* Reset an audio route path by name */
631int audio_route_reset_path(struct audio_route *ar, const char *name)
632{
633 struct mixer_path *path;
634
635 if (!ar) {
636 ALOGE("invalid audio_route");
637 return -1;
638 }
639
640 path = path_get_by_name(ar, name);
641 if (!path) {
642 ALOGE("unable to find path '%s'", name);
643 return -1;
644 }
645
646 path_reset(ar, path);
647
648 return 0;
649}
650
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800651/*
652 * Operates on the specified path .. controls will be updated in the
653 * order listed in the XML file
654 */
655static int audio_route_update_path(struct audio_route *ar, const char *name, bool reverse)
656{
657 struct mixer_path *path;
658 int32_t i, end;
659 unsigned int j;
660
661 if (!ar) {
662 ALOGE("invalid audio_route");
663 return -1;
664 }
665
666 path = path_get_by_name(ar, name);
667 if (!path) {
668 ALOGE("unable to find path '%s'", name);
669 return -1;
670 }
671
672 i = reverse ? (path->length - 1) : 0;
673 end = reverse ? -1 : (int32_t)path->length;
674
675 while (i != end) {
676 unsigned int ctl_index;
677 enum mixer_ctl_type type;
678
679 ctl_index = path->setting[i].ctl_index;
680
681 struct mixer_state * ms = &ar->mixer_state[ctl_index];
682
683 type = mixer_ctl_get_type(ms->ctl);
684 if (!is_supported_ctl_type(type)) {
685 continue;
686 }
687
688 /* if any value has changed, update the mixer */
689 for (j = 0; j < ms->num_values; j++) {
690 if (ms->old_value[j] != ms->new_value[j]) {
691 if (type == MIXER_CTL_TYPE_ENUM)
692 mixer_ctl_set_value(ms->ctl, 0, ms->new_value[0]);
693 else
694 mixer_ctl_set_array(ms->ctl, ms->new_value, ms->num_values);
695 memcpy(ms->old_value, ms->new_value, ms->num_values * sizeof(int));
696 break;
697 }
698 }
699
700 i = reverse ? (i - 1) : (i + 1);
701 }
702 return 0;
703}
704
705int audio_route_apply_and_update_path(struct audio_route *ar, const char *name)
706{
707 if (audio_route_apply_path(ar, name) < 0) {
708 return -1;
709 }
710 return audio_route_update_path(ar, name, false /*reverse*/);
711}
712
713int audio_route_reset_and_update_path(struct audio_route *ar, const char *name)
714{
715 if (audio_route_reset_path(ar, name) < 0) {
716 return -1;
717 }
718 return audio_route_update_path(ar, name, true /*reverse*/);
719}
720
Simon Wilsoncef93592013-01-15 16:35:46 -0800721struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
722{
723 struct config_parse_state state;
724 XML_Parser parser;
725 FILE *file;
726 int bytes_read;
727 void *buf;
728 int i;
729 struct audio_route *ar;
730
731 ar = calloc(1, sizeof(struct audio_route));
732 if (!ar)
733 goto err_calloc;
734
735 ar->mixer = mixer_open(card);
736 if (!ar->mixer) {
737 ALOGE("Unable to open the mixer, aborting.");
738 goto err_mixer_open;
739 }
740
741 ar->mixer_path = NULL;
742 ar->mixer_path_size = 0;
743 ar->num_mixer_paths = 0;
744
745 /* allocate space for and read current mixer settings */
746 if (alloc_mixer_state(ar) < 0)
747 goto err_mixer_state;
748
749 /* use the default XML path if none is provided */
750 if (xml_path == NULL)
751 xml_path = MIXER_XML_PATH;
752
753 file = fopen(xml_path, "r");
754
755 if (!file) {
756 ALOGE("Failed to open %s", xml_path);
757 goto err_fopen;
758 }
759
760 parser = XML_ParserCreate(NULL);
761 if (!parser) {
762 ALOGE("Failed to create XML parser");
763 goto err_parser_create;
764 }
765
766 memset(&state, 0, sizeof(state));
767 state.ar = ar;
768 XML_SetUserData(parser, &state);
769 XML_SetElementHandler(parser, start_tag, end_tag);
770
771 for (;;) {
772 buf = XML_GetBuffer(parser, BUF_SIZE);
773 if (buf == NULL)
774 goto err_parse;
775
776 bytes_read = fread(buf, 1, BUF_SIZE, file);
777 if (bytes_read < 0)
778 goto err_parse;
779
780 if (XML_ParseBuffer(parser, bytes_read,
781 bytes_read == 0) == XML_STATUS_ERROR) {
782 ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
783 goto err_parse;
784 }
785
786 if (bytes_read == 0)
787 break;
788 }
789
790 /* apply the initial mixer values, and save them so we can reset the
791 mixer to the original values */
792 audio_route_update_mixer(ar);
793 save_mixer_state(ar);
794
795 XML_ParserFree(parser);
796 fclose(file);
797 return ar;
798
799err_parse:
800 XML_ParserFree(parser);
801err_parser_create:
802 fclose(file);
803err_fopen:
804 free_mixer_state(ar);
805err_mixer_state:
806 mixer_close(ar->mixer);
807err_mixer_open:
808 free(ar);
809 ar = NULL;
810err_calloc:
811 return NULL;
812}
813
814void audio_route_free(struct audio_route *ar)
815{
816 free_mixer_state(ar);
817 mixer_close(ar->mixer);
818 free(ar);
819}