blob: bd5c1124bdbf155d33574b17c65c49dd0fd6e686 [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
Jon Eklund9911bc52015-04-15 15:21:53 -050035union ctl_values {
Andy Hung96247d22016-03-07 14:57:15 -080036 int *enumerated;
37 long *integer;
Jon Eklund9911bc52015-04-15 15:21:53 -050038 void *ptr;
39 unsigned char *bytes;
40};
41
Simon Wilsoncef93592013-01-15 16:35:46 -080042struct mixer_state {
43 struct mixer_ctl *ctl;
44 unsigned int num_values;
Jon Eklund9911bc52015-04-15 15:21:53 -050045 union ctl_values old_value;
46 union ctl_values new_value;
47 union ctl_values reset_value;
Simon Wilsoncef93592013-01-15 16:35:46 -080048};
49
50struct mixer_setting {
Simon Wilsonf3090fa2013-02-12 16:41:52 -080051 unsigned int ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -080052 unsigned int num_values;
Jon Eklund9911bc52015-04-15 15:21:53 -050053 unsigned int type;
54 union ctl_values value;
Simon Wilsoncef93592013-01-15 16:35:46 -080055};
56
57struct mixer_value {
Simon Wilsonf3090fa2013-02-12 16:41:52 -080058 unsigned int ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -080059 int index;
Andy Hung96247d22016-03-07 14:57:15 -080060 long value;
Simon Wilsoncef93592013-01-15 16:35:46 -080061};
62
63struct mixer_path {
64 char *name;
65 unsigned int size;
66 unsigned int length;
67 struct mixer_setting *setting;
68};
69
70struct audio_route {
71 struct mixer *mixer;
72 unsigned int num_mixer_ctls;
73 struct mixer_state *mixer_state;
74
75 unsigned int mixer_path_size;
76 unsigned int num_mixer_paths;
77 struct mixer_path *mixer_path;
78};
79
80struct config_parse_state {
81 struct audio_route *ar;
82 struct mixer_path *path;
83 int level;
84};
85
86/* path functions */
87
Eric Laurentc4b22662014-04-21 09:41:20 -070088static bool is_supported_ctl_type(enum mixer_ctl_type type)
Eric Laurentc5b5f6b2013-12-18 09:58:04 -080089{
90 switch (type) {
91 case MIXER_CTL_TYPE_BOOL:
92 case MIXER_CTL_TYPE_INT:
93 case MIXER_CTL_TYPE_ENUM:
Jon Eklund9911bc52015-04-15 15:21:53 -050094 case MIXER_CTL_TYPE_BYTE:
Eric Laurentc5b5f6b2013-12-18 09:58:04 -080095 return true;
96 default:
97 return false;
98 }
99}
100
Andy Hung96247d22016-03-07 14:57:15 -0800101/* as they match in alsa */
102static size_t sizeof_ctl_type(enum mixer_ctl_type type) {
103 switch (type) {
104 case MIXER_CTL_TYPE_BOOL:
105 case MIXER_CTL_TYPE_INT:
106 return sizeof(long);
107 case MIXER_CTL_TYPE_ENUM:
108 return sizeof(int);
109 case MIXER_CTL_TYPE_BYTE:
110 return sizeof(unsigned char);
111 case MIXER_CTL_TYPE_INT64:
112 case MIXER_CTL_TYPE_IEC958:
113 case MIXER_CTL_TYPE_UNKNOWN:
114 default:
115 LOG_ALWAYS_FATAL("Unsupported mixer ctl type: %d, check type before calling", (int)type);
116 return 0;
117 }
118}
119
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800120static inline struct mixer_ctl *index_to_ctl(struct audio_route *ar,
121 unsigned int ctl_index)
122{
123 return ar->mixer_state[ctl_index].ctl;
124}
125
Glenn Kasten45db1692016-03-24 11:43:42 -0700126#if 0
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800127static void path_print(struct audio_route *ar, struct mixer_path *path)
Simon Wilsoncef93592013-01-15 16:35:46 -0800128{
129 unsigned int i;
130 unsigned int j;
131
132 ALOGE("Path: %s, length: %d", path->name, path->length);
133 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800134 struct mixer_ctl *ctl = index_to_ctl(ar, path->setting[i].ctl_index);
135
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700136 ALOGE(" id=%d: ctl=%s", i, mixer_ctl_get_name(ctl));
Jon Eklund9911bc52015-04-15 15:21:53 -0500137 if (mixer_ctl_get_type(ctl) == MIXER_CTL_TYPE_BYTE) {
138 for (j = 0; j < path->setting[i].num_values; j++)
139 ALOGE(" id=%d value=0x%02x", j, path->setting[i].value.bytes[j]);
Andy Hung96247d22016-03-07 14:57:15 -0800140 } else if (mixer_ctl_get_type(ctl) == MIXER_CTL_TYPE_ENUM) {
141 for (j = 0; j < path->setting[i].num_values; j++)
142 ALOGE(" id=%d value=%d", j, path->setting[i].value.enumerated[j]);
Jon Eklund9911bc52015-04-15 15:21:53 -0500143 } else {
144 for (j = 0; j < path->setting[i].num_values; j++)
Andy Hung96247d22016-03-07 14:57:15 -0800145 ALOGE(" id=%d value=%ld", j, path->setting[i].value.integer[j]);
Jon Eklund9911bc52015-04-15 15:21:53 -0500146 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800147 }
148}
Glenn Kasten45db1692016-03-24 11:43:42 -0700149#endif
Simon Wilsoncef93592013-01-15 16:35:46 -0800150
151static void path_free(struct audio_route *ar)
152{
153 unsigned int i;
154
155 for (i = 0; i < ar->num_mixer_paths; i++) {
156 if (ar->mixer_path[i].name)
157 free(ar->mixer_path[i].name);
158 if (ar->mixer_path[i].setting) {
Jon Eklund9911bc52015-04-15 15:21:53 -0500159 if (ar->mixer_path[i].setting->value.ptr)
160 free(ar->mixer_path[i].setting->value.ptr);
Simon Wilsoncef93592013-01-15 16:35:46 -0800161 free(ar->mixer_path[i].setting);
162 }
163 }
164 free(ar->mixer_path);
Glenn Kasten45db1692016-03-24 11:43:42 -0700165 ar->mixer_path = NULL;
166 ar->mixer_path_size = 0;
Simon Wilsoncef93592013-01-15 16:35:46 -0800167}
168
169static struct mixer_path *path_get_by_name(struct audio_route *ar,
170 const char *name)
171{
172 unsigned int i;
173
174 for (i = 0; i < ar->num_mixer_paths; i++)
175 if (strcmp(ar->mixer_path[i].name, name) == 0)
176 return &ar->mixer_path[i];
177
178 return NULL;
179}
180
181static struct mixer_path *path_create(struct audio_route *ar, const char *name)
182{
183 struct mixer_path *new_mixer_path = NULL;
184
185 if (path_get_by_name(ar, name)) {
186 ALOGE("Path name '%s' already exists", name);
187 return NULL;
188 }
189
190 /* check if we need to allocate more space for mixer paths */
191 if (ar->mixer_path_size <= ar->num_mixer_paths) {
192 if (ar->mixer_path_size == 0)
193 ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
194 else
195 ar->mixer_path_size *= 2;
196
197 new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
198 sizeof(struct mixer_path));
199 if (new_mixer_path == NULL) {
200 ALOGE("Unable to allocate more paths");
201 return NULL;
202 } else {
203 ar->mixer_path = new_mixer_path;
204 }
205 }
206
207 /* initialise the new mixer path */
208 ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
209 ar->mixer_path[ar->num_mixer_paths].size = 0;
210 ar->mixer_path[ar->num_mixer_paths].length = 0;
211 ar->mixer_path[ar->num_mixer_paths].setting = NULL;
212
213 /* return the mixer path just added, then increment number of them */
214 return &ar->mixer_path[ar->num_mixer_paths++];
215}
216
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800217static int find_ctl_index_in_path(struct mixer_path *path,
218 unsigned int ctl_index)
Simon Wilsoncef93592013-01-15 16:35:46 -0800219{
220 unsigned int i;
221
222 for (i = 0; i < path->length; i++)
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800223 if (path->setting[i].ctl_index == ctl_index)
Simon Wilsoncef93592013-01-15 16:35:46 -0800224 return i;
225
226 return -1;
227}
228
229static int alloc_path_setting(struct mixer_path *path)
230{
231 struct mixer_setting *new_path_setting;
232 int path_index;
233
234 /* check if we need to allocate more space for path settings */
235 if (path->size <= path->length) {
236 if (path->size == 0)
237 path->size = INITIAL_MIXER_PATH_SIZE;
238 else
239 path->size *= 2;
240
241 new_path_setting = realloc(path->setting,
242 path->size * sizeof(struct mixer_setting));
243 if (new_path_setting == NULL) {
244 ALOGE("Unable to allocate more path settings");
245 return -1;
246 } else {
247 path->setting = new_path_setting;
248 }
249 }
250
251 path_index = path->length;
252 path->length++;
253
254 return path_index;
255}
256
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800257static int path_add_setting(struct audio_route *ar, struct mixer_path *path,
Simon Wilsoncef93592013-01-15 16:35:46 -0800258 struct mixer_setting *setting)
259{
Simon Wilsoncef93592013-01-15 16:35:46 -0800260 int path_index;
261
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800262 if (find_ctl_index_in_path(path, setting->ctl_index) != -1) {
263 struct mixer_ctl *ctl = index_to_ctl(ar, setting->ctl_index);
264
Simon Wilsoncef93592013-01-15 16:35:46 -0800265 ALOGE("Control '%s' already exists in path '%s'",
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800266 mixer_ctl_get_name(ctl), path->name);
Simon Wilsoncef93592013-01-15 16:35:46 -0800267 return -1;
268 }
269
Andy Hung96247d22016-03-07 14:57:15 -0800270 if (!is_supported_ctl_type(setting->type)) {
271 ALOGE("unsupported type %d", (int)setting->type);
272 return -1;
273 }
274
Simon Wilsoncef93592013-01-15 16:35:46 -0800275 path_index = alloc_path_setting(path);
276 if (path_index < 0)
277 return -1;
278
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800279 path->setting[path_index].ctl_index = setting->ctl_index;
Jon Eklund9911bc52015-04-15 15:21:53 -0500280 path->setting[path_index].type = setting->type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800281 path->setting[path_index].num_values = setting->num_values;
Jon Eklund9911bc52015-04-15 15:21:53 -0500282
Andy Hung96247d22016-03-07 14:57:15 -0800283 size_t value_sz = sizeof_ctl_type(setting->type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500284
Andy Hung96247d22016-03-07 14:57:15 -0800285 path->setting[path_index].value.ptr = calloc(setting->num_values, value_sz);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700286 /* copy all values */
Jon Eklund9911bc52015-04-15 15:21:53 -0500287 memcpy(path->setting[path_index].value.ptr, setting->value.ptr,
288 setting->num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800289
290 return 0;
291}
292
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800293static int path_add_value(struct audio_route *ar, struct mixer_path *path,
Simon Wilsoncef93592013-01-15 16:35:46 -0800294 struct mixer_value *mixer_value)
295{
296 unsigned int i;
297 int path_index;
298 unsigned int num_values;
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800299 struct mixer_ctl *ctl;
Simon Wilsoncef93592013-01-15 16:35:46 -0800300
301 /* Check that mixer value index is within range */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800302 ctl = index_to_ctl(ar, mixer_value->ctl_index);
303 num_values = mixer_ctl_get_num_values(ctl);
Simon Wilsoncef93592013-01-15 16:35:46 -0800304 if (mixer_value->index >= (int)num_values) {
305 ALOGE("mixer index %d is out of range for '%s'", mixer_value->index,
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800306 mixer_ctl_get_name(ctl));
Simon Wilsoncef93592013-01-15 16:35:46 -0800307 return -1;
308 }
309
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800310 path_index = find_ctl_index_in_path(path, mixer_value->ctl_index);
Simon Wilsoncef93592013-01-15 16:35:46 -0800311 if (path_index < 0) {
312 /* New path */
313
Andy Hung96247d22016-03-07 14:57:15 -0800314 enum mixer_ctl_type type = mixer_ctl_get_type(ctl);
315 if (!is_supported_ctl_type(type)) {
316 ALOGE("unsupported type %d", (int)type);
317 return -1;
318 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800319 path_index = alloc_path_setting(path);
320 if (path_index < 0)
321 return -1;
322
323 /* initialise the new path setting */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800324 path->setting[path_index].ctl_index = mixer_value->ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -0800325 path->setting[path_index].num_values = num_values;
Andy Hung96247d22016-03-07 14:57:15 -0800326 path->setting[path_index].type = type;
Jon Eklund9911bc52015-04-15 15:21:53 -0500327
Andy Hung96247d22016-03-07 14:57:15 -0800328 size_t value_sz = sizeof_ctl_type(type);
329 path->setting[path_index].value.ptr = calloc(num_values, value_sz);
Jon Eklund9911bc52015-04-15 15:21:53 -0500330 if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE)
331 path->setting[path_index].value.bytes[0] = mixer_value->value;
Andy Hung96247d22016-03-07 14:57:15 -0800332 else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM)
333 path->setting[path_index].value.enumerated[0] = mixer_value->value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500334 else
335 path->setting[path_index].value.integer[0] = mixer_value->value;
Simon Wilsoncef93592013-01-15 16:35:46 -0800336 }
337
338 if (mixer_value->index == -1) {
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700339 /* set all values the same */
Jon Eklund9911bc52015-04-15 15:21:53 -0500340 if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE) {
341 for (i = 0; i < num_values; i++)
342 path->setting[path_index].value.bytes[i] = mixer_value->value;
Andy Hung96247d22016-03-07 14:57:15 -0800343 } else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM) {
344 for (i = 0; i < num_values; i++)
345 path->setting[path_index].value.enumerated[i] = mixer_value->value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500346 } else {
347 for (i = 0; i < num_values; i++)
348 path->setting[path_index].value.integer[i] = mixer_value->value;
349 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800350 } else {
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700351 /* set only one value */
Jon Eklund9911bc52015-04-15 15:21:53 -0500352 if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE)
353 path->setting[path_index].value.bytes[mixer_value->index] = mixer_value->value;
Andy Hung96247d22016-03-07 14:57:15 -0800354 else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM)
355 path->setting[path_index].value.enumerated[mixer_value->index] = mixer_value->value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500356 else
357 path->setting[path_index].value.integer[mixer_value->index] = mixer_value->value;
Simon Wilsoncef93592013-01-15 16:35:46 -0800358 }
359
360 return 0;
361}
362
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800363static int path_add_path(struct audio_route *ar, struct mixer_path *path,
364 struct mixer_path *sub_path)
Simon Wilsoncef93592013-01-15 16:35:46 -0800365{
366 unsigned int i;
367
368 for (i = 0; i < sub_path->length; i++)
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800369 if (path_add_setting(ar, path, &sub_path->setting[i]) < 0)
Simon Wilsoncef93592013-01-15 16:35:46 -0800370 return -1;
371
372 return 0;
373}
374
375static int path_apply(struct audio_route *ar, struct mixer_path *path)
376{
377 unsigned int i;
Simon Wilsoncef93592013-01-15 16:35:46 -0800378 unsigned int ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800379 struct mixer_ctl *ctl;
380 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800381
382 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800383 ctl_index = path->setting[i].ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800384 ctl = index_to_ctl(ar, ctl_index);
385 type = mixer_ctl_get_type(ctl);
386 if (!is_supported_ctl_type(type))
387 continue;
Andy Hung96247d22016-03-07 14:57:15 -0800388 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500389 memcpy(ar->mixer_state[ctl_index].new_value.ptr, path->setting[i].value.ptr,
390 path->setting[i].num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800391 }
392
393 return 0;
394}
395
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800396static int path_reset(struct audio_route *ar, struct mixer_path *path)
397{
398 unsigned int i;
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800399 unsigned int ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800400 struct mixer_ctl *ctl;
401 enum mixer_ctl_type type;
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800402
403 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800404 ctl_index = path->setting[i].ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800405 ctl = index_to_ctl(ar, ctl_index);
406 type = mixer_ctl_get_type(ctl);
407 if (!is_supported_ctl_type(type))
408 continue;
Andy Hung96247d22016-03-07 14:57:15 -0800409 size_t value_sz = sizeof_ctl_type(type);
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800410 /* reset the value(s) */
Jon Eklund9911bc52015-04-15 15:21:53 -0500411 memcpy(ar->mixer_state[ctl_index].new_value.ptr,
412 ar->mixer_state[ctl_index].reset_value.ptr,
413 ar->mixer_state[ctl_index].num_values * value_sz);
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800414 }
415
416 return 0;
417}
418
Simon Wilsoncef93592013-01-15 16:35:46 -0800419/* mixer helper function */
420static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
421{
422 unsigned int i;
Eric Laurentff3e6f42016-10-12 10:38:12 -0700423 unsigned int num_values = mixer_ctl_get_num_enums(ctl);
424
425 if (string == NULL) {
426 ALOGE("NULL enum value string passed to mixer_enum_string_to_value() for ctl %s",
427 mixer_ctl_get_name(ctl));
428 return 0;
429 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800430
431 /* Search the enum strings for a particular one */
Eric Laurentff3e6f42016-10-12 10:38:12 -0700432 for (i = 0; i < num_values; i++) {
Simon Wilsoncef93592013-01-15 16:35:46 -0800433 if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
434 break;
435 }
Eric Laurentff3e6f42016-10-12 10:38:12 -0700436 if (i == num_values) {
437 ALOGE("unknown enum value string %s for ctl %s",
438 string, mixer_ctl_get_name(ctl));
439 return 0;
440 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800441 return i;
442}
443
444static void start_tag(void *data, const XML_Char *tag_name,
445 const XML_Char **attr)
446{
447 const XML_Char *attr_name = NULL;
448 const XML_Char *attr_id = NULL;
449 const XML_Char *attr_value = NULL;
450 struct config_parse_state *state = data;
451 struct audio_route *ar = state->ar;
452 unsigned int i;
453 unsigned int ctl_index;
454 struct mixer_ctl *ctl;
Andy Hung96247d22016-03-07 14:57:15 -0800455 long value;
Simon Wilsoncef93592013-01-15 16:35:46 -0800456 unsigned int id;
457 struct mixer_value mixer_value;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800458 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800459
460 /* Get name, id and value attributes (these may be empty) */
461 for (i = 0; attr[i]; i += 2) {
462 if (strcmp(attr[i], "name") == 0)
463 attr_name = attr[i + 1];
464 if (strcmp(attr[i], "id") == 0)
465 attr_id = attr[i + 1];
466 else if (strcmp(attr[i], "value") == 0)
467 attr_value = attr[i + 1];
468 }
469
470 /* Look at tags */
471 if (strcmp(tag_name, "path") == 0) {
472 if (attr_name == NULL) {
473 ALOGE("Unnamed path!");
474 } else {
475 if (state->level == 1) {
476 /* top level path: create and stash the path */
477 state->path = path_create(ar, (char *)attr_name);
478 } else {
479 /* nested path */
480 struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800481 path_add_path(ar, state->path, sub_path);
Simon Wilsoncef93592013-01-15 16:35:46 -0800482 }
483 }
484 }
485
486 else if (strcmp(tag_name, "ctl") == 0) {
487 /* Obtain the mixer ctl and value */
488 ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
Simon Wilsond35bd632013-05-13 12:47:41 -0700489 if (ctl == NULL) {
490 ALOGE("Control '%s' doesn't exist - skipping", attr_name);
491 goto done;
492 }
493
Simon Wilsoncef93592013-01-15 16:35:46 -0800494 switch (mixer_ctl_get_type(ctl)) {
495 case MIXER_CTL_TYPE_BOOL:
496 case MIXER_CTL_TYPE_INT:
Andy Hung96247d22016-03-07 14:57:15 -0800497 value = strtol((char *)attr_value, NULL, 0);
Simon Wilsoncef93592013-01-15 16:35:46 -0800498 break;
Jon Eklund9911bc52015-04-15 15:21:53 -0500499 case MIXER_CTL_TYPE_BYTE:
500 value = (unsigned char) strtol((char *)attr_value, NULL, 16);
501 break;
Simon Wilsoncef93592013-01-15 16:35:46 -0800502 case MIXER_CTL_TYPE_ENUM:
503 value = mixer_enum_string_to_value(ctl, (char *)attr_value);
504 break;
505 default:
506 value = 0;
507 break;
508 }
509
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800510 /* locate the mixer ctl in the list */
511 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
512 if (ar->mixer_state[ctl_index].ctl == ctl)
513 break;
514 }
515
Simon Wilsoncef93592013-01-15 16:35:46 -0800516 if (state->level == 1) {
517 /* top level ctl (initial setting) */
518
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800519 type = mixer_ctl_get_type(ctl);
520 if (is_supported_ctl_type(type)) {
521 /* apply the new value */
522 if (attr_id) {
523 /* set only one value */
524 id = atoi((char *)attr_id);
525 if (id < ar->mixer_state[ctl_index].num_values)
Jon Eklund9911bc52015-04-15 15:21:53 -0500526 if (type == MIXER_CTL_TYPE_BYTE)
527 ar->mixer_state[ctl_index].new_value.bytes[id] = value;
Andy Hung96247d22016-03-07 14:57:15 -0800528 else if (type == MIXER_CTL_TYPE_ENUM)
529 ar->mixer_state[ctl_index].new_value.enumerated[id] = value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500530 else
531 ar->mixer_state[ctl_index].new_value.integer[id] = value;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800532 else
533 ALOGE("value id out of range for mixer ctl '%s'",
534 mixer_ctl_get_name(ctl));
535 } else {
536 /* set all values the same */
537 for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
Jon Eklund9911bc52015-04-15 15:21:53 -0500538 if (type == MIXER_CTL_TYPE_BYTE)
539 ar->mixer_state[ctl_index].new_value.bytes[i] = value;
Andy Hung96247d22016-03-07 14:57:15 -0800540 else if (type == MIXER_CTL_TYPE_ENUM)
541 ar->mixer_state[ctl_index].new_value.enumerated[i] = value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500542 else
543 ar->mixer_state[ctl_index].new_value.integer[i] = value;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800544 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800545 }
546 } else {
547 /* nested ctl (within a path) */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800548 mixer_value.ctl_index = ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -0800549 mixer_value.value = value;
550 if (attr_id)
551 mixer_value.index = atoi((char *)attr_id);
552 else
553 mixer_value.index = -1;
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800554 path_add_value(ar, state->path, &mixer_value);
Simon Wilsoncef93592013-01-15 16:35:46 -0800555 }
556 }
557
Simon Wilsond35bd632013-05-13 12:47:41 -0700558done:
Simon Wilsoncef93592013-01-15 16:35:46 -0800559 state->level++;
560}
561
562static void end_tag(void *data, const XML_Char *tag_name)
563{
564 struct config_parse_state *state = data;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800565 (void)tag_name;
Simon Wilsoncef93592013-01-15 16:35:46 -0800566
567 state->level--;
568}
569
570static int alloc_mixer_state(struct audio_route *ar)
571{
572 unsigned int i;
Simon Wilsoncef93592013-01-15 16:35:46 -0800573 unsigned int num_values;
574 struct mixer_ctl *ctl;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700575 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800576
577 ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
Andy Hung96247d22016-03-07 14:57:15 -0800578 ar->mixer_state = calloc(ar->num_mixer_ctls, sizeof(struct mixer_state));
Simon Wilsoncef93592013-01-15 16:35:46 -0800579 if (!ar->mixer_state)
580 return -1;
581
582 for (i = 0; i < ar->num_mixer_ctls; i++) {
583 ctl = mixer_get_ctl(ar->mixer, i);
584 num_values = mixer_ctl_get_num_values(ctl);
585
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700586 ar->mixer_state[i].ctl = ctl;
587 ar->mixer_state[i].num_values = num_values;
588
589 /* Skip unsupported types that are not supported yet in XML */
590 type = mixer_ctl_get_type(ctl);
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800591
592 if (!is_supported_ctl_type(type))
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700593 continue;
594
Andy Hung96247d22016-03-07 14:57:15 -0800595 size_t value_sz = sizeof_ctl_type(type);
596 ar->mixer_state[i].old_value.ptr = calloc(num_values, value_sz);
597 ar->mixer_state[i].new_value.ptr = calloc(num_values, value_sz);
598 ar->mixer_state[i].reset_value.ptr = calloc(num_values, value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800599
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700600 if (type == MIXER_CTL_TYPE_ENUM)
Andy Hung96247d22016-03-07 14:57:15 -0800601 ar->mixer_state[i].old_value.enumerated[0] = mixer_ctl_get_value(ctl, 0);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700602 else
Jon Eklund9911bc52015-04-15 15:21:53 -0500603 mixer_ctl_get_array(ctl, ar->mixer_state[i].old_value.ptr, num_values);
604
605 memcpy(ar->mixer_state[i].new_value.ptr, ar->mixer_state[i].old_value.ptr,
606 num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800607 }
608
609 return 0;
610}
611
612static void free_mixer_state(struct audio_route *ar)
613{
614 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800615 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800616
617 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800618 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
619 if (!is_supported_ctl_type(type))
620 continue;
621
Jon Eklund9911bc52015-04-15 15:21:53 -0500622 free(ar->mixer_state[i].old_value.ptr);
623 free(ar->mixer_state[i].new_value.ptr);
624 free(ar->mixer_state[i].reset_value.ptr);
Simon Wilsoncef93592013-01-15 16:35:46 -0800625 }
626
627 free(ar->mixer_state);
628 ar->mixer_state = NULL;
629}
630
631/* Update the mixer with any changed values */
632int audio_route_update_mixer(struct audio_route *ar)
633{
634 unsigned int i;
635 unsigned int j;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700636 struct mixer_ctl *ctl;
Simon Wilsoncef93592013-01-15 16:35:46 -0800637
638 for (i = 0; i < ar->num_mixer_ctls; i++) {
639 unsigned int num_values = ar->mixer_state[i].num_values;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700640 enum mixer_ctl_type type;
641
642 ctl = ar->mixer_state[i].ctl;
643
644 /* Skip unsupported types */
645 type = mixer_ctl_get_type(ctl);
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800646 if (!is_supported_ctl_type(type))
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700647 continue;
Simon Wilsoncef93592013-01-15 16:35:46 -0800648
649 /* if the value has changed, update the mixer */
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700650 bool changed = false;
Jon Eklund9911bc52015-04-15 15:21:53 -0500651 if (type == MIXER_CTL_TYPE_BYTE) {
652 for (j = 0; j < num_values; j++) {
653 if (ar->mixer_state[i].old_value.bytes[j] != ar->mixer_state[i].new_value.bytes[j]) {
654 changed = true;
655 break;
656 }
657 }
Andy Hung96247d22016-03-07 14:57:15 -0800658 } else if (type == MIXER_CTL_TYPE_ENUM) {
659 for (j = 0; j < num_values; j++) {
660 if (ar->mixer_state[i].old_value.enumerated[j]
661 != ar->mixer_state[i].new_value.enumerated[j]) {
662 changed = true;
663 break;
664 }
665 }
Jon Eklund9911bc52015-04-15 15:21:53 -0500666 } else {
667 for (j = 0; j < num_values; j++) {
668 if (ar->mixer_state[i].old_value.integer[j] != ar->mixer_state[i].new_value.integer[j]) {
669 changed = true;
670 break;
671 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800672 }
673 }
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700674 if (changed) {
675 if (type == MIXER_CTL_TYPE_ENUM)
Andy Hung96247d22016-03-07 14:57:15 -0800676 mixer_ctl_set_value(ctl, 0, ar->mixer_state[i].new_value.enumerated[0]);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700677 else
Jon Eklund9911bc52015-04-15 15:21:53 -0500678 mixer_ctl_set_array(ctl, ar->mixer_state[i].new_value.ptr, num_values);
679
Andy Hung96247d22016-03-07 14:57:15 -0800680 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500681 memcpy(ar->mixer_state[i].old_value.ptr, ar->mixer_state[i].new_value.ptr,
682 num_values * value_sz);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700683 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800684 }
685
686 return 0;
687}
688
689/* saves the current state of the mixer, for resetting all controls */
690static void save_mixer_state(struct audio_route *ar)
691{
692 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800693 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800694
695 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800696 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
697 if (!is_supported_ctl_type(type))
698 continue;
699
Andy Hung96247d22016-03-07 14:57:15 -0800700 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500701 memcpy(ar->mixer_state[i].reset_value.ptr, ar->mixer_state[i].new_value.ptr,
702 ar->mixer_state[i].num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800703 }
704}
705
706/* Reset the audio routes back to the initial state */
707void audio_route_reset(struct audio_route *ar)
708{
709 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800710 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800711
712 /* load all of the saved values */
713 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800714 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
715 if (!is_supported_ctl_type(type))
716 continue;
717
Andy Hung96247d22016-03-07 14:57:15 -0800718 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500719 memcpy(ar->mixer_state[i].new_value.ptr, ar->mixer_state[i].reset_value.ptr,
720 ar->mixer_state[i].num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800721 }
722}
723
724/* Apply an audio route path by name */
725int audio_route_apply_path(struct audio_route *ar, const char *name)
726{
727 struct mixer_path *path;
728
729 if (!ar) {
730 ALOGE("invalid audio_route");
731 return -1;
732 }
733
734 path = path_get_by_name(ar, name);
735 if (!path) {
736 ALOGE("unable to find path '%s'", name);
737 return -1;
738 }
739
740 path_apply(ar, path);
741
742 return 0;
743}
744
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800745/* Reset an audio route path by name */
746int audio_route_reset_path(struct audio_route *ar, const char *name)
747{
748 struct mixer_path *path;
749
750 if (!ar) {
751 ALOGE("invalid audio_route");
752 return -1;
753 }
754
755 path = path_get_by_name(ar, name);
756 if (!path) {
757 ALOGE("unable to find path '%s'", name);
758 return -1;
759 }
760
761 path_reset(ar, path);
762
763 return 0;
764}
765
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800766/*
767 * Operates on the specified path .. controls will be updated in the
768 * order listed in the XML file
769 */
770static int audio_route_update_path(struct audio_route *ar, const char *name, bool reverse)
771{
772 struct mixer_path *path;
773 int32_t i, end;
774 unsigned int j;
775
776 if (!ar) {
777 ALOGE("invalid audio_route");
778 return -1;
779 }
780
781 path = path_get_by_name(ar, name);
782 if (!path) {
783 ALOGE("unable to find path '%s'", name);
784 return -1;
785 }
786
787 i = reverse ? (path->length - 1) : 0;
788 end = reverse ? -1 : (int32_t)path->length;
789
790 while (i != end) {
791 unsigned int ctl_index;
792 enum mixer_ctl_type type;
793
794 ctl_index = path->setting[i].ctl_index;
795
796 struct mixer_state * ms = &ar->mixer_state[ctl_index];
797
798 type = mixer_ctl_get_type(ms->ctl);
799 if (!is_supported_ctl_type(type)) {
800 continue;
801 }
802
Andy Hung96247d22016-03-07 14:57:15 -0800803 size_t value_sz = sizeof_ctl_type(type);
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800804 /* if any value has changed, update the mixer */
805 for (j = 0; j < ms->num_values; j++) {
Jon Eklund9911bc52015-04-15 15:21:53 -0500806 if (type == MIXER_CTL_TYPE_BYTE) {
807 if (ms->old_value.bytes[j] != ms->new_value.bytes[j]) {
808 mixer_ctl_set_array(ms->ctl, ms->new_value.bytes, ms->num_values);
Andy Hung96247d22016-03-07 14:57:15 -0800809 memcpy(ms->old_value.bytes, ms->new_value.bytes, ms->num_values * value_sz);
Jon Eklund9911bc52015-04-15 15:21:53 -0500810 break;
811 }
Andy Hung96247d22016-03-07 14:57:15 -0800812 } else if (type == MIXER_CTL_TYPE_ENUM) {
813 if (ms->old_value.enumerated[j] != ms->new_value.enumerated[j]) {
814 mixer_ctl_set_value(ms->ctl, 0, ms->new_value.enumerated[0]);
815 memcpy(ms->old_value.enumerated, ms->new_value.enumerated,
816 ms->num_values * value_sz);
817 break;
818 }
819 } else if (ms->old_value.integer[j] != ms->new_value.integer[j]) {
820 mixer_ctl_set_array(ms->ctl, ms->new_value.integer, ms->num_values);
821 memcpy(ms->old_value.integer, ms->new_value.integer, ms->num_values * value_sz);
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800822 break;
823 }
824 }
825
826 i = reverse ? (i - 1) : (i + 1);
827 }
828 return 0;
829}
830
831int audio_route_apply_and_update_path(struct audio_route *ar, const char *name)
832{
833 if (audio_route_apply_path(ar, name) < 0) {
834 return -1;
835 }
836 return audio_route_update_path(ar, name, false /*reverse*/);
837}
838
839int audio_route_reset_and_update_path(struct audio_route *ar, const char *name)
840{
841 if (audio_route_reset_path(ar, name) < 0) {
842 return -1;
843 }
844 return audio_route_update_path(ar, name, true /*reverse*/);
845}
846
Simon Wilsoncef93592013-01-15 16:35:46 -0800847struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
848{
849 struct config_parse_state state;
850 XML_Parser parser;
851 FILE *file;
852 int bytes_read;
853 void *buf;
Simon Wilsoncef93592013-01-15 16:35:46 -0800854 struct audio_route *ar;
855
856 ar = calloc(1, sizeof(struct audio_route));
857 if (!ar)
858 goto err_calloc;
859
860 ar->mixer = mixer_open(card);
861 if (!ar->mixer) {
862 ALOGE("Unable to open the mixer, aborting.");
863 goto err_mixer_open;
864 }
865
866 ar->mixer_path = NULL;
867 ar->mixer_path_size = 0;
868 ar->num_mixer_paths = 0;
869
870 /* allocate space for and read current mixer settings */
871 if (alloc_mixer_state(ar) < 0)
872 goto err_mixer_state;
873
874 /* use the default XML path if none is provided */
875 if (xml_path == NULL)
876 xml_path = MIXER_XML_PATH;
877
878 file = fopen(xml_path, "r");
879
880 if (!file) {
881 ALOGE("Failed to open %s", xml_path);
882 goto err_fopen;
883 }
884
885 parser = XML_ParserCreate(NULL);
886 if (!parser) {
887 ALOGE("Failed to create XML parser");
888 goto err_parser_create;
889 }
890
891 memset(&state, 0, sizeof(state));
892 state.ar = ar;
893 XML_SetUserData(parser, &state);
894 XML_SetElementHandler(parser, start_tag, end_tag);
895
896 for (;;) {
897 buf = XML_GetBuffer(parser, BUF_SIZE);
898 if (buf == NULL)
899 goto err_parse;
900
901 bytes_read = fread(buf, 1, BUF_SIZE, file);
902 if (bytes_read < 0)
903 goto err_parse;
904
905 if (XML_ParseBuffer(parser, bytes_read,
906 bytes_read == 0) == XML_STATUS_ERROR) {
907 ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
908 goto err_parse;
909 }
910
911 if (bytes_read == 0)
912 break;
913 }
914
915 /* apply the initial mixer values, and save them so we can reset the
916 mixer to the original values */
917 audio_route_update_mixer(ar);
918 save_mixer_state(ar);
919
920 XML_ParserFree(parser);
921 fclose(file);
922 return ar;
923
924err_parse:
Glenn Kasten45db1692016-03-24 11:43:42 -0700925 path_free(ar);
Simon Wilsoncef93592013-01-15 16:35:46 -0800926 XML_ParserFree(parser);
927err_parser_create:
928 fclose(file);
929err_fopen:
930 free_mixer_state(ar);
931err_mixer_state:
932 mixer_close(ar->mixer);
933err_mixer_open:
934 free(ar);
935 ar = NULL;
936err_calloc:
937 return NULL;
938}
939
940void audio_route_free(struct audio_route *ar)
941{
942 free_mixer_state(ar);
943 mixer_close(ar->mixer);
Glenn Kasten45db1692016-03-24 11:43:42 -0700944 path_free(ar);
Simon Wilsoncef93592013-01-15 16:35:46 -0800945 free(ar);
946}