1 /*
2  * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above
10  *       copyright notice, this list of conditions and the following
11  *       disclaimer in the documentation and/or other materials provided
12  *       with the distribution.
13  *     * Neither the name of The Linux Foundation nor the names of its
14  *       contributors may be used to endorse or promote products derived
15  *       from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #define LOG_TAG "platform_info"
31 #define LOG_NDDEBUG 0
32 
33 #include <errno.h>
34 #include <stdio.h>
35 #include <expat.h>
36 #include <cutils/log.h>
37 #include <audio_hw.h>
38 #include "platform_api.h"
39 #include <platform.h>
40 
41 #define BUF_SIZE                    1024
42 
43 typedef enum {
44     ROOT,
45     ACDB,
46     BITWIDTH,
47     PCM_ID,
48     BACKEND_NAME,
49     INTERFACE_NAME,
50 } section_t;
51 
52 typedef void (* section_process_fn)(const XML_Char **attr);
53 
54 static void process_acdb_id(const XML_Char **attr);
55 static void process_bit_width(const XML_Char **attr);
56 static void process_pcm_id(const XML_Char **attr);
57 static void process_backend_name(const XML_Char **attr);
58 static void process_interface_name(const XML_Char **attr);
59 static void process_root(const XML_Char **attr);
60 
61 static section_process_fn section_table[] = {
62     [ROOT] = process_root,
63     [ACDB] = process_acdb_id,
64     [BITWIDTH] = process_bit_width,
65     [PCM_ID] = process_pcm_id,
66     [BACKEND_NAME] = process_backend_name,
67     [INTERFACE_NAME] = process_interface_name,
68 };
69 
70 static section_t section;
71 
72 /*
73  * <audio_platform_info>
74  * <acdb_ids>
75  * <device name="???" acdb_id="???"/>
76  * ...
77  * ...
78  * </acdb_ids>
79  * <backend_names>
80  * <device name="???" backend="???"/>
81  * ...
82  * ...
83  * </backend_names>
84  * <pcm_ids>
85  * <usecase name="???" type="in/out" id="???"/>
86  * ...
87  * ...
88  * </pcm_ids>
89  * <interface_names>
90  * <device name="Use audio device name here, not sound device name" interface="PRIMARY_I2S" codec_type="external/internal"/>
91  * ...
92  * ...
93  * </interface_names>
94  * </audio_platform_info>
95  */
96 
process_root(const XML_Char ** attr __unused)97 static void process_root(const XML_Char **attr __unused)
98 {
99 }
100 
101 /* mapping from usecase to pcm dev id */
process_pcm_id(const XML_Char ** attr)102 static void process_pcm_id(const XML_Char **attr)
103 {
104     int index;
105 
106     if (strcmp(attr[0], "name") != 0) {
107         ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
108         goto done;
109     }
110 
111     index = platform_get_usecase_index((char *)attr[1]);
112     if (index < 0) {
113         ALOGE("%s: usecase %s not found!",
114               __func__, attr[1]);
115         goto done;
116     }
117 
118     if (strcmp(attr[2], "type") != 0) {
119         ALOGE("%s: usecase type not mentioned", __func__);
120         goto done;
121     }
122 
123     int type = -1;
124 
125     if (!strcasecmp((char *)attr[3], "in")) {
126         type = 1;
127     } else if (!strcasecmp((char *)attr[3], "out")) {
128         type = 0;
129     } else {
130         ALOGE("%s: type must be IN or OUT", __func__);
131         goto done;
132     }
133 
134     if (strcmp(attr[4], "id") != 0) {
135         ALOGE("%s: usecase id not mentioned", __func__);
136         goto done;
137     }
138 
139     int id = atoi((char *)attr[5]);
140 
141     if (platform_set_usecase_pcm_id(index, type, id) < 0) {
142         ALOGE("%s: usecase %s type %d id %d was not set!",
143               __func__, attr[1], type, id);
144         goto done;
145     }
146 
147 done:
148     return;
149 }
150 
151 /* backend to be used for a device */
process_backend_name(const XML_Char ** attr)152 static void process_backend_name(const XML_Char **attr)
153 {
154     int index;
155 
156     if (strcmp(attr[0], "name") != 0) {
157         ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
158         goto done;
159     }
160 
161     index = platform_get_snd_device_index((char *)attr[1]);
162     if (index < 0) {
163         ALOGE("%s: Device %s not found, no ACDB ID set!",
164               __func__, attr[1]);
165         goto done;
166     }
167 
168     if (strcmp(attr[2], "backend") != 0) {
169         ALOGE("%s: Device %s has no backend set!",
170               __func__, attr[1]);
171         goto done;
172     }
173 
174     if (platform_set_snd_device_backend(index, attr[3]) < 0) {
175         ALOGE("%s: Device %s backend %s was not set!",
176               __func__, attr[1], attr[3]);
177         goto done;
178     }
179 
180 done:
181     return;
182 }
183 
process_acdb_id(const XML_Char ** attr)184 static void process_acdb_id(const XML_Char **attr)
185 {
186     int index;
187 
188     if (strcmp(attr[0], "name") != 0) {
189         ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
190         goto done;
191     }
192 
193     index = platform_get_snd_device_index((char *)attr[1]);
194     if (index < 0) {
195         ALOGE("%s: Device %s in platform info xml not found, no ACDB ID set!",
196               __func__, attr[1]);
197         goto done;
198     }
199 
200     if (strcmp(attr[2], "acdb_id") != 0) {
201         ALOGE("%s: Device %s in platform info xml has no acdb_id, no ACDB ID set!",
202               __func__, attr[1]);
203         goto done;
204     }
205 
206     if (platform_set_snd_device_acdb_id(index, atoi((char *)attr[3])) < 0) {
207         ALOGE("%s: Device %s, ACDB ID %d was not set!",
208               __func__, attr[1], atoi((char *)attr[3]));
209         goto done;
210     }
211 
212 done:
213     return;
214 }
215 
process_bit_width(const XML_Char ** attr)216 static void process_bit_width(const XML_Char **attr)
217 {
218     int index;
219 
220     if (strcmp(attr[0], "name") != 0) {
221         ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
222         goto done;
223     }
224 
225     index = platform_get_snd_device_index((char *)attr[1]);
226     if (index < 0) {
227         ALOGE("%s: Device %s in platform info xml not found, no ACDB ID set!",
228               __func__, attr[1]);
229         goto done;
230     }
231 
232     if (strcmp(attr[2], "bit_width") != 0) {
233         ALOGE("%s: Device %s in platform info xml has no bit_width, no ACDB ID set!",
234               __func__, attr[1]);
235         goto done;
236     }
237 
238     if (platform_set_snd_device_bit_width(index, atoi((char *)attr[3])) < 0) {
239         ALOGE("%s: Device %s, ACDB ID %d was not set!",
240               __func__, attr[1], atoi((char *)attr[3]));
241         goto done;
242     }
243 
244 done:
245     return;
246 }
247 
process_interface_name(const XML_Char ** attr)248 static void process_interface_name(const XML_Char **attr)
249 {
250     int ret;
251 
252     if (strcmp(attr[0], "name") != 0) {
253         ALOGE("%s: 'name' not found, no Audio Interface set!", __func__);
254 
255         goto done;
256     }
257 
258     if (strcmp(attr[2], "interface") != 0) {
259         ALOGE("%s: Device %s has no Audio Interface set!",
260               __func__, attr[1]);
261 
262         goto done;
263     }
264 
265     if (strcmp(attr[4], "codec_type") != 0) {
266         ALOGE("%s: Device %s has no codec type set!",
267               __func__, attr[1]);
268 
269         goto done;
270     }
271 
272     ret = platform_set_audio_device_interface((char *)attr[1], (char *)attr[3],
273                                               (char *)attr[5]);
274     if (ret < 0) {
275         ALOGE("%s: Audio Interface not set!", __func__);
276 
277         goto done;
278     }
279 
280 done:
281     return;
282 }
283 
start_tag(void * userdata __unused,const XML_Char * tag_name,const XML_Char ** attr)284 static void start_tag(void *userdata __unused, const XML_Char *tag_name,
285                       const XML_Char **attr)
286 {
287     const XML_Char              *attr_name = NULL;
288     const XML_Char              *attr_value = NULL;
289     unsigned int                i;
290 
291     if (strcmp(tag_name, "bit_width_configs") == 0) {
292         section = BITWIDTH;
293     } else if (strcmp(tag_name, "acdb_ids") == 0) {
294         section = ACDB;
295     } else if (strcmp(tag_name, "pcm_ids") == 0) {
296         section = PCM_ID;
297     } else if (strcmp(tag_name, "backend_names") == 0) {
298         section = BACKEND_NAME;
299     } else if (strcmp(tag_name, "interface_names") == 0) {
300         section = INTERFACE_NAME;
301     } else if (strcmp(tag_name, "device") == 0) {
302         if ((section != ACDB) && (section != BACKEND_NAME) && (section != BITWIDTH) &&
303             (section != INTERFACE_NAME)) {
304             ALOGE("device tag only supported for acdb/backend names/bitwitdh/interface names");
305             return;
306         }
307 
308         /* call into process function for the current section */
309         section_process_fn fn = section_table[section];
310         fn(attr);
311     } else if (strcmp(tag_name, "usecase") == 0) {
312         if (section != PCM_ID) {
313             ALOGE("usecase tag only supported with PCM_ID section");
314             return;
315         }
316 
317         section_process_fn fn = section_table[PCM_ID];
318         fn(attr);
319     }
320 
321     return;
322 }
323 
end_tag(void * userdata __unused,const XML_Char * tag_name)324 static void end_tag(void *userdata __unused, const XML_Char *tag_name)
325 {
326     if (strcmp(tag_name, "bit_width_configs") == 0) {
327         section = ROOT;
328     } else if (strcmp(tag_name, "acdb_ids") == 0) {
329         section = ROOT;
330     } else if (strcmp(tag_name, "pcm_ids") == 0) {
331         section = ROOT;
332     } else if (strcmp(tag_name, "backend_names") == 0) {
333         section = ROOT;
334     } else if (strcmp(tag_name, "interface_names") == 0) {
335         section = ROOT;
336     }
337 }
338 
platform_info_init(const char * filename)339 int platform_info_init(const char *filename)
340 {
341     XML_Parser      parser;
342     FILE            *file;
343     int             ret = 0;
344     int             bytes_read;
345     void            *buf;
346 
347     file = fopen(filename, "r");
348     section = ROOT;
349 
350     if (!file) {
351         ALOGD("%s: Failed to open %s, using defaults.",
352             __func__, filename);
353         ret = -ENODEV;
354         goto done;
355     }
356 
357     parser = XML_ParserCreate(NULL);
358     if (!parser) {
359         ALOGE("%s: Failed to create XML parser!", __func__);
360         ret = -ENODEV;
361         goto err_close_file;
362     }
363 
364     XML_SetElementHandler(parser, start_tag, end_tag);
365 
366     while (1) {
367         buf = XML_GetBuffer(parser, BUF_SIZE);
368         if (buf == NULL) {
369             ALOGE("%s: XML_GetBuffer failed", __func__);
370             ret = -ENOMEM;
371             goto err_free_parser;
372         }
373 
374         bytes_read = fread(buf, 1, BUF_SIZE, file);
375         if (bytes_read < 0) {
376             ALOGE("%s: fread failed, bytes read = %d", __func__, bytes_read);
377              ret = bytes_read;
378             goto err_free_parser;
379         }
380 
381         if (XML_ParseBuffer(parser, bytes_read,
382                             bytes_read == 0) == XML_STATUS_ERROR) {
383             ALOGE("%s: XML_ParseBuffer failed, for %s",
384                 __func__, filename);
385             ret = -EINVAL;
386             goto err_free_parser;
387         }
388 
389         if (bytes_read == 0)
390             break;
391     }
392 
393 err_free_parser:
394     XML_ParserFree(parser);
395 err_close_file:
396     fclose(file);
397 done:
398     return ret;
399 }
400