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