Home | History | Annotate | Download | only in hal
      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 
    102 static void process_root(const XML_Char **attr __unused)
    103 {
    104 }
    105 
    106 /* mapping from usecase to pcm dev id */
    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 */
    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 
    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 
    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 
    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 */
    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 
    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 
    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 
    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