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