blob: 700af61f558750844bf1710d8f78f655e115abdb [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
Mark Salyzyn4df5a2b2017-01-10 14:29:16 -080027#include <log/log.h>
Simon Wilsoncef93592013-01-15 16:35:46 -080028
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++) {
David Linbc187752016-11-11 17:43:16 -0800156 free(ar->mixer_path[i].name);
Simon Wilsoncef93592013-01-15 16:35:46 -0800157 if (ar->mixer_path[i].setting) {
panshuangquan5d8a6542017-09-01 14:26:19 +0800158 size_t j;
159 for (j = 0; j < ar->mixer_path[i].length; j++) {
160 free(ar->mixer_path[i].setting[j].value.ptr);
161 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800162 free(ar->mixer_path[i].setting);
panshuangquan5d8a6542017-09-01 14:26:19 +0800163 ar->mixer_path[i].size = 0;
164 ar->mixer_path[i].length = 0;
165 ar->mixer_path[i].setting = NULL;
Simon Wilsoncef93592013-01-15 16:35:46 -0800166 }
167 }
168 free(ar->mixer_path);
Glenn Kasten45db1692016-03-24 11:43:42 -0700169 ar->mixer_path = NULL;
170 ar->mixer_path_size = 0;
Mikhail Naganov687ea632017-02-23 13:12:36 -0800171 ar->num_mixer_paths = 0;
Simon Wilsoncef93592013-01-15 16:35:46 -0800172}
173
174static struct mixer_path *path_get_by_name(struct audio_route *ar,
175 const char *name)
176{
177 unsigned int i;
178
179 for (i = 0; i < ar->num_mixer_paths; i++)
180 if (strcmp(ar->mixer_path[i].name, name) == 0)
181 return &ar->mixer_path[i];
182
183 return NULL;
184}
185
186static struct mixer_path *path_create(struct audio_route *ar, const char *name)
187{
188 struct mixer_path *new_mixer_path = NULL;
189
190 if (path_get_by_name(ar, name)) {
191 ALOGE("Path name '%s' already exists", name);
192 return NULL;
193 }
194
195 /* check if we need to allocate more space for mixer paths */
196 if (ar->mixer_path_size <= ar->num_mixer_paths) {
197 if (ar->mixer_path_size == 0)
198 ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE;
199 else
200 ar->mixer_path_size *= 2;
201
202 new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size *
203 sizeof(struct mixer_path));
204 if (new_mixer_path == NULL) {
205 ALOGE("Unable to allocate more paths");
206 return NULL;
207 } else {
208 ar->mixer_path = new_mixer_path;
209 }
210 }
211
212 /* initialise the new mixer path */
213 ar->mixer_path[ar->num_mixer_paths].name = strdup(name);
214 ar->mixer_path[ar->num_mixer_paths].size = 0;
215 ar->mixer_path[ar->num_mixer_paths].length = 0;
216 ar->mixer_path[ar->num_mixer_paths].setting = NULL;
217
218 /* return the mixer path just added, then increment number of them */
219 return &ar->mixer_path[ar->num_mixer_paths++];
220}
221
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800222static int find_ctl_index_in_path(struct mixer_path *path,
223 unsigned int ctl_index)
Simon Wilsoncef93592013-01-15 16:35:46 -0800224{
225 unsigned int i;
226
227 for (i = 0; i < path->length; i++)
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800228 if (path->setting[i].ctl_index == ctl_index)
Simon Wilsoncef93592013-01-15 16:35:46 -0800229 return i;
230
231 return -1;
232}
233
234static int alloc_path_setting(struct mixer_path *path)
235{
236 struct mixer_setting *new_path_setting;
237 int path_index;
238
239 /* check if we need to allocate more space for path settings */
240 if (path->size <= path->length) {
241 if (path->size == 0)
242 path->size = INITIAL_MIXER_PATH_SIZE;
243 else
244 path->size *= 2;
245
246 new_path_setting = realloc(path->setting,
247 path->size * sizeof(struct mixer_setting));
248 if (new_path_setting == NULL) {
249 ALOGE("Unable to allocate more path settings");
250 return -1;
251 } else {
252 path->setting = new_path_setting;
253 }
254 }
255
256 path_index = path->length;
257 path->length++;
258
259 return path_index;
260}
261
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800262static int path_add_setting(struct audio_route *ar, struct mixer_path *path,
Simon Wilsoncef93592013-01-15 16:35:46 -0800263 struct mixer_setting *setting)
264{
Simon Wilsoncef93592013-01-15 16:35:46 -0800265 int path_index;
266
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800267 if (find_ctl_index_in_path(path, setting->ctl_index) != -1) {
268 struct mixer_ctl *ctl = index_to_ctl(ar, setting->ctl_index);
269
Simon Wilsoncef93592013-01-15 16:35:46 -0800270 ALOGE("Control '%s' already exists in path '%s'",
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800271 mixer_ctl_get_name(ctl), path->name);
Simon Wilsoncef93592013-01-15 16:35:46 -0800272 return -1;
273 }
274
Andy Hung96247d22016-03-07 14:57:15 -0800275 if (!is_supported_ctl_type(setting->type)) {
276 ALOGE("unsupported type %d", (int)setting->type);
277 return -1;
278 }
279
Simon Wilsoncef93592013-01-15 16:35:46 -0800280 path_index = alloc_path_setting(path);
281 if (path_index < 0)
282 return -1;
283
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800284 path->setting[path_index].ctl_index = setting->ctl_index;
Jon Eklund9911bc52015-04-15 15:21:53 -0500285 path->setting[path_index].type = setting->type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800286 path->setting[path_index].num_values = setting->num_values;
Jon Eklund9911bc52015-04-15 15:21:53 -0500287
Andy Hung96247d22016-03-07 14:57:15 -0800288 size_t value_sz = sizeof_ctl_type(setting->type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500289
Andy Hung96247d22016-03-07 14:57:15 -0800290 path->setting[path_index].value.ptr = calloc(setting->num_values, value_sz);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700291 /* copy all values */
Jon Eklund9911bc52015-04-15 15:21:53 -0500292 memcpy(path->setting[path_index].value.ptr, setting->value.ptr,
293 setting->num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800294
295 return 0;
296}
297
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800298static int path_add_value(struct audio_route *ar, struct mixer_path *path,
Simon Wilsoncef93592013-01-15 16:35:46 -0800299 struct mixer_value *mixer_value)
300{
301 unsigned int i;
302 int path_index;
303 unsigned int num_values;
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800304 struct mixer_ctl *ctl;
Simon Wilsoncef93592013-01-15 16:35:46 -0800305
306 /* Check that mixer value index is within range */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800307 ctl = index_to_ctl(ar, mixer_value->ctl_index);
308 num_values = mixer_ctl_get_num_values(ctl);
Simon Wilsoncef93592013-01-15 16:35:46 -0800309 if (mixer_value->index >= (int)num_values) {
310 ALOGE("mixer index %d is out of range for '%s'", mixer_value->index,
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800311 mixer_ctl_get_name(ctl));
Simon Wilsoncef93592013-01-15 16:35:46 -0800312 return -1;
313 }
314
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800315 path_index = find_ctl_index_in_path(path, mixer_value->ctl_index);
Simon Wilsoncef93592013-01-15 16:35:46 -0800316 if (path_index < 0) {
317 /* New path */
318
Andy Hung96247d22016-03-07 14:57:15 -0800319 enum mixer_ctl_type type = mixer_ctl_get_type(ctl);
320 if (!is_supported_ctl_type(type)) {
321 ALOGE("unsupported type %d", (int)type);
322 return -1;
323 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800324 path_index = alloc_path_setting(path);
325 if (path_index < 0)
326 return -1;
327
328 /* initialise the new path setting */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800329 path->setting[path_index].ctl_index = mixer_value->ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -0800330 path->setting[path_index].num_values = num_values;
Andy Hung96247d22016-03-07 14:57:15 -0800331 path->setting[path_index].type = type;
Jon Eklund9911bc52015-04-15 15:21:53 -0500332
Andy Hung96247d22016-03-07 14:57:15 -0800333 size_t value_sz = sizeof_ctl_type(type);
334 path->setting[path_index].value.ptr = calloc(num_values, value_sz);
Jon Eklund9911bc52015-04-15 15:21:53 -0500335 if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE)
336 path->setting[path_index].value.bytes[0] = mixer_value->value;
Andy Hung96247d22016-03-07 14:57:15 -0800337 else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM)
338 path->setting[path_index].value.enumerated[0] = mixer_value->value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500339 else
340 path->setting[path_index].value.integer[0] = mixer_value->value;
Simon Wilsoncef93592013-01-15 16:35:46 -0800341 }
342
343 if (mixer_value->index == -1) {
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700344 /* set all values the same */
Jon Eklund9911bc52015-04-15 15:21:53 -0500345 if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE) {
346 for (i = 0; i < num_values; i++)
347 path->setting[path_index].value.bytes[i] = mixer_value->value;
Andy Hung96247d22016-03-07 14:57:15 -0800348 } else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM) {
349 for (i = 0; i < num_values; i++)
350 path->setting[path_index].value.enumerated[i] = mixer_value->value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500351 } else {
352 for (i = 0; i < num_values; i++)
353 path->setting[path_index].value.integer[i] = mixer_value->value;
354 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800355 } else {
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700356 /* set only one value */
Jon Eklund9911bc52015-04-15 15:21:53 -0500357 if (path->setting[path_index].type == MIXER_CTL_TYPE_BYTE)
358 path->setting[path_index].value.bytes[mixer_value->index] = mixer_value->value;
Andy Hung96247d22016-03-07 14:57:15 -0800359 else if (path->setting[path_index].type == MIXER_CTL_TYPE_ENUM)
360 path->setting[path_index].value.enumerated[mixer_value->index] = mixer_value->value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500361 else
362 path->setting[path_index].value.integer[mixer_value->index] = mixer_value->value;
Simon Wilsoncef93592013-01-15 16:35:46 -0800363 }
364
365 return 0;
366}
367
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800368static int path_add_path(struct audio_route *ar, struct mixer_path *path,
369 struct mixer_path *sub_path)
Simon Wilsoncef93592013-01-15 16:35:46 -0800370{
371 unsigned int i;
372
373 for (i = 0; i < sub_path->length; i++)
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800374 if (path_add_setting(ar, path, &sub_path->setting[i]) < 0)
Simon Wilsoncef93592013-01-15 16:35:46 -0800375 return -1;
376
377 return 0;
378}
379
380static int path_apply(struct audio_route *ar, struct mixer_path *path)
381{
382 unsigned int i;
Simon Wilsoncef93592013-01-15 16:35:46 -0800383 unsigned int ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800384 struct mixer_ctl *ctl;
385 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800386
John Muire70833b2017-01-20 16:08:57 -0800387 ALOGD("Apply path: %s", path->name != NULL ? path->name : "none");
Simon Wilsoncef93592013-01-15 16:35:46 -0800388 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800389 ctl_index = path->setting[i].ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800390 ctl = index_to_ctl(ar, ctl_index);
391 type = mixer_ctl_get_type(ctl);
392 if (!is_supported_ctl_type(type))
393 continue;
Andy Hung96247d22016-03-07 14:57:15 -0800394 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500395 memcpy(ar->mixer_state[ctl_index].new_value.ptr, path->setting[i].value.ptr,
396 path->setting[i].num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800397 }
398
399 return 0;
400}
401
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800402static int path_reset(struct audio_route *ar, struct mixer_path *path)
403{
404 unsigned int i;
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800405 unsigned int ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800406 struct mixer_ctl *ctl;
407 enum mixer_ctl_type type;
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800408
John Muire70833b2017-01-20 16:08:57 -0800409 ALOGV("Reset path: %s", path->name != NULL ? path->name : "none");
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800410 for (i = 0; i < path->length; i++) {
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800411 ctl_index = path->setting[i].ctl_index;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800412 ctl = index_to_ctl(ar, ctl_index);
413 type = mixer_ctl_get_type(ctl);
414 if (!is_supported_ctl_type(type))
415 continue;
Andy Hung96247d22016-03-07 14:57:15 -0800416 size_t value_sz = sizeof_ctl_type(type);
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800417 /* reset the value(s) */
Jon Eklund9911bc52015-04-15 15:21:53 -0500418 memcpy(ar->mixer_state[ctl_index].new_value.ptr,
419 ar->mixer_state[ctl_index].reset_value.ptr,
420 ar->mixer_state[ctl_index].num_values * value_sz);
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800421 }
422
423 return 0;
424}
425
Simon Wilsoncef93592013-01-15 16:35:46 -0800426/* mixer helper function */
427static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string)
428{
429 unsigned int i;
Eric Laurentff3e6f42016-10-12 10:38:12 -0700430 unsigned int num_values = mixer_ctl_get_num_enums(ctl);
431
432 if (string == NULL) {
433 ALOGE("NULL enum value string passed to mixer_enum_string_to_value() for ctl %s",
434 mixer_ctl_get_name(ctl));
435 return 0;
436 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800437
438 /* Search the enum strings for a particular one */
Eric Laurentff3e6f42016-10-12 10:38:12 -0700439 for (i = 0; i < num_values; i++) {
Simon Wilsoncef93592013-01-15 16:35:46 -0800440 if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0)
441 break;
442 }
Eric Laurentff3e6f42016-10-12 10:38:12 -0700443 if (i == num_values) {
444 ALOGE("unknown enum value string %s for ctl %s",
445 string, mixer_ctl_get_name(ctl));
446 return 0;
447 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800448 return i;
449}
450
451static void start_tag(void *data, const XML_Char *tag_name,
452 const XML_Char **attr)
453{
454 const XML_Char *attr_name = NULL;
455 const XML_Char *attr_id = NULL;
456 const XML_Char *attr_value = NULL;
457 struct config_parse_state *state = data;
458 struct audio_route *ar = state->ar;
459 unsigned int i;
460 unsigned int ctl_index;
461 struct mixer_ctl *ctl;
Andy Hung96247d22016-03-07 14:57:15 -0800462 long value;
Simon Wilsoncef93592013-01-15 16:35:46 -0800463 unsigned int id;
464 struct mixer_value mixer_value;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800465 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800466
467 /* Get name, id and value attributes (these may be empty) */
468 for (i = 0; attr[i]; i += 2) {
469 if (strcmp(attr[i], "name") == 0)
470 attr_name = attr[i + 1];
471 if (strcmp(attr[i], "id") == 0)
472 attr_id = attr[i + 1];
473 else if (strcmp(attr[i], "value") == 0)
474 attr_value = attr[i + 1];
475 }
476
477 /* Look at tags */
478 if (strcmp(tag_name, "path") == 0) {
479 if (attr_name == NULL) {
480 ALOGE("Unnamed path!");
481 } else {
482 if (state->level == 1) {
483 /* top level path: create and stash the path */
484 state->path = path_create(ar, (char *)attr_name);
panshuangquan5d8a6542017-09-01 14:26:19 +0800485 if (state->path == NULL)
486 ALOGE("path created failed, please check the path if existed");
Simon Wilsoncef93592013-01-15 16:35:46 -0800487 } else {
488 /* nested path */
489 struct mixer_path *sub_path = path_get_by_name(ar, attr_name);
Thierry Strudelb3a35b12016-10-27 10:12:43 -0700490 if (!sub_path) {
491 ALOGE("unable to find sub path '%s'", attr_name);
panshuangquan5d8a6542017-09-01 14:26:19 +0800492 } else if (state->path != NULL) {
Thierry Strudelb3a35b12016-10-27 10:12:43 -0700493 path_add_path(ar, state->path, sub_path);
494 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800495 }
496 }
497 }
498
499 else if (strcmp(tag_name, "ctl") == 0) {
500 /* Obtain the mixer ctl and value */
501 ctl = mixer_get_ctl_by_name(ar->mixer, attr_name);
Simon Wilsond35bd632013-05-13 12:47:41 -0700502 if (ctl == NULL) {
503 ALOGE("Control '%s' doesn't exist - skipping", attr_name);
504 goto done;
505 }
506
Simon Wilsoncef93592013-01-15 16:35:46 -0800507 switch (mixer_ctl_get_type(ctl)) {
508 case MIXER_CTL_TYPE_BOOL:
509 case MIXER_CTL_TYPE_INT:
Andy Hung96247d22016-03-07 14:57:15 -0800510 value = strtol((char *)attr_value, NULL, 0);
Simon Wilsoncef93592013-01-15 16:35:46 -0800511 break;
Jon Eklund9911bc52015-04-15 15:21:53 -0500512 case MIXER_CTL_TYPE_BYTE:
513 value = (unsigned char) strtol((char *)attr_value, NULL, 16);
514 break;
Simon Wilsoncef93592013-01-15 16:35:46 -0800515 case MIXER_CTL_TYPE_ENUM:
516 value = mixer_enum_string_to_value(ctl, (char *)attr_value);
517 break;
518 default:
519 value = 0;
520 break;
521 }
522
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800523 /* locate the mixer ctl in the list */
524 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) {
525 if (ar->mixer_state[ctl_index].ctl == ctl)
526 break;
527 }
528
Simon Wilsoncef93592013-01-15 16:35:46 -0800529 if (state->level == 1) {
530 /* top level ctl (initial setting) */
531
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800532 type = mixer_ctl_get_type(ctl);
533 if (is_supported_ctl_type(type)) {
534 /* apply the new value */
535 if (attr_id) {
536 /* set only one value */
537 id = atoi((char *)attr_id);
538 if (id < ar->mixer_state[ctl_index].num_values)
Jon Eklund9911bc52015-04-15 15:21:53 -0500539 if (type == MIXER_CTL_TYPE_BYTE)
540 ar->mixer_state[ctl_index].new_value.bytes[id] = value;
Andy Hung96247d22016-03-07 14:57:15 -0800541 else if (type == MIXER_CTL_TYPE_ENUM)
542 ar->mixer_state[ctl_index].new_value.enumerated[id] = value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500543 else
544 ar->mixer_state[ctl_index].new_value.integer[id] = value;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800545 else
546 ALOGE("value id out of range for mixer ctl '%s'",
547 mixer_ctl_get_name(ctl));
548 } else {
549 /* set all values the same */
550 for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++)
Jon Eklund9911bc52015-04-15 15:21:53 -0500551 if (type == MIXER_CTL_TYPE_BYTE)
552 ar->mixer_state[ctl_index].new_value.bytes[i] = value;
Andy Hung96247d22016-03-07 14:57:15 -0800553 else if (type == MIXER_CTL_TYPE_ENUM)
554 ar->mixer_state[ctl_index].new_value.enumerated[i] = value;
Jon Eklund9911bc52015-04-15 15:21:53 -0500555 else
556 ar->mixer_state[ctl_index].new_value.integer[i] = value;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800557 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800558 }
559 } else {
560 /* nested ctl (within a path) */
Simon Wilsonf3090fa2013-02-12 16:41:52 -0800561 mixer_value.ctl_index = ctl_index;
Simon Wilsoncef93592013-01-15 16:35:46 -0800562 mixer_value.value = value;
563 if (attr_id)
564 mixer_value.index = atoi((char *)attr_id);
565 else
566 mixer_value.index = -1;
panshuangquan5d8a6542017-09-01 14:26:19 +0800567 if (state->path != NULL)
568 path_add_value(ar, state->path, &mixer_value);
Simon Wilsoncef93592013-01-15 16:35:46 -0800569 }
570 }
571
Simon Wilsond35bd632013-05-13 12:47:41 -0700572done:
Simon Wilsoncef93592013-01-15 16:35:46 -0800573 state->level++;
574}
575
576static void end_tag(void *data, const XML_Char *tag_name)
577{
578 struct config_parse_state *state = data;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800579 (void)tag_name;
Simon Wilsoncef93592013-01-15 16:35:46 -0800580
581 state->level--;
582}
583
584static int alloc_mixer_state(struct audio_route *ar)
585{
586 unsigned int i;
Simon Wilsoncef93592013-01-15 16:35:46 -0800587 unsigned int num_values;
588 struct mixer_ctl *ctl;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700589 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800590
591 ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer);
Andy Hung96247d22016-03-07 14:57:15 -0800592 ar->mixer_state = calloc(ar->num_mixer_ctls, sizeof(struct mixer_state));
Simon Wilsoncef93592013-01-15 16:35:46 -0800593 if (!ar->mixer_state)
594 return -1;
595
596 for (i = 0; i < ar->num_mixer_ctls; i++) {
597 ctl = mixer_get_ctl(ar->mixer, i);
598 num_values = mixer_ctl_get_num_values(ctl);
599
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700600 ar->mixer_state[i].ctl = ctl;
601 ar->mixer_state[i].num_values = num_values;
602
603 /* Skip unsupported types that are not supported yet in XML */
604 type = mixer_ctl_get_type(ctl);
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800605
606 if (!is_supported_ctl_type(type))
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700607 continue;
608
Andy Hung96247d22016-03-07 14:57:15 -0800609 size_t value_sz = sizeof_ctl_type(type);
610 ar->mixer_state[i].old_value.ptr = calloc(num_values, value_sz);
611 ar->mixer_state[i].new_value.ptr = calloc(num_values, value_sz);
612 ar->mixer_state[i].reset_value.ptr = calloc(num_values, value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800613
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700614 if (type == MIXER_CTL_TYPE_ENUM)
Andy Hung96247d22016-03-07 14:57:15 -0800615 ar->mixer_state[i].old_value.enumerated[0] = mixer_ctl_get_value(ctl, 0);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700616 else
Jon Eklund9911bc52015-04-15 15:21:53 -0500617 mixer_ctl_get_array(ctl, ar->mixer_state[i].old_value.ptr, num_values);
618
619 memcpy(ar->mixer_state[i].new_value.ptr, ar->mixer_state[i].old_value.ptr,
620 num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800621 }
622
623 return 0;
624}
625
626static void free_mixer_state(struct audio_route *ar)
627{
628 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800629 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800630
631 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800632 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
633 if (!is_supported_ctl_type(type))
634 continue;
635
Jon Eklund9911bc52015-04-15 15:21:53 -0500636 free(ar->mixer_state[i].old_value.ptr);
637 free(ar->mixer_state[i].new_value.ptr);
638 free(ar->mixer_state[i].reset_value.ptr);
Simon Wilsoncef93592013-01-15 16:35:46 -0800639 }
640
641 free(ar->mixer_state);
642 ar->mixer_state = NULL;
643}
644
645/* Update the mixer with any changed values */
646int audio_route_update_mixer(struct audio_route *ar)
647{
648 unsigned int i;
649 unsigned int j;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700650 struct mixer_ctl *ctl;
Simon Wilsoncef93592013-01-15 16:35:46 -0800651
652 for (i = 0; i < ar->num_mixer_ctls; i++) {
653 unsigned int num_values = ar->mixer_state[i].num_values;
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700654 enum mixer_ctl_type type;
655
656 ctl = ar->mixer_state[i].ctl;
657
658 /* Skip unsupported types */
659 type = mixer_ctl_get_type(ctl);
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800660 if (!is_supported_ctl_type(type))
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700661 continue;
Simon Wilsoncef93592013-01-15 16:35:46 -0800662
663 /* if the value has changed, update the mixer */
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700664 bool changed = false;
Jon Eklund9911bc52015-04-15 15:21:53 -0500665 if (type == MIXER_CTL_TYPE_BYTE) {
666 for (j = 0; j < num_values; j++) {
667 if (ar->mixer_state[i].old_value.bytes[j] != ar->mixer_state[i].new_value.bytes[j]) {
668 changed = true;
669 break;
670 }
671 }
Andy Hung96247d22016-03-07 14:57:15 -0800672 } else if (type == MIXER_CTL_TYPE_ENUM) {
673 for (j = 0; j < num_values; j++) {
674 if (ar->mixer_state[i].old_value.enumerated[j]
675 != ar->mixer_state[i].new_value.enumerated[j]) {
676 changed = true;
677 break;
678 }
679 }
Jon Eklund9911bc52015-04-15 15:21:53 -0500680 } else {
681 for (j = 0; j < num_values; j++) {
682 if (ar->mixer_state[i].old_value.integer[j] != ar->mixer_state[i].new_value.integer[j]) {
683 changed = true;
684 break;
685 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800686 }
687 }
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700688 if (changed) {
689 if (type == MIXER_CTL_TYPE_ENUM)
Andy Hung96247d22016-03-07 14:57:15 -0800690 mixer_ctl_set_value(ctl, 0, ar->mixer_state[i].new_value.enumerated[0]);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700691 else
Jon Eklund9911bc52015-04-15 15:21:53 -0500692 mixer_ctl_set_array(ctl, ar->mixer_state[i].new_value.ptr, num_values);
693
Andy Hung96247d22016-03-07 14:57:15 -0800694 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500695 memcpy(ar->mixer_state[i].old_value.ptr, ar->mixer_state[i].new_value.ptr,
696 num_values * value_sz);
Ravi Kumar Alamanda63b31c12013-05-24 18:02:24 -0700697 }
Simon Wilsoncef93592013-01-15 16:35:46 -0800698 }
699
700 return 0;
701}
702
703/* saves the current state of the mixer, for resetting all controls */
704static void save_mixer_state(struct audio_route *ar)
705{
706 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800707 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800708
709 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800710 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
711 if (!is_supported_ctl_type(type))
712 continue;
713
Andy Hung96247d22016-03-07 14:57:15 -0800714 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500715 memcpy(ar->mixer_state[i].reset_value.ptr, ar->mixer_state[i].new_value.ptr,
716 ar->mixer_state[i].num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800717 }
718}
719
720/* Reset the audio routes back to the initial state */
721void audio_route_reset(struct audio_route *ar)
722{
723 unsigned int i;
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800724 enum mixer_ctl_type type;
Simon Wilsoncef93592013-01-15 16:35:46 -0800725
726 /* load all of the saved values */
727 for (i = 0; i < ar->num_mixer_ctls; i++) {
Eric Laurentc5b5f6b2013-12-18 09:58:04 -0800728 type = mixer_ctl_get_type(ar->mixer_state[i].ctl);
729 if (!is_supported_ctl_type(type))
730 continue;
731
Andy Hung96247d22016-03-07 14:57:15 -0800732 size_t value_sz = sizeof_ctl_type(type);
Jon Eklund9911bc52015-04-15 15:21:53 -0500733 memcpy(ar->mixer_state[i].new_value.ptr, ar->mixer_state[i].reset_value.ptr,
734 ar->mixer_state[i].num_values * value_sz);
Simon Wilsoncef93592013-01-15 16:35:46 -0800735 }
736}
737
738/* Apply an audio route path by name */
739int audio_route_apply_path(struct audio_route *ar, const char *name)
740{
741 struct mixer_path *path;
742
743 if (!ar) {
744 ALOGE("invalid audio_route");
745 return -1;
746 }
747
748 path = path_get_by_name(ar, name);
749 if (!path) {
750 ALOGE("unable to find path '%s'", name);
751 return -1;
752 }
753
754 path_apply(ar, path);
755
756 return 0;
757}
758
Ravi Kumar Alamanda72c277e2013-02-08 07:41:07 -0800759/* Reset an audio route path by name */
760int audio_route_reset_path(struct audio_route *ar, const char *name)
761{
762 struct mixer_path *path;
763
764 if (!ar) {
765 ALOGE("invalid audio_route");
766 return -1;
767 }
768
769 path = path_get_by_name(ar, name);
770 if (!path) {
771 ALOGE("unable to find path '%s'", name);
772 return -1;
773 }
774
775 path_reset(ar, path);
776
777 return 0;
778}
779
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800780/*
781 * Operates on the specified path .. controls will be updated in the
782 * order listed in the XML file
783 */
784static int audio_route_update_path(struct audio_route *ar, const char *name, bool reverse)
785{
786 struct mixer_path *path;
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800787 unsigned int j;
788
789 if (!ar) {
790 ALOGE("invalid audio_route");
791 return -1;
792 }
793
794 path = path_get_by_name(ar, name);
795 if (!path) {
796 ALOGE("unable to find path '%s'", name);
797 return -1;
798 }
799
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800800
Ivan Lozanoeea5bc32017-12-06 13:09:08 -0800801 for (size_t i = 0; i < path->length; ++i) {
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800802 unsigned int ctl_index;
803 enum mixer_ctl_type type;
804
Ivan Lozanoeea5bc32017-12-06 13:09:08 -0800805 ctl_index = path->setting[reverse ? path->length - 1 - i : i].ctl_index;
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800806
807 struct mixer_state * ms = &ar->mixer_state[ctl_index];
808
809 type = mixer_ctl_get_type(ms->ctl);
810 if (!is_supported_ctl_type(type)) {
811 continue;
812 }
813
Andy Hung96247d22016-03-07 14:57:15 -0800814 size_t value_sz = sizeof_ctl_type(type);
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800815 /* if any value has changed, update the mixer */
816 for (j = 0; j < ms->num_values; j++) {
Jon Eklund9911bc52015-04-15 15:21:53 -0500817 if (type == MIXER_CTL_TYPE_BYTE) {
818 if (ms->old_value.bytes[j] != ms->new_value.bytes[j]) {
819 mixer_ctl_set_array(ms->ctl, ms->new_value.bytes, ms->num_values);
Andy Hung96247d22016-03-07 14:57:15 -0800820 memcpy(ms->old_value.bytes, ms->new_value.bytes, ms->num_values * value_sz);
Jon Eklund9911bc52015-04-15 15:21:53 -0500821 break;
822 }
Andy Hung96247d22016-03-07 14:57:15 -0800823 } else if (type == MIXER_CTL_TYPE_ENUM) {
824 if (ms->old_value.enumerated[j] != ms->new_value.enumerated[j]) {
825 mixer_ctl_set_value(ms->ctl, 0, ms->new_value.enumerated[0]);
826 memcpy(ms->old_value.enumerated, ms->new_value.enumerated,
827 ms->num_values * value_sz);
828 break;
829 }
830 } else if (ms->old_value.integer[j] != ms->new_value.integer[j]) {
831 mixer_ctl_set_array(ms->ctl, ms->new_value.integer, ms->num_values);
832 memcpy(ms->old_value.integer, ms->new_value.integer, ms->num_values * value_sz);
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800833 break;
834 }
835 }
Haynes Mathew Georgea8070c02014-02-04 20:49:25 -0800836 }
837 return 0;
838}
839
840int audio_route_apply_and_update_path(struct audio_route *ar, const char *name)
841{
842 if (audio_route_apply_path(ar, name) < 0) {
843 return -1;
844 }
845 return audio_route_update_path(ar, name, false /*reverse*/);
846}
847
848int audio_route_reset_and_update_path(struct audio_route *ar, const char *name)
849{
850 if (audio_route_reset_path(ar, name) < 0) {
851 return -1;
852 }
853 return audio_route_update_path(ar, name, true /*reverse*/);
854}
855
Simon Wilsoncef93592013-01-15 16:35:46 -0800856struct audio_route *audio_route_init(unsigned int card, const char *xml_path)
857{
858 struct config_parse_state state;
859 XML_Parser parser;
860 FILE *file;
861 int bytes_read;
862 void *buf;
Simon Wilsoncef93592013-01-15 16:35:46 -0800863 struct audio_route *ar;
864
865 ar = calloc(1, sizeof(struct audio_route));
866 if (!ar)
867 goto err_calloc;
868
869 ar->mixer = mixer_open(card);
870 if (!ar->mixer) {
871 ALOGE("Unable to open the mixer, aborting.");
872 goto err_mixer_open;
873 }
874
875 ar->mixer_path = NULL;
876 ar->mixer_path_size = 0;
877 ar->num_mixer_paths = 0;
878
879 /* allocate space for and read current mixer settings */
880 if (alloc_mixer_state(ar) < 0)
881 goto err_mixer_state;
882
883 /* use the default XML path if none is provided */
884 if (xml_path == NULL)
885 xml_path = MIXER_XML_PATH;
886
887 file = fopen(xml_path, "r");
888
889 if (!file) {
Kevin Rocard33d916b2017-04-18 16:06:28 -0700890 ALOGE("Failed to open %s: %s", xml_path, strerror(errno));
Simon Wilsoncef93592013-01-15 16:35:46 -0800891 goto err_fopen;
892 }
893
894 parser = XML_ParserCreate(NULL);
895 if (!parser) {
896 ALOGE("Failed to create XML parser");
897 goto err_parser_create;
898 }
899
900 memset(&state, 0, sizeof(state));
901 state.ar = ar;
902 XML_SetUserData(parser, &state);
903 XML_SetElementHandler(parser, start_tag, end_tag);
904
905 for (;;) {
906 buf = XML_GetBuffer(parser, BUF_SIZE);
907 if (buf == NULL)
908 goto err_parse;
909
910 bytes_read = fread(buf, 1, BUF_SIZE, file);
911 if (bytes_read < 0)
912 goto err_parse;
913
914 if (XML_ParseBuffer(parser, bytes_read,
915 bytes_read == 0) == XML_STATUS_ERROR) {
916 ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH);
917 goto err_parse;
918 }
919
920 if (bytes_read == 0)
921 break;
922 }
923
924 /* apply the initial mixer values, and save them so we can reset the
925 mixer to the original values */
926 audio_route_update_mixer(ar);
927 save_mixer_state(ar);
928
929 XML_ParserFree(parser);
930 fclose(file);
931 return ar;
932
933err_parse:
Glenn Kasten45db1692016-03-24 11:43:42 -0700934 path_free(ar);
Simon Wilsoncef93592013-01-15 16:35:46 -0800935 XML_ParserFree(parser);
936err_parser_create:
937 fclose(file);
938err_fopen:
939 free_mixer_state(ar);
940err_mixer_state:
941 mixer_close(ar->mixer);
942err_mixer_open:
943 free(ar);
944 ar = NULL;
945err_calloc:
946 return NULL;
947}
948
949void audio_route_free(struct audio_route *ar)
950{
951 free_mixer_state(ar);
952 mixer_close(ar->mixer);
Glenn Kasten45db1692016-03-24 11:43:42 -0700953 path_free(ar);
Simon Wilsoncef93592013-01-15 16:35:46 -0800954 free(ar);
955}