1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "platform_info"
18 #define LOG_NDDEBUG 0
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <expat.h>
23 #include <cutils/log.h>
24 #include <audio_hw.h>
25 #include "platform_api.h"
26 #include <platform.h>
27
28 typedef enum {
29 ROOT,
30 ACDB,
31 PCM_ID,
32 BACKEND_NAME,
33 CONFIG_PARAMS,
34 OPERATOR_SPECIFIC,
35 } section_t;
36
37 typedef void (* section_process_fn)(const XML_Char **attr);
38
39 static void process_acdb_id(const XML_Char **attr);
40 static void process_pcm_id(const XML_Char **attr);
41 static void process_backend_name(const XML_Char **attr);
42 static void process_config_params(const XML_Char **attr);
43 static void process_root(const XML_Char **attr);
44 static void process_operator_specific(const XML_Char **attr);
45
46 static section_process_fn section_table[] = {
47 [ROOT] = process_root,
48 [ACDB] = process_acdb_id,
49 [PCM_ID] = process_pcm_id,
50 [BACKEND_NAME] = process_backend_name,
51 [CONFIG_PARAMS] = process_config_params,
52 [OPERATOR_SPECIFIC] = process_operator_specific,
53 };
54
55 static section_t section;
56
57 struct platform_info {
58 void *platform;
59 struct str_parms *kvpairs;
60 };
61
62 static struct platform_info my_data;
63
64 /*
65 * <audio_platform_info>
66 * <acdb_ids>
67 * <device name="???" acdb_id="???"/>
68 * ...
69 * ...
70 * </acdb_ids>
71 * <backend_names>
72 * <device name="???" backend="???"/>
73 * ...
74 * ...
75 * </backend_names>
76 * <pcm_ids>
77 * <usecase name="???" type="in/out" id="???"/>
78 * ...
79 * ...
80 * </pcm_ids>
81 * <config_params>
82 * <param key="snd_card_name" value="msm8994-tomtom-mtp-snd-card"/>
83 * <param key="operator_info" value="tmus;aa;bb;cc"/>
84 * <param key="operator_info" value="sprint;xx;yy;zz"/>
85 * ...
86 * ...
87 * </config_params>
88 *
89 * <operator_specific>
90 * <device name="???" operator="???" mixer_path="???" acdb_id="???"/>
91 * ...
92 * ...
93 * </operator_specific>
94 *
95 * </audio_platform_info>
96 */
97
process_root(const XML_Char ** attr __unused)98 static void process_root(const XML_Char **attr __unused)
99 {
100 }
101
102 /* mapping from usecase to pcm dev id */
process_pcm_id(const XML_Char ** attr)103 static void process_pcm_id(const XML_Char **attr)
104 {
105 int index;
106
107 if (strcmp(attr[0], "name") != 0) {
108 ALOGE("%s: 'name' not found, no pcm_id set!", __func__);
109 goto done;
110 }
111
112 index = platform_get_usecase_index((char *)attr[1]);
113 if (index < 0) {
114 ALOGE("%s: usecase %s in %s not found!",
115 __func__, attr[1], PLATFORM_INFO_XML_PATH);
116 goto done;
117 }
118
119 if (strcmp(attr[2], "type") != 0) {
120 ALOGE("%s: usecase type not mentioned", __func__);
121 goto done;
122 }
123
124 int type = -1;
125
126 if (!strcasecmp((char *)attr[3], "in")) {
127 type = 1;
128 } else if (!strcasecmp((char *)attr[3], "out")) {
129 type = 0;
130 } else {
131 ALOGE("%s: type must be IN or OUT", __func__);
132 goto done;
133 }
134
135 if (strcmp(attr[4], "id") != 0) {
136 ALOGE("%s: usecase id not mentioned", __func__);
137 goto done;
138 }
139
140 int id = atoi((char *)attr[5]);
141
142 if (platform_set_usecase_pcm_id(index, type, id) < 0) {
143 ALOGE("%s: usecase %s in %s, type %d id %d was not set!",
144 __func__, attr[1], PLATFORM_INFO_XML_PATH, type, id);
145 goto done;
146 }
147
148 done:
149 return;
150 }
151
152 /* backend to be used for a device */
process_backend_name(const XML_Char ** attr)153 static void process_backend_name(const XML_Char **attr)
154 {
155 int index;
156 char *hw_interface = NULL;
157
158 if (strcmp(attr[0], "name") != 0) {
159 ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
160 goto done;
161 }
162
163 index = platform_get_snd_device_index((char *)attr[1]);
164 if (index < 0) {
165 ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
166 __func__, attr[1], PLATFORM_INFO_XML_PATH);
167 goto done;
168 }
169
170 if (strcmp(attr[2], "backend") != 0) {
171 ALOGE("%s: Device %s in %s has no backed set!",
172 __func__, attr[1], PLATFORM_INFO_XML_PATH);
173 goto done;
174 }
175
176 if (attr[4] != NULL) {
177 if (strcmp(attr[4], "interface") != 0) {
178 hw_interface = NULL;
179 } else {
180 hw_interface = (char *)attr[5];
181 }
182 }
183
184 if (platform_set_snd_device_backend(index, attr[3], hw_interface) < 0) {
185 ALOGE("%s: Device %s in %s, backend %s was not set!",
186 __func__, attr[1], PLATFORM_INFO_XML_PATH, attr[3]);
187 goto done;
188 }
189
190 done:
191 return;
192 }
193
process_acdb_id(const XML_Char ** attr)194 static void process_acdb_id(const XML_Char **attr)
195 {
196 int index;
197
198 if (strcmp(attr[0], "name") != 0) {
199 ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
200 goto done;
201 }
202
203 index = platform_get_snd_device_index((char *)attr[1]);
204 if (index < 0) {
205 ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
206 __func__, attr[1], PLATFORM_INFO_XML_PATH);
207 goto done;
208 }
209
210 if (strcmp(attr[2], "acdb_id") != 0) {
211 ALOGE("%s: Device %s in %s has no acdb_id, no ACDB ID set!",
212 __func__, attr[1], PLATFORM_INFO_XML_PATH);
213 goto done;
214 }
215
216 if (platform_set_snd_device_acdb_id(index, atoi((char *)attr[3])) < 0) {
217 ALOGE("%s: Device %s in %s, ACDB ID %d was not set!",
218 __func__, attr[1], PLATFORM_INFO_XML_PATH, atoi((char *)attr[3]));
219 goto done;
220 }
221
222 done:
223 return;
224 }
225
226
process_operator_specific(const XML_Char ** attr)227 static void process_operator_specific(const XML_Char **attr)
228 {
229 snd_device_t snd_device = SND_DEVICE_NONE;
230
231 if (strcmp(attr[0], "name") != 0) {
232 ALOGE("%s: 'name' not found", __func__);
233 goto done;
234 }
235
236 snd_device = platform_get_snd_device_index((char *)attr[1]);
237 if (snd_device < 0) {
238 ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
239 __func__, (char *)attr[3], PLATFORM_INFO_XML_PATH);
240 goto done;
241 }
242
243 if (strcmp(attr[2], "operator") != 0) {
244 ALOGE("%s: 'operator' not found", __func__);
245 goto done;
246 }
247
248 if (strcmp(attr[4], "mixer_path") != 0) {
249 ALOGE("%s: 'mixer_path' not found", __func__);
250 goto done;
251 }
252
253 if (strcmp(attr[6], "acdb_id") != 0) {
254 ALOGE("%s: 'acdb_id' not found", __func__);
255 goto done;
256 }
257
258 platform_add_operator_specific_device(snd_device, (char *)attr[3], (char *)attr[5], atoi((char *)attr[7]));
259
260 done:
261 return;
262 }
263
264 /* platform specific configuration key-value pairs */
process_config_params(const XML_Char ** attr)265 static void process_config_params(const XML_Char **attr)
266 {
267 if (strcmp(attr[0], "key") != 0) {
268 ALOGE("%s: 'key' not found", __func__);
269 goto done;
270 }
271
272 if (strcmp(attr[2], "value") != 0) {
273 ALOGE("%s: 'value' not found", __func__);
274 goto done;
275 }
276
277 str_parms_add_str(my_data.kvpairs, (char*)attr[1], (char*)attr[3]);
278 platform_set_parameters(my_data.platform, my_data.kvpairs);
279 done:
280 return;
281 }
282
start_tag(void * userdata __unused,const XML_Char * tag_name,const XML_Char ** attr)283 static void start_tag(void *userdata __unused, const XML_Char *tag_name,
284 const XML_Char **attr)
285 {
286 const XML_Char *attr_name = NULL;
287 const XML_Char *attr_value = NULL;
288 unsigned int i;
289
290 if (strcmp(tag_name, "acdb_ids") == 0) {
291 section = ACDB;
292 } else if (strcmp(tag_name, "pcm_ids") == 0) {
293 section = PCM_ID;
294 } else if (strcmp(tag_name, "backend_names") == 0) {
295 section = BACKEND_NAME;
296 } else if (strcmp(tag_name, "config_params") == 0) {
297 section = CONFIG_PARAMS;
298 } else if (strcmp(tag_name, "operator_specific") == 0) {
299 section = OPERATOR_SPECIFIC;
300 } else if (strcmp(tag_name, "device") == 0) {
301 if ((section != ACDB) && (section != BACKEND_NAME) && (section != OPERATOR_SPECIFIC)) {
302 ALOGE("device tag only supported for acdb/backend names");
303 return;
304 }
305
306 /* call into process function for the current section */
307 section_process_fn fn = section_table[section];
308 fn(attr);
309 } else if (strcmp(tag_name, "usecase") == 0) {
310 if (section != PCM_ID) {
311 ALOGE("usecase tag only supported with PCM_ID section");
312 return;
313 }
314
315 section_process_fn fn = section_table[PCM_ID];
316 fn(attr);
317 } else if (strcmp(tag_name, "param") == 0) {
318 if (section != CONFIG_PARAMS) {
319 ALOGE("param tag only supported with CONFIG_PARAMS section");
320 return;
321 }
322
323 section_process_fn fn = section_table[section];
324 fn(attr);
325 }
326
327 return;
328 }
329
end_tag(void * userdata __unused,const XML_Char * tag_name)330 static void end_tag(void *userdata __unused, const XML_Char *tag_name)
331 {
332 if (strcmp(tag_name, "acdb_ids") == 0) {
333 section = ROOT;
334 } else if (strcmp(tag_name, "pcm_ids") == 0) {
335 section = ROOT;
336 } else if (strcmp(tag_name, "backend_names") == 0) {
337 section = ROOT;
338 } else if (strcmp(tag_name, "config_params") == 0) {
339 section = ROOT;
340 } else if (strcmp(tag_name, "operator_specific") == 0) {
341 section = ROOT;
342 }
343 }
344
platform_info_init(const char * filename,void * platform)345 int platform_info_init(const char *filename, void *platform)
346 {
347 XML_Parser parser;
348 FILE *file;
349 int ret = 0;
350 int bytes_read;
351 void *buf;
352 static const uint32_t kBufSize = 1024;
353 char platform_info_file_name[MIXER_PATH_MAX_LENGTH]= {0};
354 section = ROOT;
355
356 if (filename == NULL) {
357 strlcpy(platform_info_file_name, PLATFORM_INFO_XML_PATH, MIXER_PATH_MAX_LENGTH);
358 } else {
359 strlcpy(platform_info_file_name, filename, MIXER_PATH_MAX_LENGTH);
360 }
361
362 ALOGV("%s: platform info file name is %s", __func__, platform_info_file_name);
363
364 file = fopen(platform_info_file_name, "r");
365
366 if (!file) {
367 ALOGD("%s: Failed to open %s, using defaults.",
368 __func__, platform_info_file_name);
369 ret = -ENODEV;
370 goto done;
371 }
372
373 parser = XML_ParserCreate(NULL);
374 if (!parser) {
375 ALOGE("%s: Failed to create XML parser!", __func__);
376 ret = -ENODEV;
377 goto err_close_file;
378 }
379
380 my_data.platform = platform;
381 my_data.kvpairs = str_parms_create();
382
383 XML_SetElementHandler(parser, start_tag, end_tag);
384
385 while (1) {
386 buf = XML_GetBuffer(parser, kBufSize);
387 if (buf == NULL) {
388 ALOGE("%s: XML_GetBuffer failed", __func__);
389 ret = -ENOMEM;
390 goto err_free_parser;
391 }
392
393 bytes_read = fread(buf, 1, kBufSize, file);
394 if (bytes_read < 0) {
395 ALOGE("%s: fread failed, bytes read = %d", __func__, bytes_read);
396 ret = bytes_read;
397 goto err_free_parser;
398 }
399
400 if (XML_ParseBuffer(parser, bytes_read,
401 bytes_read == 0) == XML_STATUS_ERROR) {
402 ALOGE("%s: XML_ParseBuffer failed, for %s",
403 __func__, platform_info_file_name);
404 ret = -EINVAL;
405 goto err_free_parser;
406 }
407
408 if (bytes_read == 0)
409 break;
410 }
411
412 err_free_parser:
413 XML_ParserFree(parser);
414 err_close_file:
415 fclose(file);
416 done:
417 return ret;
418 }
419