1 /*
2 * Copyright (C) 2013 The Android Open Source Project
3 * Inspired by TinyHW, written by Mark Brown at Wolfson Micro
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #define LOG_TAG "audio_route"
19 /*#define LOG_NDEBUG 0*/
20
21 #include <errno.h>
22 #include <expat.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <cutils/log.h>
28
29 #include <tinyalsa/asoundlib.h>
30
31 #define BUF_SIZE 1024
32 #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
33 #define INITIAL_MIXER_PATH_SIZE 8
34
35 struct mixer_state {
36 struct mixer_ctl *ctl;
37 unsigned int num_values;
38 int *old_value;
39 int *new_value;
40 int *reset_value;
41 /* If linked is true, only the first element in each array is valid */
42 bool old_linked;
43 bool new_linked;
44 bool reset_linked;
45 };
46
47 struct mixer_setting {
48 struct mixer_ctl *ctl;
49 unsigned int num_values;
50 int *value;
51 /* If linked is true, only the first element in each array is valid */
52 bool linked;
53 };
54
55 struct mixer_value {
56 struct mixer_ctl *ctl;
57 int index;
58 int value;
59 };
60
61 struct mixer_path {
62 char *name;
63 unsigned int size;
64 unsigned int length;
65 struct mixer_setting *setting;
66 };
67
68 struct audio_route {
69 struct mixer *mixer;
70 unsigned int num_mixer_ctls;
71 struct mixer_state *mixer_state;
72
73 unsigned int mixer_path_size;
74 unsigned int num_mixer_paths;
75 struct mixer_path *mixer_path;
76 };
77
78 struct config_parse_state {
79 struct audio_route *ar;
80 struct mixer_path *path;
81 int level;
82 };
83
84 /* path functions */
85
path_print(struct mixer_path * path)86 static void path_print(struct mixer_path *path)
87 {
88 unsigned int i;
89 unsigned int j;
90
91 ALOGE("Path: %s, length: %d", path->name, path->length);
92 for (i = 0; i < path->length; i++) {
93 ALOGE(" id=%d: ctl=%s linked=%c", i,
94 mixer_ctl_get_name(path->setting[i].ctl),
95 path->setting[i].linked ? 'y' : 'n');
96 for (j = 0; j < path->setting[i].num_values; j++)
97 ALOGE(" id=%d value=%d", j, path->setting[i].value[j]);
98 }
99 }
100
path_free(struct audio_route * ar)101 static void path_free(struct audio_route *ar)
102 {
103 unsigned int i;
104
105 for (i = 0; i < ar->num_mixer_paths; i++) {
106 if (ar->mixer_path[i].name)
107 free(ar->mixer_path[i].name);
108 if (ar->mixer_path[i].setting) {
109 if (ar->mixer_path[i].setting->value)
110 free(ar->mixer_path[i].setting->value);
111 free(ar->mixer_path[i].setting);
112 }
113 }
114 free(ar->mixer_path);
115 }
116
path_get_by_name(struct audio_route * ar,const char * name)117 static struct mixer_path *path_get_by_name(struct audio_route *ar,
118 const char *name)
119 {
120 unsigned int i;
121
122 for (i = 0; i < ar->num_mixer_paths; i++)
123 if (strcmp(ar->mixer_path[i].name, name) == 0)
124 return &ar->mixer_path[i];
125
126 return NULL;
127 }
128
path_create(struct audio_route * ar,const char * name)129 static struct mixer_path *path_create(struct audio_route *ar, const char *name)
130 {
131 struct mixer_path *new_mixer_path = NULL;
132
133 if (path_get_by_name(ar, name)) {
134 ALOGE("Path name '%s' already exists", name);
135 return NULL;
136 }
137
138 /* check if we need to allocate more space for mixer paths */
139 if (ar->mixer_path_size <= ar->num_mixer_paths) {
140 if (ar->mixer_path_size == 0)
141 ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
142 else
143 ar->mixer_path_size *= 2;
144
145 new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
146 sizeof(struct mixer_path));
147 if (new_mixer_path == NULL) {
148 ALOGE("Unable to allocate more paths");
149 return NULL;
150 } else {
151 ar->mixer_path = new_mixer_path;
152 }
153 }
154
155 /* initialise the new mixer path */
156 ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
157 ar->mixer_path[ar->num_mixer_paths].size = 0;
158 ar->mixer_path[ar->num_mixer_paths].length = 0;
159 ar->mixer_path[ar->num_mixer_paths].setting = NULL;
160
161 /* return the mixer path just added, then increment number of them */
162 return &ar->mixer_path[ar->num_mixer_paths++];
163 }
164
find_ctl_in_path(struct mixer_path * path,struct mixer_ctl * ctl)165 static int find_ctl_in_path(struct mixer_path *path, struct mixer_ctl *ctl)
166 {
167 unsigned int i;
168
169 for (i = 0; i < path->length; i++)
170 if (path->setting[i].ctl == ctl)
171 return i;
172
173 return -1;
174 }
175
alloc_path_setting(struct mixer_path * path)176 static int alloc_path_setting(struct mixer_path *path)
177 {
178 struct mixer_setting *new_path_setting;
179 int path_index;
180
181 /* check if we need to allocate more space for path settings */
182 if (path->size <= path->length) {
183 if (path->size == 0)
184 path->size = INITIAL_MIXER_PATH_SIZE;
185 else
186 path->size *= 2;
187
188 new_path_setting = realloc(path->setting,
189 path->size * sizeof(struct mixer_setting));
190 if (new_path_setting == NULL) {
191 ALOGE("Unable to allocate more path settings");
192 return -1;
193 } else {
194 path->setting = new_path_setting;
195 }
196 }
197
198 path_index = path->length;
199 path->length++;
200
201 return path_index;
202 }
203
path_add_setting(struct mixer_path * path,struct mixer_setting * setting)204 static int path_add_setting(struct mixer_path *path,
205 struct mixer_setting *setting)
206 {
207 unsigned int i;
208 int path_index;
209
210 if (find_ctl_in_path(path, setting->ctl) != -1) {
211 ALOGE("Control '%s' already exists in path '%s'",
212 mixer_ctl_get_name(setting->ctl), path->name);
213 return -1;
214 }
215
216 path_index = alloc_path_setting(path);
217 if (path_index < 0)
218 return -1;
219
220 path->setting[path_index].ctl = setting->ctl;
221 path->setting[path_index].num_values = setting->num_values;
222 path->setting[path_index].value = malloc(setting->num_values * sizeof(int));
223 path->setting[path_index].linked = setting->linked;
224 if (setting->linked) {
225 path->setting[path_index].value[0] = setting->value[0];
226 } else {
227 for (i = 0; i < setting->num_values; i++)
228 path->setting[path_index].value[i] = setting->value[i];
229 }
230
231 return 0;
232 }
233
path_add_value(struct mixer_path * path,struct mixer_value * mixer_value)234 static int path_add_value(struct mixer_path *path,
235 struct mixer_value *mixer_value)
236 {
237 unsigned int i;
238 int path_index;
239 unsigned int num_values;
240
241 /* Check that mixer value index is within range */
242 num_values = mixer_ctl_get_num_values(mixer_value->ctl);
243 if (mixer_value->index >= (int)num_values) {
244 ALOGE("mixer index %d is out of range for '%s'", mixer_value->index,
245 mixer_ctl_get_name(mixer_value->ctl));
246 return -1;
247 }
248
249 path_index = find_ctl_in_path(path, mixer_value->ctl);
250 if (path_index < 0) {
251 /* New path */
252
253 path_index = alloc_path_setting(path);
254 if (path_index < 0)
255 return -1;
256
257 /* initialise the new path setting */
258 path->setting[path_index].ctl = mixer_value->ctl;
259 path->setting[path_index].num_values = num_values;
260 path->setting[path_index].value = malloc(num_values * sizeof(int));
261 path->setting[path_index].linked = true;
262 path->setting[path_index].value[0] = mixer_value->value;
263 }
264
265 if (mixer_value->index == -1) {
266 /* Linked, so only set the first value */
267 path->setting[path_index].linked = true;
268 path->setting[path_index].value[0] = mixer_value->value;
269 } else {
270 if (path->setting[path_index].linked && (num_values > 1)) {
271 /* Unlinking the values, so duplicate them across */
272 for (i = 1; i < num_values; i++) {
273 path->setting[path_index].value[i] =
274 path->setting[path_index].value[0];
275 }
276 path->setting[path_index].linked = false;
277 }
278 path->setting[path_index].value[mixer_value->index] = mixer_value->value;
279 }
280
281 return 0;
282 }
283
path_add_path(struct mixer_path * path,struct mixer_path * sub_path)284 static int path_add_path(struct mixer_path *path, struct mixer_path *sub_path)
285 {
286 unsigned int i;
287
288 for (i = 0; i < sub_path->length; i++)
289 if (path_add_setting(path, &sub_path->setting[i]) < 0)
290 return -1;
291
292 return 0;
293 }
294
path_apply(struct audio_route * ar,struct mixer_path * path)295 static int path_apply(struct audio_route *ar, struct mixer_path *path)
296 {
297 unsigned int i;
298 unsigned int j;
299 unsigned int ctl_index;
300
301 for (i = 0; i < path->length; i++) {
302 struct mixer_ctl *ctl = path->setting[i].ctl;
303
304 /* locate the mixer ctl in the list */
305 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++)
306 if (ar->mixer_state[ctl_index].ctl == ctl)
307 break;
308
309 /* apply the new value(s) */
310 for (j = 0; j < ar->mixer_state[ctl_index].num_values; j++) {
311 ar->mixer_state[ctl_index].new_value[j] = path->setting[i].value[j];
312 if (path->setting[i].linked)
313 break;
314 }
315 ar->mixer_state[ctl_index].new_linked = path->setting[i].linked;
316 }
317
318 return 0;
319 }
320
path_reset(struct audio_route * ar,struct mixer_path * path)321 static int path_reset(struct audio_route *ar, struct mixer_path *path)
322 {
323 unsigned int i;
324 unsigned int j;
325 unsigned int ctl_index;
326
327 for (i = 0; i < path->length; i++) {
328 struct mixer_ctl *ctl = path->setting[i].ctl;
329
330 /* locate the mixer ctl in the list */
331 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
332 if (ar->mixer_state[ctl_index].ctl == ctl)
333 break;
334 }
335
336 /* reset the value(s) */
337 for (j = 0; j < ar->mixer_state[ctl_index].num_values; j++) {
338 ar->mixer_state[ctl_index].new_value[j] = ar->mixer_state[ctl_index].reset_value[j];
339 if (ar->mixer_state[ctl_index].reset_linked)
340 break;
341 }
342 ar->mixer_state[ctl_index].new_linked = ar->mixer_state[ctl_index].reset_linked;
343 }
344
345 return 0;
346 }
347
348 /* mixer helper function */
mixer_enum_string_to_value(struct mixer_ctl * ctl,const char * string)349 static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
350 {
351 unsigned int i;
352
353 /* Search the enum strings for a particular one */
354 for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) {
355 if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
356 break;
357 }
358
359 return i;
360 }
361
start_tag(void * data,const XML_Char * tag_name,const XML_Char ** attr)362 static void start_tag(void *data, const XML_Char *tag_name,
363 const XML_Char **attr)
364 {
365 const XML_Char *attr_name = NULL;
366 const XML_Char *attr_id = NULL;
367 const XML_Char *attr_value = NULL;
368 struct config_parse_state *state = data;
369 struct audio_route *ar = state->ar;
370 unsigned int i;
371 unsigned int ctl_index;
372 struct mixer_ctl *ctl;
373 int value;
374 unsigned int id;
375 struct mixer_value mixer_value;
376
377 /* Get name, id and value attributes (these may be empty) */
378 for (i = 0; attr[i]; i += 2) {
379 if (strcmp(attr[i], "name") == 0)
380 attr_name = attr[i + 1];
381 if (strcmp(attr[i], "id") == 0)
382 attr_id = attr[i + 1];
383 else if (strcmp(attr[i], "value") == 0)
384 attr_value = attr[i + 1];
385 }
386
387 /* Look at tags */
388 if (strcmp(tag_name, "path") == 0) {
389 if (attr_name == NULL) {
390 ALOGE("Unnamed path!");
391 } else {
392 if (state->level == 1) {
393 /* top level path: create and stash the path */
394 state->path = path_create(ar, (char *)attr_name);
395 } else {
396 /* nested path */
397 struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
398 path_add_path(state->path, sub_path);
399 }
400 }
401 }
402
403 else if (strcmp(tag_name, "ctl") == 0) {
404 /* Obtain the mixer ctl and value */
405 ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
406 switch (mixer_ctl_get_type(ctl)) {
407 case MIXER_CTL_TYPE_BOOL:
408 case MIXER_CTL_TYPE_INT:
409 value = atoi((char *)attr_value);
410 break;
411 case MIXER_CTL_TYPE_ENUM:
412 value = mixer_enum_string_to_value(ctl, (char *)attr_value);
413 break;
414 default:
415 value = 0;
416 break;
417 }
418
419 if (state->level == 1) {
420 /* top level ctl (initial setting) */
421
422 /* locate the mixer ctl in the list */
423 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
424 if (ar->mixer_state[ctl_index].ctl == ctl)
425 break;
426 }
427
428 /* apply the new value */
429 if (attr_id) {
430 /* set only one value */
431 id = atoi((char *)attr_id);
432 if (id < ar->mixer_state[ctl_index].num_values) {
433 if (ar->mixer_state[ctl_index].new_linked) {
434 /*
435 * We're unlinking the values, so copy old_value[0] into
436 * all the new_value elements.
437 */
438 for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++) {
439 ar->mixer_state[ctl_index].new_value[i] =
440 ar->mixer_state[ctl_index].old_value[0];
441 }
442 ar->mixer_state[ctl_index].new_linked = false;
443 }
444 ar->mixer_state[ctl_index].new_value[id] = value;
445 } else {
446 ALOGE("value id out of range for mixer ctl '%s'",
447 mixer_ctl_get_name(ctl));
448 }
449 } else {
450 ar->mixer_state[ctl_index].new_value[0] = value;
451 ar->mixer_state[ctl_index].new_linked = true;
452 }
453 } else {
454 /* nested ctl (within a path) */
455 mixer_value.ctl = ctl;
456 mixer_value.value = value;
457 if (attr_id)
458 mixer_value.index = atoi((char *)attr_id);
459 else
460 mixer_value.index = -1;
461 path_add_value(state->path, &mixer_value);
462 }
463 }
464
465 state->level++;
466 }
467
end_tag(void * data,const XML_Char * tag_name)468 static void end_tag(void *data, const XML_Char *tag_name)
469 {
470 struct config_parse_state *state = data;
471
472 state->level--;
473 }
474
alloc_mixer_state(struct audio_route * ar)475 static int alloc_mixer_state(struct audio_route *ar)
476 {
477 unsigned int i;
478 unsigned int j;
479 unsigned int num_values;
480 struct mixer_ctl *ctl;
481 bool linked;
482
483 ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
484 ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state));
485 if (!ar->mixer_state)
486 return -1;
487
488 for (i = 0; i < ar->num_mixer_ctls; i++) {
489 ctl = mixer_get_ctl(ar->mixer, i);
490 num_values = mixer_ctl_get_num_values(ctl);
491
492 ar->mixer_state[i].old_value = malloc(num_values * sizeof(int));
493 ar->mixer_state[i].new_value = malloc(num_values * sizeof(int));
494 ar->mixer_state[i].reset_value = malloc(num_values * sizeof(int));
495
496 /*
497 * Get all mixer values for controls with multiple values. If all
498 * values are the same, set the linked flag.
499 */
500 linked = true;
501 for (j = 0; j < num_values; j++) {
502 ar->mixer_state[i].old_value[j] = mixer_ctl_get_value(ctl, j);
503 ar->mixer_state[i].new_value[j] = ar->mixer_state[i].old_value[j];
504
505 /*
506 * If the next value is different from the last, set linked to
507 * false.
508 */
509 if ((j > 0) && (ar->mixer_state[i].old_value[j - 1] !=
510 ar->mixer_state[i].old_value[j])) {
511 linked = false;
512 }
513 }
514 ar->mixer_state[i].ctl = ctl;
515 ar->mixer_state[i].old_linked = linked;
516 ar->mixer_state[i].new_linked = linked;
517 ar->mixer_state[i].num_values = num_values;
518 }
519
520 return 0;
521 }
522
free_mixer_state(struct audio_route * ar)523 static void free_mixer_state(struct audio_route *ar)
524 {
525 unsigned int i;
526
527 for (i = 0; i < ar->num_mixer_ctls; i++) {
528 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 */
audio_route_update_mixer(struct audio_route * ar)538 int audio_route_update_mixer(struct audio_route *ar)
539 {
540 unsigned int i;
541 unsigned int j;
542
543 for (i = 0; i < ar->num_mixer_ctls; i++) {
544 unsigned int num_values = ar->mixer_state[i].num_values;
545
546 /* if the value has changed, update the mixer */
547 if (ar->mixer_state[i].new_linked) {
548 if (ar->mixer_state[i].old_value[0] != ar->mixer_state[i].new_value[0]) {
549 /* linked ctl, so set all ctl values the same */
550 for (j = 0; j < num_values; j++)
551 mixer_ctl_set_value(ar->mixer_state[i].ctl, j,
552 ar->mixer_state[i].new_value[0]);
553 ar->mixer_state[i].old_value[0] = ar->mixer_state[i].new_value[0];
554 }
555 } else {
556 for (j = 0; j < num_values; j++) {
557 /*
558 * unlinked ctl, so set each value if necessary.
559 * Note that if the new value is unlinked but the old is
560 * linked, only value 0 is valid, so we always have to
561 * update the mixer for the other values.
562 */
563 if (ar->mixer_state[i].old_linked ||
564 (ar->mixer_state[i].old_value[j] !=
565 ar->mixer_state[i].new_value[j])) {
566 mixer_ctl_set_value(ar->mixer_state[i].ctl, j,
567 ar->mixer_state[i].new_value[j]);
568 ar->mixer_state[i].old_value[j] = ar->mixer_state[i].new_value[j];
569 }
570 }
571 }
572 ar->mixer_state[i].old_linked = ar->mixer_state[i].new_linked;
573 }
574
575 return 0;
576 }
577
578 /* saves the current state of the mixer, for resetting all controls */
save_mixer_state(struct audio_route * ar)579 static void save_mixer_state(struct audio_route *ar)
580 {
581 unsigned int i;
582 unsigned int j;
583
584 for (i = 0; i < ar->num_mixer_ctls; i++) {
585 for (j = 0; j < ar->mixer_state[i].num_values; j++) {
586 ar->mixer_state[i].reset_value[j] = ar->mixer_state[i].new_value[j];
587
588 /* if the values are linked, only need to save value 0 */
589 if (ar->mixer_state[i].new_linked)
590 break;
591 }
592 ar->mixer_state[i].reset_linked = ar->mixer_state[i].new_linked;
593 }
594 }
595
596 /* Reset the audio routes back to the initial state */
audio_route_reset(struct audio_route * ar)597 void audio_route_reset(struct audio_route *ar)
598 {
599 unsigned int i;
600 unsigned int j;
601
602 /* load all of the saved values */
603 for (i = 0; i < ar->num_mixer_ctls; i++) {
604 for (j = 0; j < ar->mixer_state[i].num_values; j++) {
605 ar->mixer_state[i].new_value[j] = ar->mixer_state[i].reset_value[j];
606
607 /* if the values are linked, only need to save value 0 */
608 if (ar->mixer_state[i].reset_linked)
609 break;
610 }
611 ar->mixer_state[i].new_linked = ar->mixer_state[i].reset_linked;
612 }
613 }
614
615 /* Apply an audio route path by name */
audio_route_apply_path(struct audio_route * ar,const char * name)616 int audio_route_apply_path(struct audio_route *ar, const char *name)
617 {
618 struct mixer_path *path;
619
620 if (!ar) {
621 ALOGE("invalid audio_route");
622 return -1;
623 }
624
625 path = path_get_by_name(ar, name);
626 if (!path) {
627 ALOGE("unable to find path '%s'", name);
628 return -1;
629 }
630
631 path_apply(ar, path);
632
633 return 0;
634 }
635
636 /* Reset an audio route path by name */
audio_route_reset_path(struct audio_route * ar,const char * name)637 int audio_route_reset_path(struct audio_route *ar, const char *name)
638 {
639 struct mixer_path *path;
640
641 if (!ar) {
642 ALOGE("invalid audio_route");
643 return -1;
644 }
645
646 path = path_get_by_name(ar, name);
647 if (!path) {
648 ALOGE("unable to find path '%s'", name);
649 return -1;
650 }
651
652 path_reset(ar, path);
653
654 return 0;
655 }
656
audio_route_init(unsigned int card,const char * xml_path)657 struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
658 {
659 struct config_parse_state state;
660 XML_Parser parser;
661 FILE *file;
662 int bytes_read;
663 void *buf;
664 int i;
665 struct audio_route *ar;
666
667 ar = calloc(1, sizeof(struct audio_route));
668 if (!ar)
669 goto err_calloc;
670
671 ar->mixer = mixer_open(card);
672 if (!ar->mixer) {
673 ALOGE("Unable to open the mixer, aborting.");
674 goto err_mixer_open;
675 }
676
677 ar->mixer_path = NULL;
678 ar->mixer_path_size = 0;
679 ar->num_mixer_paths = 0;
680
681 /* allocate space for and read current mixer settings */
682 if (alloc_mixer_state(ar) < 0)
683 goto err_mixer_state;
684
685 /* use the default XML path if none is provided */
686 if (xml_path == NULL)
687 xml_path = MIXER_XML_PATH;
688
689 file = fopen(xml_path, "r");
690
691 if (!file) {
692 ALOGE("Failed to open %s", xml_path);
693 goto err_fopen;
694 }
695
696 parser = XML_ParserCreate(NULL);
697 if (!parser) {
698 ALOGE("Failed to create XML parser");
699 goto err_parser_create;
700 }
701
702 memset(&state, 0, sizeof(state));
703 state.ar = ar;
704 XML_SetUserData(parser, &state);
705 XML_SetElementHandler(parser, start_tag, end_tag);
706
707 for (;;) {
708 buf = XML_GetBuffer(parser, BUF_SIZE);
709 if (buf == NULL)
710 goto err_parse;
711
712 bytes_read = fread(buf, 1, BUF_SIZE, file);
713 if (bytes_read < 0)
714 goto err_parse;
715
716 if (XML_ParseBuffer(parser, bytes_read,
717 bytes_read == 0) == XML_STATUS_ERROR) {
718 ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
719 goto err_parse;
720 }
721
722 if (bytes_read == 0)
723 break;
724 }
725
726 /* apply the initial mixer values, and save them so we can reset the
727 mixer to the original values */
728 audio_route_update_mixer(ar);
729 save_mixer_state(ar);
730
731 XML_ParserFree(parser);
732 fclose(file);
733 return ar;
734
735 err_parse:
736 XML_ParserFree(parser);
737 err_parser_create:
738 fclose(file);
739 err_fopen:
740 free_mixer_state(ar);
741 err_mixer_state:
742 mixer_close(ar->mixer);
743 err_mixer_open:
744 free(ar);
745 ar = NULL;
746 err_calloc:
747 return NULL;
748 }
749
audio_route_free(struct audio_route * ar)750 void audio_route_free(struct audio_route *ar)
751 {
752 free_mixer_state(ar);
753 mixer_close(ar->mixer);
754 free(ar);
755 }
756