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