1 /* 2 * Copyright (C) 2015 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 #define LOG_TAG "soundtrigger" 17 /* #define LOG_NDEBUG 0 */ 18 #define LOG_NDDEBUG 0 19 20 #include <errno.h> 21 #include <stdbool.h> 22 #include <stdlib.h> 23 #include <dlfcn.h> 24 #include <pthread.h> 25 #include <unistd.h> 26 #include <log/log.h> 27 #include "audio_hw.h" 28 #include "audio_extn.h" 29 #include "platform.h" 30 #include "platform_api.h" 31 32 /*-------------------- Begin: AHAL-STHAL Interface ---------------------------*/ 33 /* 34 * Maintain the proprietary interface between AHAL and STHAL locally to avoid 35 * the compilation dependency of interface header file from STHAL. 36 */ 37 38 #define MAKE_HAL_VERSION(maj, min) ((((maj) & 0xff) << 8) | ((min) & 0xff)) 39 #define MAJOR_VERSION(ver) (((ver) & 0xff00) >> 8) 40 #define MINOR_VERSION(ver) ((ver) & 0x00ff) 41 42 /* Proprietary interface version used for compatibility with STHAL */ 43 #define STHAL_PROP_API_VERSION_1_0 MAKE_HAL_VERSION(1, 0) 44 #define STHAL_PROP_API_CURRENT_VERSION STHAL_PROP_API_VERSION_1_0 45 46 #define ST_EVENT_CONFIG_MAX_STR_VALUE 32 47 48 typedef enum { 49 ST_EVENT_SESSION_REGISTER, 50 ST_EVENT_SESSION_DEREGISTER 51 } sound_trigger_event_type_t; 52 53 typedef enum { 54 AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, 55 AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, 56 AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE, 57 AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE, 58 AUDIO_EVENT_STOP_LAB, 59 AUDIO_EVENT_SSR, 60 AUDIO_EVENT_NUM_ST_SESSIONS, 61 AUDIO_EVENT_READ_SAMPLES, 62 AUDIO_EVENT_DEVICE_CONNECT, 63 AUDIO_EVENT_DEVICE_DISCONNECT, 64 AUDIO_EVENT_SVA_EXEC_MODE, 65 AUDIO_EVENT_SVA_EXEC_MODE_STATUS, 66 AUDIO_EVENT_CAPTURE_STREAM_INACTIVE, 67 AUDIO_EVENT_CAPTURE_STREAM_ACTIVE, 68 } audio_event_type_t; 69 70 typedef enum { 71 USECASE_TYPE_PCM_PLAYBACK, 72 USECASE_TYPE_PCM_CAPTURE, 73 USECASE_TYPE_VOICE_CALL, 74 USECASE_TYPE_VOIP_CALL, 75 } audio_stream_usecase_type_t; 76 77 typedef enum { 78 SND_CARD_STATUS_OFFLINE, 79 SND_CARD_STATUS_ONLINE, 80 CPE_STATUS_OFFLINE, 81 CPE_STATUS_ONLINE, 82 SLPI_STATUS_OFFLINE, 83 SLPI_STATUS_ONLINE, 84 } ssr_event_status_t; 85 86 struct sound_trigger_session_info { 87 void* p_ses; /* opaque pointer to st_session obj */ 88 int capture_handle; 89 struct pcm *pcm; 90 struct pcm_config config; 91 }; 92 93 struct audio_read_samples_info { 94 struct sound_trigger_session_info *ses_info; 95 void *buf; 96 size_t num_bytes; 97 }; 98 99 struct audio_hal_usecase { 100 audio_stream_usecase_type_t type; 101 }; 102 103 struct sound_trigger_event_info { 104 struct sound_trigger_session_info st_ses; 105 }; 106 107 struct audio_event_info { 108 union { 109 ssr_event_status_t status; 110 int value; 111 struct sound_trigger_session_info ses_info; 112 struct audio_read_samples_info aud_info; 113 char str_value[ST_EVENT_CONFIG_MAX_STR_VALUE]; 114 struct audio_hal_usecase usecase; 115 }u; 116 }; 117 118 /* STHAL callback which is called by AHAL */ 119 typedef int (*sound_trigger_hw_call_back_t)(audio_event_type_t, 120 struct audio_event_info*); 121 122 /*---------------- End: AHAL-STHAL Interface ----------------------------------*/ 123 124 #define XSTR(x) STR(x) 125 #define STR(x) #x 126 127 #define DLSYM(handle, ptr, symbol, err) \ 128 do { \ 129 ptr = dlsym(handle, #symbol); \ 130 if (ptr == NULL) { \ 131 ALOGE("%s: dlsym %s failed . %s", __func__, #symbol, dlerror()); \ 132 err = -ENODEV; \ 133 } \ 134 } while(0) 135 136 #ifdef __LP64__ 137 #define SOUND_TRIGGER_LIBRARY_PATH "/vendor/lib64/hw/sound_trigger.primary.%s.so" 138 #else 139 #define SOUND_TRIGGER_LIBRARY_PATH "/vendor/lib/hw/sound_trigger.primary.%s.so" 140 #endif 141 142 /* 143 * Current proprietary API version used by AHAL. Queried by STHAL 144 * for compatibility check with AHAL 145 */ 146 const unsigned int sthal_prop_api_version = STHAL_PROP_API_CURRENT_VERSION; 147 148 struct sound_trigger_info { 149 struct sound_trigger_session_info st_ses; 150 bool lab_stopped; 151 struct listnode list; 152 }; 153 154 struct sound_trigger_audio_device { 155 void *lib_handle; 156 struct audio_device *adev; 157 sound_trigger_hw_call_back_t st_callback; 158 struct listnode st_ses_list; 159 pthread_mutex_t lock; 160 unsigned int sthal_prop_api_version; 161 }; 162 163 static struct sound_trigger_audio_device *st_dev; 164 165 static struct sound_trigger_info * 166 get_sound_trigger_info(int capture_handle) 167 { 168 struct sound_trigger_info *st_ses_info = NULL; 169 struct listnode *node; 170 ALOGV("%s: list empty %d capture_handle %d", __func__, 171 list_empty(&st_dev->st_ses_list), capture_handle); 172 list_for_each(node, &st_dev->st_ses_list) { 173 st_ses_info = node_to_item(node, struct sound_trigger_info , list); 174 if (st_ses_info->st_ses.capture_handle == capture_handle) 175 return st_ses_info; 176 } 177 return NULL; 178 } 179 180 static int populate_usecase(struct audio_hal_usecase *usecase, 181 struct audio_usecase *uc_info) 182 { 183 int status = 0; 184 185 switch (uc_info->type) { 186 case PCM_PLAYBACK: 187 if (uc_info->id == USECASE_AUDIO_PLAYBACK_VOIP) 188 usecase->type = USECASE_TYPE_VOIP_CALL; 189 else 190 usecase->type = USECASE_TYPE_PCM_PLAYBACK; 191 break; 192 193 case PCM_CAPTURE: 194 if (uc_info->id == USECASE_AUDIO_RECORD_VOIP) 195 usecase->type = USECASE_TYPE_VOIP_CALL; 196 else 197 usecase->type = USECASE_TYPE_PCM_CAPTURE; 198 break; 199 200 case VOICE_CALL: 201 usecase->type = USECASE_TYPE_VOICE_CALL; 202 break; 203 204 default: 205 ALOGE("%s: unsupported usecase type %d", __func__, uc_info->type); 206 status = -EINVAL; 207 } 208 return status; 209 } 210 211 static void stdev_snd_mon_cb(void * stream __unused, struct str_parms * parms) 212 { 213 if (!parms) 214 return; 215 216 audio_extn_sound_trigger_set_parameters(NULL, parms); 217 return; 218 } 219 220 int audio_hw_call_back(sound_trigger_event_type_t event, 221 struct sound_trigger_event_info* config) 222 { 223 int status = 0; 224 struct sound_trigger_info *st_ses_info; 225 226 if (!st_dev) 227 return -EINVAL; 228 229 pthread_mutex_lock(&st_dev->lock); 230 switch (event) { 231 case ST_EVENT_SESSION_REGISTER: 232 if (!config) { 233 ALOGE("%s: NULL config", __func__); 234 status = -EINVAL; 235 break; 236 } 237 st_ses_info= calloc(1, sizeof(struct sound_trigger_info )); 238 if (!st_ses_info) { 239 ALOGE("%s: st_ses_info alloc failed", __func__); 240 status = -ENOMEM; 241 break; 242 } 243 memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses)); 244 ALOGV("%s: add capture_handle %d pcm %p", __func__, 245 st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm); 246 list_add_tail(&st_dev->st_ses_list, &st_ses_info->list); 247 break; 248 249 case ST_EVENT_SESSION_DEREGISTER: 250 if (!config) { 251 ALOGE("%s: NULL config", __func__); 252 status = -EINVAL; 253 break; 254 } 255 st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle); 256 if (!st_ses_info) { 257 ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm); 258 status = -EINVAL; 259 break; 260 } 261 ALOGV("%s: remove capture_handle %d pcm %p", __func__, 262 st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm); 263 list_remove(&st_ses_info->list); 264 free(st_ses_info); 265 break; 266 default: 267 ALOGW("%s: Unknown event %d", __func__, event); 268 break; 269 } 270 pthread_mutex_unlock(&st_dev->lock); 271 return status; 272 } 273 274 int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer, 275 size_t bytes) 276 { 277 int ret = -1; 278 struct sound_trigger_info *st_info = NULL; 279 struct audio_event_info event; 280 281 if (!st_dev) 282 return ret; 283 284 if (!in->is_st_session_active) { 285 ALOGE(" %s: Sound trigger is not active", __func__); 286 goto exit; 287 } 288 if (in->standby) 289 in->standby = false; 290 291 pthread_mutex_lock(&st_dev->lock); 292 st_info = get_sound_trigger_info(in->capture_handle); 293 pthread_mutex_unlock(&st_dev->lock); 294 if (st_info) { 295 event.u.aud_info.ses_info = &st_info->st_ses; 296 event.u.aud_info.buf = buffer; 297 event.u.aud_info.num_bytes = bytes; 298 ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event); 299 } 300 301 exit: 302 if (ret) { 303 if (-ENETRESET == ret) 304 in->is_st_session_active = false; 305 memset(buffer, 0, bytes); 306 ALOGV("%s: read failed status %d - sleep", __func__, ret); 307 usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) * 308 in->config.rate)); 309 } 310 return ret; 311 } 312 313 void audio_extn_sound_trigger_stop_lab(struct stream_in *in) 314 { 315 struct sound_trigger_info *st_ses_info = NULL; 316 struct audio_event_info event; 317 318 if (!st_dev || !in || !in->is_st_session_active) 319 return; 320 321 pthread_mutex_lock(&st_dev->lock); 322 st_ses_info = get_sound_trigger_info(in->capture_handle); 323 pthread_mutex_unlock(&st_dev->lock); 324 if (st_ses_info) { 325 event.u.ses_info = st_ses_info->st_ses; 326 ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm); 327 st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event); 328 in->is_st_session_active = false; 329 } 330 } 331 332 void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in) 333 { 334 struct sound_trigger_info *st_ses_info = NULL; 335 struct listnode *node; 336 337 if (!st_dev || !in) 338 return; 339 340 pthread_mutex_lock(&st_dev->lock); 341 in->is_st_session = false; 342 ALOGV("%s: list %d capture_handle %d", __func__, 343 list_empty(&st_dev->st_ses_list), in->capture_handle); 344 list_for_each(node, &st_dev->st_ses_list) { 345 st_ses_info = node_to_item(node, struct sound_trigger_info , list); 346 if (st_ses_info->st_ses.capture_handle == in->capture_handle) { 347 in->pcm = st_ses_info->st_ses.pcm; 348 in->config = st_ses_info->st_ses.config; 349 in->channel_mask = audio_channel_in_mask_from_count(in->config.channels); 350 in->is_st_session = true; 351 in->is_st_session_active = true; 352 ALOGV("%s: capture_handle %d is sound trigger", __func__, in->capture_handle); 353 break; 354 } 355 } 356 pthread_mutex_unlock(&st_dev->lock); 357 } 358 359 void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device, 360 st_event_type_t event) 361 { 362 int device_type = -1; 363 364 if (!st_dev) 365 return; 366 367 if (snd_device >= SND_DEVICE_OUT_BEGIN && 368 snd_device < SND_DEVICE_OUT_END) { 369 device_type = PCM_PLAYBACK; 370 } else if (snd_device >= SND_DEVICE_IN_BEGIN && 371 snd_device < SND_DEVICE_IN_END) { 372 if (snd_device == SND_DEVICE_IN_CAPTURE_VI_FEEDBACK) 373 return; 374 device_type = PCM_CAPTURE; 375 } else { 376 ALOGE("%s: invalid device 0x%x, for event %d", 377 __func__, snd_device, event); 378 return; 379 } 380 381 ALOGV("%s: device 0x%x of type %d for Event %d", 382 __func__, snd_device, device_type, event); 383 if (device_type == PCM_CAPTURE) { 384 switch(event) { 385 case ST_EVENT_SND_DEVICE_FREE: 386 st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL); 387 break; 388 case ST_EVENT_SND_DEVICE_BUSY: 389 st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL); 390 break; 391 default: 392 ALOGW("%s:invalid event %d for device 0x%x", 393 __func__, event, snd_device); 394 } 395 }/*Events for output device, if required can be placed here in else*/ 396 } 397 398 void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info, 399 st_event_type_t event) 400 { 401 bool raise_event = false; 402 struct audio_event_info ev_info; 403 audio_event_type_t ev; 404 405 if (!st_dev) 406 return; 407 408 if (st_dev->sthal_prop_api_version < STHAL_PROP_API_VERSION_1_0) 409 return; 410 411 if (uc_info == NULL) { 412 ALOGE("%s: null usecase", __func__); 413 return; 414 } 415 416 bool valid_type = 417 (uc_info->type == PCM_PLAYBACK && 418 platform_snd_device_has_speaker(uc_info->out_snd_device)) || 419 (uc_info->type == PCM_CAPTURE) || 420 (uc_info->type == VOICE_CALL); 421 422 if (valid_type && platform_sound_trigger_usecase_needs_event(uc_info->id)) { 423 ALOGV("%s: uc_id %d of type %d for Event %d, with Raise=%d", 424 __func__, uc_info->id, uc_info->type, event, raise_event); 425 if (uc_info->type == PCM_CAPTURE || uc_info->type == VOICE_CALL) { 426 ev = (event == ST_EVENT_STREAM_BUSY) ? AUDIO_EVENT_CAPTURE_STREAM_ACTIVE : 427 AUDIO_EVENT_CAPTURE_STREAM_INACTIVE; 428 } else { 429 ev = (event == ST_EVENT_STREAM_BUSY) ? AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE : 430 AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE; 431 } 432 if (!populate_usecase(&ev_info.u.usecase, uc_info)) { 433 ALOGI("%s: send event %d: usecase id %d, type %d", 434 __func__, ev, uc_info->id, uc_info->type); 435 st_dev->st_callback(ev, &ev_info); 436 } 437 } 438 } 439 440 void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused, 441 struct str_parms *params) 442 { 443 struct audio_event_info event; 444 char value[32]; 445 int ret, val; 446 447 if(!st_dev || !params) { 448 ALOGE("%s: str_params NULL", __func__); 449 return; 450 } 451 452 ret = str_parms_get_str(params, "SND_CARD_STATUS", value, 453 sizeof(value)); 454 if (ret > 0) { 455 if (strstr(value, "OFFLINE")) { 456 event.u.status = SND_CARD_STATUS_OFFLINE; 457 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 458 } 459 else if (strstr(value, "ONLINE")) { 460 event.u.status = SND_CARD_STATUS_ONLINE; 461 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 462 } 463 else 464 ALOGE("%s: unknown snd_card_status", __func__); 465 } 466 467 ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value)); 468 if (ret > 0) { 469 if (strstr(value, "OFFLINE")) { 470 event.u.status = CPE_STATUS_OFFLINE; 471 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 472 } 473 else if (strstr(value, "ONLINE")) { 474 event.u.status = CPE_STATUS_ONLINE; 475 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 476 } 477 else 478 ALOGE("%s: unknown CPE status", __func__); 479 } 480 481 ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val); 482 if (ret >= 0) { 483 event.u.value = val; 484 st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event); 485 } 486 487 ret = str_parms_get_str(params, "SLPI_STATUS", value, sizeof(value)); 488 if (ret > 0) { 489 if (strstr(value, "OFFLINE")) { 490 event.u.status = SLPI_STATUS_OFFLINE; 491 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 492 } else if (strstr(value, "ONLINE")) { 493 event.u.status = SLPI_STATUS_ONLINE; 494 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 495 } else { 496 ALOGE("%s: unknown SLPI status", __func__); 497 } 498 } 499 } 500 501 int audio_extn_sound_trigger_init(struct audio_device *adev) 502 { 503 int status = 0; 504 char sound_trigger_lib[100]; 505 void *sthal_prop_api_version; 506 507 ALOGV("%s: Enter", __func__); 508 509 st_dev = (struct sound_trigger_audio_device*) 510 calloc(1, sizeof(struct sound_trigger_audio_device)); 511 if (!st_dev) { 512 ALOGE("%s: ERROR. sound trigger alloc failed", __func__); 513 return -ENOMEM; 514 } 515 516 snprintf(sound_trigger_lib, sizeof(sound_trigger_lib), 517 SOUND_TRIGGER_LIBRARY_PATH, 518 XSTR(SOUND_TRIGGER_PLATFORM_NAME)); 519 520 st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW); 521 522 if (st_dev->lib_handle == NULL) { 523 ALOGE("%s: error %s", __func__, dlerror()); 524 status = -ENODEV; 525 goto cleanup; 526 } 527 ALOGV("%s: DLOPEN successful for %s", __func__, sound_trigger_lib); 528 529 DLSYM(st_dev->lib_handle, st_dev->st_callback, sound_trigger_hw_call_back, 530 status); 531 if (status) 532 goto cleanup; 533 534 DLSYM(st_dev->lib_handle, sthal_prop_api_version, 535 sthal_prop_api_version, status); 536 if (status) { 537 st_dev->sthal_prop_api_version = 0; 538 status = 0; /* passthru for backward compability */ 539 } else { 540 st_dev->sthal_prop_api_version = *(int*)sthal_prop_api_version; 541 if (MAJOR_VERSION(st_dev->sthal_prop_api_version) != 542 MAJOR_VERSION(STHAL_PROP_API_CURRENT_VERSION)) { 543 ALOGE("%s: Incompatible API versions ahal:0x%x != sthal:0x%x", 544 __func__, STHAL_PROP_API_CURRENT_VERSION, 545 st_dev->sthal_prop_api_version); 546 goto cleanup; 547 } 548 ALOGD("%s: sthal is using proprietary API version 0x%04x", __func__, 549 st_dev->sthal_prop_api_version); 550 } 551 552 st_dev->adev = adev; 553 list_init(&st_dev->st_ses_list); 554 audio_extn_snd_mon_register_listener(st_dev, stdev_snd_mon_cb); 555 556 return 0; 557 558 cleanup: 559 if (st_dev->lib_handle) 560 dlclose(st_dev->lib_handle); 561 free(st_dev); 562 st_dev = NULL; 563 return status; 564 } 565 566 void audio_extn_sound_trigger_deinit(struct audio_device *adev) 567 { 568 ALOGV("%s: Enter", __func__); 569 if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) { 570 audio_extn_snd_mon_unregister_listener(st_dev); 571 dlclose(st_dev->lib_handle); 572 free(st_dev); 573 st_dev = NULL; 574 } 575 } 576