| /* |
| * Copyright (C) 2018 Knowles Electronics |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "SoundTriggerHAL" |
| #define LOG_NDEBUG 0 |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <malloc.h> |
| #include <poll.h> |
| #include <pthread.h> |
| #include <sys/ioctl.h> |
| #include <sys/prctl.h> |
| #include <log/log.h> |
| #include <cutils/uevent.h> |
| #include <cutils/properties.h> |
| #include <math.h> |
| #include <dlfcn.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <sys/timerfd.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware_legacy/power.h> |
| |
| #include "cvq_ioctl.h" |
| #include "sound_trigger_hw_iaxxx.h" |
| #include "sound_trigger_intf.h" |
| |
| #define MAX_GENERIC_SOUND_MODELS (9) |
| #define MAX_KEY_PHRASES (1) |
| #define MAX_MODELS (MAX_GENERIC_SOUND_MODELS + MAX_KEY_PHRASES) |
| |
| #define MAX_USERS (1) |
| #define MAX_BUFFER_MS (3000) |
| #define POWER_CONSUMPTION (0) // TBD |
| #define ST_HAL_VERSION (1) |
| |
| #define UEVENT_MSG_LEN (1024) |
| |
| #define OK_GOOGLE_KW_ID (0) |
| #define AMBIENT_KW_ID (1) |
| #define ENTITY_KW_ID (2) |
| #define WAKEUP_KW_ID (3) |
| #define USELESS_KW_ID (999) |
| |
| #define CVQ_ENDPOINT (IAXXX_SYSID_PLUGIN_1_OUT_EP_0) |
| #define MUSIC_BUF_ENDPOINT (IAXXX_SYSID_PLUGIN_3_OUT_EP_1) |
| |
| #define IAXXX_VQ_EVENT_STR "IAXXX_VQ_EVENT" |
| #define IAXXX_RECOVERY_EVENT_STR "IAXXX_RECOVERY_EVENT" |
| #define IAXXX_FW_DWNLD_SUCCESS_STR "IAXXX_FW_DWNLD_SUCCESS" |
| #define IAXXX_FW_CRASH_EVENT_STR "IAXXX_CRASH_EVENT" |
| |
| #define WAKE_LOCK_NAME "sthal_wake_lock" |
| |
| #define CARD_NAME "iaxxx" |
| #define SOUND_TRIGGER_MIXER_PATH_BASE "/vendor/etc/sound_trigger_mixer_paths" |
| #define SOUND_TRIGGER_MIXER_PATH_XML "/vendor/etc/sound_trigger_mixer_paths_default.xml" |
| |
| #define MAX_SND_CARD (8) |
| #define RETRY_NUMBER (40) |
| #define RETRY_US (1000000) |
| #define TUNNEL_TIMEOUT 5 |
| |
| #define SENSOR_CREATE_WAIT_TIME_IN_S (1) |
| #define SENSOR_CREATE_WAIT_MAX_COUNT (5) |
| |
| #define CHRE_CREATE_WAIT_TIME_IN_S (1) |
| #define CHRE_CREATE_WAIT_MAX_COUNT (5) |
| |
| #define FIRMWARE_READY_WAIT_TIME_IN_S (4) |
| |
| #define ST_DEVICE_HANDSET_MIC 1 |
| |
| #ifdef __LP64__ |
| #define ADNC_STRM_LIBRARY_PATH "/vendor/lib64/hw/adnc_strm.primary.default.so" |
| #else |
| #define ADNC_STRM_LIBRARY_PATH "/vendor/lib/hw/adnc_strm.primary.default.so" |
| #endif |
| |
| static struct sound_trigger_properties_extended_1_3 hw_properties = { |
| { |
| SOUND_TRIGGER_DEVICE_API_VERSION_1_3, //ST version |
| sizeof(struct sound_trigger_properties_extended_1_3) |
| }, |
| { |
| "Knowles Electronics", // implementor |
| "Continuous VoiceQ", // description |
| 0, // library version |
| // Version UUID |
| { 0x80f7dcd5, 0xbb62, 0x4816, 0xa931, {0x9c, 0xaa, 0x52, 0x5d, 0xf5, 0xc7}}, |
| MAX_MODELS, // max_sound_models |
| MAX_KEY_PHRASES, // max_key_phrases |
| MAX_USERS, // max_users |
| RECOGNITION_MODE_VOICE_TRIGGER | // recognition_mode |
| RECOGNITION_MODE_GENERIC_TRIGGER, |
| true, // capture_transition |
| MAX_BUFFER_MS, // max_capture_ms |
| true, // concurrent_capture |
| false, // trigger_in_event |
| POWER_CONSUMPTION // power_consumption_mw |
| }, |
| "", //supported arch |
| 0, // audio capability |
| }; |
| |
| struct model_info { |
| void *recognition_cookie; |
| void *sound_model_cookie; |
| sound_model_handle_t model_handle; |
| sound_trigger_uuid_t uuid; |
| recognition_callback_t recognition_callback; |
| sound_model_callback_t sound_model_callback; |
| struct sound_trigger_recognition_config *config; |
| int kw_id; |
| sound_trigger_sound_model_type_t type; |
| |
| void *data; |
| int data_sz; |
| bool is_loaded; |
| bool is_active; |
| bool is_state_query; |
| }; |
| |
| struct knowles_sound_trigger_device { |
| struct sound_trigger_hw_device device; |
| struct model_info models[MAX_MODELS]; |
| sound_trigger_uuid_t authkw_model_uuid; |
| pthread_t callback_thread; |
| pthread_t monitor_thread; |
| pthread_t transitions_thread; |
| pthread_mutex_t lock; |
| pthread_cond_t transition_cond; |
| pthread_cond_t tunnel_create; |
| pthread_cond_t sensor_create; |
| pthread_cond_t chre_create; |
| pthread_cond_t firmware_ready_cond; |
| int opened; |
| int send_sock; |
| int recv_sock; |
| |
| // Information about streaming |
| int is_streaming; |
| void *adnc_cvq_strm_lib; |
| int (*adnc_strm_open)(bool, int, int); |
| size_t (*adnc_strm_read)(long, void *, size_t); |
| int (*adnc_strm_close)(long); |
| long adnc_strm_handle[MAX_MODELS]; |
| struct timespec adnc_strm_last_read[MAX_MODELS]; |
| |
| sound_trigger_uuid_t hotword_model_uuid; |
| sound_trigger_uuid_t sensor_model_uuid; |
| sound_trigger_uuid_t ambient_model_uuid; |
| sound_trigger_uuid_t chre_model_uuid; |
| sound_trigger_uuid_t entity_model_uuid; |
| sound_trigger_uuid_t wakeup_model_uuid; |
| |
| bool is_mic_route_enabled; |
| bool is_bargein_route_enabled; |
| bool is_chre_loaded; |
| bool is_buffer_package_loaded; |
| bool is_sensor_route_enabled; |
| bool is_src_package_loaded; |
| bool is_st_hal_ready; |
| int hotword_buffer_enable; |
| int music_buffer_enable; |
| bool is_sensor_destroy_in_prog; |
| bool is_chre_destroy_in_prog; |
| |
| // conditions indicate AHAL and mic concurrency status |
| bool is_concurrent_capture; |
| bool is_con_mic_route_enabled; |
| bool is_ahal_media_recording; |
| bool is_ahal_in_voice_voip_mode; |
| bool is_ahal_voice_voip_stop; |
| bool is_ahal_voice_voip_start; |
| |
| unsigned int current_enable; |
| unsigned int recover_model_list; |
| unsigned int rx_active_count; |
| transit_case_t transit_case; |
| |
| struct audio_route *route_hdl; |
| struct mixer *mixer; |
| struct iaxxx_odsp_hw *odsp_hdl; |
| |
| void *audio_hal_handle; |
| audio_hw_call_back_t audio_hal_cb; |
| unsigned int sthal_prop_api_version; |
| |
| int snd_crd_num; |
| char mixer_path_xml[NAME_MAX_SIZE]; |
| bool fw_reset_done_by_hal; |
| |
| // sensor stop signal event |
| timer_t ss_timer; |
| bool ss_timer_created; |
| |
| // Chre stop signal event |
| timer_t chre_timer; |
| bool chre_timer_created; |
| unsigned int hotword_version; |
| }; |
| |
| /* |
| * Since there's only ever one sound_trigger_device, keep it as a global so |
| * that other people can dlopen this lib to get at the streaming audio. |
| */ |
| |
| static struct knowles_sound_trigger_device g_stdev = |
| { |
| .lock = PTHREAD_MUTEX_INITIALIZER, |
| .transition_cond = PTHREAD_COND_INITIALIZER, |
| .tunnel_create = PTHREAD_COND_INITIALIZER, |
| .sensor_create = PTHREAD_COND_INITIALIZER, |
| .chre_create = PTHREAD_COND_INITIALIZER, |
| .firmware_ready_cond = PTHREAD_COND_INITIALIZER |
| }; |
| |
| static struct timespec reset_time = {0}; |
| |
| static enum sthal_mode get_sthal_mode(struct knowles_sound_trigger_device *stdev) |
| { |
| enum sthal_mode stmode = CON_DISABLED_ST; |
| |
| if (stdev->is_ahal_in_voice_voip_mode == true) { |
| stmode = IN_CALL; |
| goto exit; |
| } |
| |
| if (stdev->is_concurrent_capture == false) { |
| if (stdev->is_ahal_media_recording == true) |
| stmode = CON_DISABLED_CAPTURE; |
| else |
| stmode = CON_DISABLED_ST; |
| goto exit; |
| } |
| |
| if (stdev->is_con_mic_route_enabled == true ) { |
| stmode = CON_ENABLED_CAPTURE_ST; |
| goto exit; |
| } else { |
| stmode = CON_ENABLED_ST; |
| goto exit; |
| } |
| |
| ALOGW("%s: Invalid ST mode, use defualt mode", __func__); |
| |
| exit: |
| //ALOGV("%s: ST mode is %d", __func__, stmode); |
| return stmode; |
| } |
| |
| static bool can_enable_chre(struct knowles_sound_trigger_device *stdev) |
| { |
| bool ret = false; |
| enum sthal_mode stm = get_sthal_mode(stdev); |
| |
| if (stm == CON_ENABLED_CAPTURE_ST || |
| stm == CON_ENABLED_ST || |
| stm == CON_DISABLED_ST) |
| ret = true; |
| return ret; |
| } |
| |
| static bool can_update_recover_list(struct knowles_sound_trigger_device *stdev) |
| { |
| bool ret = false; |
| enum sthal_mode stm = get_sthal_mode(stdev); |
| |
| if (stm == IN_CALL || stm == CON_DISABLED_CAPTURE) |
| ret = true; |
| return ret; |
| } |
| |
| static bool is_mic_controlled_by_ahal(struct knowles_sound_trigger_device *stdev) |
| { |
| bool ret = false; |
| |
| if (get_sthal_mode(stdev) == CON_ENABLED_CAPTURE_ST) |
| ret = true; |
| return ret; |
| } |
| |
| static bool check_uuid_equality(sound_trigger_uuid_t uuid1, |
| sound_trigger_uuid_t uuid2) |
| { |
| if (uuid1.timeLow != uuid2.timeLow || |
| uuid1.timeMid != uuid2.timeMid || |
| uuid1.timeHiAndVersion != uuid2.timeHiAndVersion || |
| uuid1.clockSeq != uuid2.clockSeq) { |
| return false; |
| } |
| |
| for (int i = 0; i < 6; i++) { |
| if(uuid1.node[i] != uuid2.node[i]) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool str_to_uuid(char* uuid_str, sound_trigger_uuid_t* uuid) |
| { |
| if (uuid_str == NULL) { |
| ALOGI("Invalid str_to_uuid input."); |
| return false; |
| } |
| |
| int tmp[10]; |
| if (sscanf(uuid_str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", |
| tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, |
| tmp+6, tmp+7, tmp+8, tmp+9) < 10) { |
| ALOGI("Invalid UUID, got: %s", uuid_str); |
| return false; |
| } |
| uuid->timeLow = (unsigned int)tmp[0]; |
| uuid->timeMid = (unsigned short)tmp[1]; |
| uuid->timeHiAndVersion = (unsigned short)tmp[2]; |
| uuid->clockSeq = (unsigned short)tmp[3]; |
| uuid->node[0] = (unsigned char)tmp[4]; |
| uuid->node[1] = (unsigned char)tmp[5]; |
| uuid->node[2] = (unsigned char)tmp[6]; |
| uuid->node[3] = (unsigned char)tmp[7]; |
| uuid->node[4] = (unsigned char)tmp[8]; |
| uuid->node[5] = (unsigned char)tmp[9]; |
| |
| return true; |
| } |
| |
| static int find_empty_model_slot(struct knowles_sound_trigger_device *st_dev) |
| { |
| int i = -1; |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (st_dev->models[i].is_loaded == false) |
| break; |
| } |
| |
| if (i >= MAX_MODELS) { |
| i = -1; |
| } |
| |
| return i; |
| } |
| |
| static int find_handle_for_kw_id( |
| struct knowles_sound_trigger_device *st_dev, int kw_id) |
| { |
| int i = 0; |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (kw_id == st_dev->models[i].kw_id) |
| break; |
| } |
| |
| return i; |
| } |
| |
| static int find_handle_for_uuid( |
| struct knowles_sound_trigger_device *stdev, |
| sound_trigger_uuid_t uuid) |
| { |
| int i = 0; |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (check_uuid_equality(uuid, stdev->models[i].uuid)) |
| break; |
| } |
| |
| if (i == MAX_MODELS) |
| return -1; |
| else |
| return i; |
| } |
| |
| static bool is_any_model_active(struct knowles_sound_trigger_device *stdev) { |
| int i = 0; |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active == true) { |
| break; |
| } |
| } |
| |
| if (i == MAX_MODELS) |
| return false; |
| else |
| return true; |
| } |
| |
| static bool is_any_model_loaded(struct knowles_sound_trigger_device *stdev) { |
| int i = 0; |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_loaded == true) { |
| break; |
| } |
| } |
| |
| if (i == MAX_MODELS) |
| return false; |
| else |
| return true; |
| } |
| |
| static void reg_hal_event_session( |
| struct sound_trigger_recognition_config *config, |
| sound_model_handle_t handle) |
| { |
| struct knowles_sound_trigger_device *stdev = &g_stdev; |
| struct sound_trigger_event_info event_info; |
| /* |
| * Register config and capture_handle of trigger sound model to audio hal |
| * It only register while request capturing buffer. |
| */ |
| if (config->capture_requested && stdev->audio_hal_cb) { |
| ALOGD("%s: ST_EVENT_SESSION_REGISTER capture_handle %d model %p", |
| __func__, config->capture_handle, &stdev->models[handle]); |
| event_info.st_ses.p_ses = (void *)&stdev->models[handle]; |
| event_info.st_ses.config = stdev_hotword_pcm_config; |
| event_info.st_ses.capture_handle = config->capture_handle; |
| event_info.st_ses.pcm = NULL; |
| stdev->audio_hal_cb(ST_EVENT_SESSION_REGISTER, &event_info); |
| } |
| } |
| |
| static void dereg_hal_event_session( |
| struct sound_trigger_recognition_config *config, |
| sound_model_handle_t handle) |
| { |
| struct knowles_sound_trigger_device *stdev = &g_stdev; |
| struct sound_trigger_event_info event_info; |
| /* |
| * Indicate to audio hal that streaming is stopped. |
| * Stop capturing data from STHAL. |
| */ |
| if (config->capture_requested && stdev->audio_hal_cb) { |
| ALOGD("%s: ST_EVENT_SESSION_DEREGISTER capture_handle %d model %p", |
| __func__, config->capture_handle, &stdev->models[handle]); |
| event_info.st_ses.p_ses = (void *)&stdev->models[handle]; |
| event_info.st_ses.capture_handle = config->capture_handle; |
| event_info.st_ses.pcm = NULL; |
| stdev->audio_hal_cb(ST_EVENT_SESSION_DEREGISTER, &event_info); |
| } |
| } |
| |
| |
| static char *stdev_keyphrase_event_alloc(sound_model_handle_t handle, |
| struct sound_trigger_recognition_config *config, |
| int recognition_status) |
| { |
| char *data; |
| struct sound_trigger_phrase_recognition_event *event; |
| data = (char *)calloc(1, |
| sizeof(struct sound_trigger_phrase_recognition_event)); |
| if (!data) |
| return NULL; |
| event = (struct sound_trigger_phrase_recognition_event *)data; |
| event->common.status = recognition_status; |
| event->common.type = SOUND_MODEL_TYPE_KEYPHRASE; |
| event->common.model = handle; |
| event->common.capture_available = false; |
| |
| if (config) { |
| unsigned int i; |
| |
| event->num_phrases = config->num_phrases; |
| if (event->num_phrases > SOUND_TRIGGER_MAX_PHRASES) |
| event->num_phrases = SOUND_TRIGGER_MAX_PHRASES; |
| for (i = 0; i < event->num_phrases; i++) |
| memcpy(&event->phrase_extras[i], |
| &config->phrases[i], |
| sizeof(struct sound_trigger_phrase_recognition_extra)); |
| } |
| |
| event->num_phrases = 1; |
| event->phrase_extras[0].confidence_level = 100; |
| event->phrase_extras[0].num_levels = 1; |
| event->phrase_extras[0].levels[0].level = 100; |
| event->phrase_extras[0].levels[0].user_id = 0; |
| /* |
| * Signify that all the data is comming through streaming |
| * and not through the buffer. |
| */ |
| event->common.capture_available = true; |
| event->common.capture_delay_ms = 0; |
| event->common.capture_preamble_ms = 0; |
| event->common.audio_config = AUDIO_CONFIG_INITIALIZER; |
| event->common.audio_config.sample_rate = 16000; |
| event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO; |
| event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT; |
| |
| return data; |
| } |
| |
| static char *stdev_generic_event_alloc(int model_handle, void *payload, |
| unsigned int payload_size, |
| int recognition_status) |
| { |
| char *data; |
| struct sound_trigger_generic_recognition_event *event; |
| |
| data = (char *)calloc(1, |
| sizeof(struct sound_trigger_generic_recognition_event) + |
| payload_size); |
| if (!data) { |
| ALOGE("%s: Failed to allocate memory for recog event", __func__); |
| return NULL; |
| } |
| |
| event = (struct sound_trigger_generic_recognition_event *)data; |
| event->common.status = recognition_status; |
| event->common.type = SOUND_MODEL_TYPE_GENERIC; |
| event->common.model = model_handle; |
| |
| /* |
| * Signify that all the data is comming through streaming and |
| * not through the buffer. |
| */ |
| event->common.capture_available = true; |
| event->common.audio_config = AUDIO_CONFIG_INITIALIZER; |
| event->common.audio_config.sample_rate = 16000; |
| event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO; |
| event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT; |
| |
| if (payload && payload_size > 0) { |
| event->common.data_size = payload_size; |
| event->common.data_offset = |
| sizeof(struct sound_trigger_generic_recognition_event); |
| |
| memcpy((data + event->common.data_offset), payload, payload_size); |
| } |
| |
| return data; |
| } |
| |
| static void stdev_close_term_sock(struct knowles_sound_trigger_device *stdev) |
| { |
| if (stdev->send_sock >= 0) { |
| close(stdev->send_sock); |
| stdev->send_sock = -1; |
| } |
| |
| if (stdev->recv_sock >= 0) { |
| close(stdev->recv_sock); |
| stdev->recv_sock = -1; |
| } |
| } |
| |
| static bool is_uuid_in_recover_list(struct knowles_sound_trigger_device *stdev, |
| sound_model_handle_t handle) |
| { |
| int mask = 0; |
| sound_trigger_uuid_t target_uuid = stdev->models[handle].uuid; |
| |
| if (check_uuid_equality(target_uuid, stdev->chre_model_uuid)) { |
| mask = CHRE_MASK; |
| } else if (check_uuid_equality(target_uuid, stdev->hotword_model_uuid) || |
| check_uuid_equality(target_uuid, stdev->wakeup_model_uuid)) { |
| mask = PLUGIN1_MASK; |
| } else if (check_uuid_equality(target_uuid, stdev->ambient_model_uuid) || |
| check_uuid_equality(target_uuid, stdev->entity_model_uuid)) { |
| mask = PLUGIN2_MASK; |
| } else { |
| //ALOGV("%s: Invalid uuid.", __func__); |
| } |
| |
| return (stdev->recover_model_list & mask) ? true : false; |
| } |
| |
| static void update_recover_list(struct knowles_sound_trigger_device *stdev, |
| sound_model_handle_t handle, |
| bool enable) |
| { |
| int mask = 0; |
| |
| sound_trigger_uuid_t target_uuid = stdev->models[handle].uuid; |
| |
| ALOGD("%s: handle %d enable %d", __func__, handle, enable); |
| if (check_uuid_equality(target_uuid, stdev->chre_model_uuid)) { |
| mask = CHRE_MASK; |
| } else if (check_uuid_equality(target_uuid, stdev->hotword_model_uuid) || |
| check_uuid_equality(target_uuid, stdev->wakeup_model_uuid)) { |
| mask = PLUGIN1_MASK; |
| } else if (check_uuid_equality(target_uuid, stdev->ambient_model_uuid) || |
| check_uuid_equality(target_uuid, stdev->entity_model_uuid)) { |
| mask = PLUGIN2_MASK; |
| } else { |
| //ALOGV("%s: Invalid uuid.", __func__); |
| } |
| |
| if (enable) |
| stdev->recover_model_list |= mask; |
| else |
| stdev->recover_model_list &= ~mask; |
| |
| return; |
| } |
| |
| int check_firmware_ready(struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| |
| if (stdev->is_st_hal_ready == false) { |
| struct timespec ts; |
| ALOGW("%s: ST HAL is not ready yet", __func__); |
| clock_gettime(CLOCK_REALTIME, &ts); |
| ts.tv_sec += FIRMWARE_READY_WAIT_TIME_IN_S; |
| err = pthread_cond_timedwait(&stdev->firmware_ready_cond, |
| &stdev->lock, &ts); |
| if (err == ETIMEDOUT) { |
| ALOGE("%s: WARNING: firmware downloading timed out after %ds", |
| __func__, FIRMWARE_READY_WAIT_TIME_IN_S); |
| err = -ENODEV; |
| } else if (err != 0) |
| err = -EINVAL; |
| } |
| |
| return err; |
| } |
| |
| static int check_and_setup_src_package( |
| struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| |
| if (stdev->is_src_package_loaded == false) { |
| err = setup_src_package(stdev->odsp_hdl); |
| stdev->is_src_package_loaded = true; |
| } else { |
| ALOGD("%s: SRC package is already loaded", __func__); |
| } |
| |
| return err; |
| } |
| |
| static int check_and_destroy_src_package( |
| struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| |
| if (!is_any_model_active(stdev) && stdev->is_src_package_loaded == true) { |
| err = destroy_src_package(stdev->odsp_hdl); |
| stdev->is_src_package_loaded = false; |
| } else { |
| ALOGD("%s: Can't destroy package due to active status", __func__); |
| } |
| |
| return err; |
| } |
| |
| static int check_and_setup_buffer_package( |
| struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| |
| if (stdev->is_buffer_package_loaded == false) { |
| err = setup_buffer_package(stdev->odsp_hdl); |
| stdev->is_buffer_package_loaded = true; |
| } else { |
| ALOGD("%s: Buffer package is already loaded", __func__); |
| } |
| |
| return err; |
| } |
| |
| static int check_and_destroy_buffer_package( |
| struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| |
| if (!is_any_model_active(stdev) && |
| stdev->is_buffer_package_loaded && |
| (!stdev->is_sensor_destroy_in_prog && |
| !stdev->is_sensor_route_enabled) && |
| (!stdev->is_chre_destroy_in_prog && |
| !stdev->is_chre_loaded)) { |
| |
| err = destroy_buffer_package(stdev->odsp_hdl); |
| stdev->is_buffer_package_loaded = false; |
| } else { |
| ALOGD("%s: Can't destroy package due to active status", __func__); |
| } |
| |
| return err; |
| } |
| |
| static int setup_package(struct knowles_sound_trigger_device *stdev, |
| struct model_info *model) |
| { |
| int err = 0; |
| |
| if (check_uuid_equality(model->uuid, stdev->chre_model_uuid)) { |
| if (!(stdev->current_enable & CHRE_MASK)) { |
| err = setup_chre_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to load CHRE package"); |
| } |
| } |
| stdev->current_enable = stdev->current_enable | CHRE_MASK; |
| } else if (check_uuid_equality(model->uuid, stdev->hotword_model_uuid)) { |
| if (!(stdev->current_enable & PLUGIN1_MASK)) { |
| err = setup_hotword_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to load Hotword package"); |
| } |
| } |
| err = write_model(stdev->odsp_hdl, model->data, model->data_sz, |
| model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to write Hotword model"); |
| } |
| |
| //setup model state. |
| stdev->current_enable = stdev->current_enable | HOTWORD_MASK; |
| err = set_hotword_state(stdev->odsp_hdl, stdev->current_enable); |
| if (err != 0) { |
| ALOGE("Failed to set Hotword state"); |
| } |
| } else if (check_uuid_equality(model->uuid, stdev->wakeup_model_uuid)) { |
| if (!(stdev->current_enable & PLUGIN1_MASK)) { |
| err = setup_hotword_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to load Hotword package"); |
| } |
| } |
| err = write_model(stdev->odsp_hdl, model->data, model->data_sz, |
| model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to write Wakeup model"); |
| } |
| |
| //setup model state. |
| stdev->current_enable = stdev->current_enable | WAKEUP_MASK; |
| err = set_hotword_state(stdev->odsp_hdl, stdev->current_enable); |
| if (err != 0) { |
| ALOGE("Failed to set Wakeup state"); |
| } |
| } else if (check_uuid_equality(model->uuid, stdev->ambient_model_uuid)) { |
| if (!(stdev->current_enable & PLUGIN2_MASK)) { |
| err = setup_ambient_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to load Ambient package"); |
| } |
| } else { |
| // tear down plugin2 for writing new model data. |
| err = tear_ambient_state(stdev->odsp_hdl, |
| stdev->current_enable); |
| } |
| err = write_model(stdev->odsp_hdl, model->data, |
| model->data_sz, model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to write Ambient model"); |
| } |
| |
| //setup model state. |
| stdev->current_enable = stdev->current_enable | AMBIENT_MASK; |
| err = set_ambient_state(stdev->odsp_hdl, stdev->current_enable); |
| if (err != 0) { |
| ALOGE("Failed to set Ambient state"); |
| } |
| |
| } else if (check_uuid_equality(model->uuid, stdev->entity_model_uuid)) { |
| if (!(stdev->current_enable & PLUGIN2_MASK)) { |
| err = setup_ambient_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to load Ambient package"); |
| } |
| } else { |
| // tear down plugin2 for writing new model data. |
| err = tear_ambient_state(stdev->odsp_hdl, |
| stdev->current_enable); |
| } |
| err = write_model(stdev->odsp_hdl, model->data, |
| model->data_sz, model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to write Entity model"); |
| } |
| |
| //setup model state. |
| stdev->current_enable = stdev->current_enable | ENTITY_MASK; |
| err = set_ambient_state(stdev->odsp_hdl, stdev->current_enable); |
| if (err != 0) { |
| ALOGE("Failed to set Entity state"); |
| } |
| } |
| |
| return err; |
| } |
| |
| static int setup_buffer(struct knowles_sound_trigger_device *stdev, |
| struct model_info *model, |
| bool enabled) |
| { |
| int err = 0; |
| if (enabled) { |
| if ((check_uuid_equality(model->uuid, stdev->hotword_model_uuid)) |
| || (check_uuid_equality(model->uuid, stdev->wakeup_model_uuid))) { |
| |
| stdev->hotword_buffer_enable++; |
| if (stdev->hotword_buffer_enable > 1) { |
| ALOGD("%d models are using hotword buffer", |
| stdev->hotword_buffer_enable); |
| goto exit; |
| } |
| err = setup_howord_buffer(stdev->odsp_hdl); |
| if (err != 0) { |
| stdev->hotword_buffer_enable--; |
| ALOGE("Failed to create the buffer plugin"); |
| } |
| } else if ((check_uuid_equality(model->uuid, stdev->ambient_model_uuid)) |
| || (check_uuid_equality(model->uuid, stdev->entity_model_uuid))) { |
| |
| stdev->music_buffer_enable++; |
| if (stdev->music_buffer_enable > 1) { |
| ALOGD("%d models are using music buffer", |
| stdev->music_buffer_enable); |
| goto exit; |
| } |
| err = setup_music_buffer(stdev->odsp_hdl); |
| if (err != 0) { |
| stdev->music_buffer_enable--; |
| ALOGE("Failed to load music buffer package"); |
| } |
| } |
| } else { |
| if ((check_uuid_equality(model->uuid, stdev->hotword_model_uuid)) |
| || (check_uuid_equality(model->uuid, stdev->wakeup_model_uuid))) { |
| if (stdev->hotword_buffer_enable == 0) { |
| ALOGW("Invalid call for setup buffer"); |
| goto exit; |
| } |
| stdev->hotword_buffer_enable--; |
| if (stdev->hotword_buffer_enable != 0) { |
| ALOGD("hotword buffer is still used by %d models", |
| stdev->hotword_buffer_enable); |
| goto exit; |
| } |
| err = destroy_howord_buffer(stdev->odsp_hdl); |
| |
| if (err != 0) { |
| ALOGE("Failed to unload hotword buffer package"); |
| } |
| |
| } else if ((check_uuid_equality(model->uuid, stdev->ambient_model_uuid)) |
| || (check_uuid_equality(model->uuid, stdev->entity_model_uuid))) { |
| if (stdev->music_buffer_enable == 0) { |
| ALOGW("Invalid call for setup buffer"); |
| goto exit; |
| } |
| stdev->music_buffer_enable--; |
| if (stdev->music_buffer_enable != 0) { |
| ALOGD("music buffer is still used by %d models", |
| stdev->music_buffer_enable); |
| goto exit; |
| } |
| err = destroy_music_buffer(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to unload music buffer package"); |
| } |
| } |
| } |
| |
| exit: |
| return err; |
| } |
| |
| static int destroy_package(struct knowles_sound_trigger_device *stdev, |
| struct model_info *model) |
| { |
| int err = 0; |
| |
| if (check_uuid_equality(model->uuid, stdev->chre_model_uuid)) { |
| stdev->current_enable = stdev->current_enable & ~CHRE_MASK; |
| if (!(stdev->current_enable & CHRE_MASK)) { |
| err = destroy_chre_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to destroy CHRE package"); |
| } |
| } |
| } else if (check_uuid_equality(model->uuid, stdev->hotword_model_uuid)) { |
| err = tear_hotword_state(stdev->odsp_hdl, HOTWORD_MASK); |
| if (err != 0) { |
| ALOGE("Failed to tear Hotword state"); |
| } |
| |
| err = flush_model(stdev->odsp_hdl, model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to flush Hotword model"); |
| } |
| stdev->current_enable = stdev->current_enable & ~HOTWORD_MASK; |
| |
| if (!(stdev->current_enable & PLUGIN1_MASK)) { |
| err = destroy_hotword_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to destroy Hotword package"); |
| } |
| } |
| |
| } else if (check_uuid_equality(model->uuid, stdev->wakeup_model_uuid)) { |
| err = tear_hotword_state(stdev->odsp_hdl, WAKEUP_MASK); |
| if (err != 0) { |
| ALOGE("Failed to tear Wakeup state"); |
| } |
| |
| err = flush_model(stdev->odsp_hdl, model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to flush Wakeup model"); |
| } |
| stdev->current_enable = stdev->current_enable & ~WAKEUP_MASK; |
| |
| if (!(stdev->current_enable & PLUGIN1_MASK)) { |
| err = destroy_hotword_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to destroy Hotword package"); |
| } |
| } |
| |
| } else if (check_uuid_equality(model->uuid, stdev->ambient_model_uuid)) { |
| err = tear_ambient_state(stdev->odsp_hdl, AMBIENT_MASK); |
| if (err != 0) { |
| ALOGE("Failed to tear Ambient state"); |
| } |
| |
| err = flush_model(stdev->odsp_hdl, model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to flush Ambient model"); |
| } |
| stdev->current_enable = stdev->current_enable & ~AMBIENT_MASK; |
| |
| if (!(stdev->current_enable & PLUGIN2_MASK)) { |
| err = destroy_ambient_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to destroy Ambient package"); |
| } |
| } |
| } else if (check_uuid_equality(model->uuid, stdev->entity_model_uuid)) { |
| err = tear_ambient_state(stdev->odsp_hdl, ENTITY_MASK); |
| if (err != 0) { |
| ALOGE("Failed to tear Entity state"); |
| } |
| |
| err = flush_model(stdev->odsp_hdl, model->kw_id); |
| if (err != 0) { |
| ALOGE("Failed to flush Entity model"); |
| } |
| stdev->current_enable = stdev->current_enable & ~ENTITY_MASK; |
| |
| if (!(stdev->current_enable & PLUGIN2_MASK)) { |
| err = destroy_ambient_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to destroy Ambient package"); |
| } |
| } |
| } |
| return err; |
| } |
| |
| static int set_package_route(struct knowles_sound_trigger_device *stdev, |
| sound_trigger_uuid_t uuid, |
| bool bargein) |
| { |
| int ret = 0; |
| /* |
| *[TODO] Add correct error return value for package route |
| * b/119390722 for tracing. |
| */ |
| if (check_uuid_equality(uuid, stdev->chre_model_uuid)) { |
| if (stdev->is_chre_loaded == true) { |
| set_chre_audio_route(stdev->route_hdl, bargein); |
| } |
| } else if (check_uuid_equality(uuid, stdev->hotword_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN1_MASK) & ~HOTWORD_MASK)) { |
| set_hotword_route(stdev->route_hdl, bargein); |
| } |
| } else if (check_uuid_equality(uuid, stdev->wakeup_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN1_MASK) & ~WAKEUP_MASK)) { |
| set_hotword_route(stdev->route_hdl, bargein); |
| } |
| } else if (check_uuid_equality(uuid, stdev->ambient_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN2_MASK) & ~AMBIENT_MASK)) { |
| set_ambient_route(stdev->route_hdl, bargein); |
| } |
| } else if (check_uuid_equality(uuid, stdev->entity_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN2_MASK) & ~ENTITY_MASK)) { |
| set_ambient_route(stdev->route_hdl, bargein); |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int tear_package_route(struct knowles_sound_trigger_device *stdev, |
| sound_trigger_uuid_t uuid, |
| bool bargein) |
| { |
| int ret = 0; |
| /* |
| *[TODO] Add correct error return value for package route |
| * b/119390722 for tracing. |
| */ |
| if (check_uuid_equality(uuid, stdev->chre_model_uuid)) { |
| if (stdev->is_chre_loaded == true) { |
| tear_chre_audio_route(stdev->route_hdl, bargein); |
| } |
| } else if (check_uuid_equality(uuid, stdev->hotword_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN1_MASK) & ~HOTWORD_MASK)) |
| tear_hotword_route(stdev->route_hdl, bargein); |
| } else if (check_uuid_equality(uuid, stdev->wakeup_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN1_MASK) & ~WAKEUP_MASK)) |
| tear_hotword_route(stdev->route_hdl, bargein); |
| } else if (check_uuid_equality(uuid, stdev->ambient_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN2_MASK) & ~AMBIENT_MASK)) |
| tear_ambient_route(stdev->route_hdl, bargein); |
| } else if (check_uuid_equality(uuid, stdev->entity_model_uuid)) { |
| if (!((stdev->current_enable & PLUGIN2_MASK) & ~ENTITY_MASK)) |
| tear_ambient_route(stdev->route_hdl, bargein); |
| } |
| |
| return ret; |
| } |
| |
| static int async_setup_aec(struct knowles_sound_trigger_device *stdev) |
| { |
| int ret = 0; |
| if (stdev->rx_active_count > 0 && |
| stdev->is_bargein_route_enabled != true && |
| stdev->is_mic_route_enabled != false) { |
| ALOGD("%s: Bargein enabling", __func__); |
| if (is_mic_controlled_by_ahal(stdev) == false) { |
| ret = enable_mic_route(stdev->route_hdl, false, |
| INTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to disable mic route with INT OSC"); |
| } |
| } |
| ret = setup_src_plugin(stdev->odsp_hdl, SRC_AMP_REF); |
| if (ret != 0) { |
| ALOGE("Failed to load SRC-amp package"); |
| } |
| ret = enable_src_route(stdev->route_hdl, true, SRC_AMP_REF); |
| if (ret != 0) { |
| ALOGE("Failed to enable SRC-amp route"); |
| } |
| |
| ret = setup_aec_package(stdev->odsp_hdl); |
| if (ret != 0) { |
| ALOGE("Failed to load AEC package"); |
| } |
| ret = enable_bargein_route(stdev->route_hdl, true); |
| if (ret != 0) { |
| ALOGE("Failed to enable buffer route"); |
| } |
| |
| if (is_mic_controlled_by_ahal(stdev) == false) { |
| ret = enable_amp_ref_route(stdev->route_hdl, true, STRM_16K); |
| if (ret != 0) { |
| ALOGE("Failed to enable amp-ref route"); |
| } |
| ret = enable_mic_route(stdev->route_hdl, true, |
| EXTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to enable mic route with EXT OSC"); |
| } |
| } else { |
| // main mic is turned by media recording |
| ret = enable_amp_ref_route(stdev->route_hdl, true, STRM_48K); |
| if (ret != 0) { |
| ALOGE("Failed to enable amp-ref route"); |
| } |
| } |
| stdev->is_bargein_route_enabled = true; |
| |
| if (stdev->hotword_buffer_enable) { |
| ret = tear_hotword_buffer_route(stdev->route_hdl, |
| !stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to tear old buffer route"); |
| } |
| ret = set_hotword_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to enable buffer route"); |
| } |
| } |
| |
| if (stdev->music_buffer_enable) { |
| ret = tear_music_buffer_route(stdev->route_hdl, |
| !stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to tear old music buffer route"); |
| } |
| ret = set_music_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to enable buffer route"); |
| } |
| } |
| |
| // Check each model, if it is active then update it's route |
| for (int i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active == true) { |
| // teardown the package route without bargein |
| ret = tear_package_route(stdev, |
| stdev->models[i].uuid, |
| !stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to tear old package route"); |
| } |
| // resetup the package route with bargein |
| ret = set_package_route(stdev, |
| stdev->models[i].uuid, |
| stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to enable package route"); |
| } |
| } |
| } |
| } else { |
| ALOGD("%s: Bargein is already enabled", __func__); |
| } |
| |
| return ret; |
| } |
| |
| static int handle_input_source(struct knowles_sound_trigger_device *stdev, |
| bool enable) |
| { |
| int err = 0; |
| enum clock_type ct = INTERNAL_OSCILLATOR; |
| enum strm_type strmt = STRM_16K; |
| |
| if (stdev->rx_active_count > 0) { |
| ct = EXTERNAL_OSCILLATOR; |
| } |
| |
| if (is_mic_controlled_by_ahal(stdev) == true) { |
| strmt = STRM_48K; |
| } |
| |
| if (enable) { |
| /* |
| * Setup the sources include microphone, SampleRate Converter |
| * and Barge-in if necessary. |
| * The SRC-mic and SRC-amp must be enabled before barge-in route. |
| * That avoid the frame alignment conflict of AEC module. |
| */ |
| if (stdev->is_mic_route_enabled == false) { |
| err = check_and_setup_src_package(stdev); |
| if (err != 0) { |
| ALOGE("Failed to load SRC package"); |
| } |
| err = setup_src_plugin(stdev->odsp_hdl, SRC_MIC); |
| if (err != 0) { |
| ALOGE("Failed to create SRC-mic plugin"); |
| } |
| err = enable_src_route(stdev->route_hdl, true, SRC_MIC); |
| if (err != 0) { |
| ALOGE("Failed to enable SRC-mic route"); |
| } |
| } |
| if (stdev->rx_active_count > 0 && |
| stdev->is_bargein_route_enabled == false) { |
| err = setup_src_plugin(stdev->odsp_hdl, SRC_AMP_REF); |
| if (err != 0) { |
| ALOGE("Failed to create SRC-amp plugin"); |
| } |
| err = enable_src_route(stdev->route_hdl, true, SRC_AMP_REF); |
| if (err != 0) { |
| ALOGE("Failed to enable SRC-amp route"); |
| } |
| |
| err = setup_aec_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to load AEC package"); |
| } |
| |
| err = enable_bargein_route(stdev->route_hdl, true); |
| if (err != 0) { |
| ALOGE("Failed to enable barge-in route"); |
| } |
| err = enable_amp_ref_route(stdev->route_hdl, true, strmt); |
| if (err != 0) { |
| ALOGE("Failed to enable amp-ref route"); |
| } |
| stdev->is_bargein_route_enabled = true; |
| } |
| if (stdev->is_mic_route_enabled == false) { |
| if (is_mic_controlled_by_ahal(stdev) == false) { |
| err = enable_mic_route(stdev->route_hdl, true, ct); |
| if (err != 0) { |
| ALOGE("Failed to enable mic route"); |
| } |
| } |
| stdev->is_mic_route_enabled = true; |
| } |
| } else { |
| if (!is_any_model_active(stdev)) { |
| ALOGD("None of model are active"); |
| if (stdev->rx_active_count > 0 && |
| stdev->is_bargein_route_enabled == true) { |
| err = enable_bargein_route(stdev->route_hdl, false); |
| if (err != 0) { |
| ALOGE("Failed to disable barge-in route"); |
| } |
| err = destroy_aec_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to destroy AEC package"); |
| } |
| err = enable_src_route(stdev->route_hdl, false, SRC_AMP_REF); |
| if (err != 0) { |
| ALOGE("Failed to disable SRC-amp route"); |
| } |
| err = destroy_src_plugin(stdev->odsp_hdl, SRC_AMP_REF); |
| if (err != 0) { |
| ALOGE("Failed to destroy SRC-amp package"); |
| } |
| err = enable_amp_ref_route(stdev->route_hdl, false, strmt); |
| if (err != 0) { |
| ALOGE("Failed to amp-ref route"); |
| } |
| stdev->is_bargein_route_enabled = false; |
| } |
| if (stdev->is_mic_route_enabled == true) { |
| err = enable_src_route(stdev->route_hdl, false, SRC_MIC); |
| if (err != 0) { |
| ALOGE("Failed to disable SRC-mic route"); |
| } |
| err = destroy_src_plugin(stdev->odsp_hdl, SRC_MIC); |
| if (err != 0) { |
| ALOGE("Failed to destroy SRC-mic package"); |
| } |
| if (is_mic_controlled_by_ahal(stdev) == false) { |
| err = enable_mic_route(stdev->route_hdl, false, ct); |
| if (err != 0) { |
| ALOGE("Failed to disable mic route"); |
| } |
| } |
| stdev->is_mic_route_enabled = false; |
| err = check_and_destroy_src_package(stdev); |
| if (err != 0) { |
| ALOGE("Failed to destroy src package"); |
| } |
| } |
| } |
| } |
| |
| return err; |
| } |
| |
| // Helper functions for audio_device_info |
| |
| int list_length(const struct listnode *list) |
| { |
| struct listnode *node; |
| int length = 0; |
| |
| if (list == NULL) { |
| return 0; |
| } |
| |
| list_for_each (node, list) { |
| ++length; |
| } |
| return length; |
| } |
| |
| /* |
| * If single device in devices list is equal to passed type |
| * type should represent a single device. |
| */ |
| bool is_single_device_type_equal(struct listnode *devices, |
| audio_devices_t type) |
| { |
| if (devices == NULL) |
| return false; |
| |
| if (list_length(devices) == 1) { |
| struct listnode *node = devices->next; |
| struct audio_device_info *item = node_to_item(node, struct audio_device_info, list); |
| if (item != NULL && (item->type == type)) |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * Check if a device with given type is present in devices list |
| */ |
| bool compare_device_type(struct listnode *devices, audio_devices_t device_type) |
| { |
| struct listnode *node; |
| struct audio_device_info *item = NULL; |
| |
| if (devices == NULL) |
| return false; |
| |
| list_for_each (node, devices) { |
| item = node_to_item(node, struct audio_device_info, list); |
| if (item != NULL && (item->type == device_type)) { |
| ALOGV("%s: device types %d match", __func__, device_type); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // End of helper functions for audio_device_info |
| |
| static void update_rx_conditions(struct knowles_sound_trigger_device *stdev, |
| audio_event_type_t event, |
| struct audio_event_info *config) |
| { |
| if (event == AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE) { |
| if (stdev->rx_active_count > 0) { |
| stdev->rx_active_count--; |
| } else { |
| ALOGW("%s: unexpected rx inactive event", __func__); |
| } |
| } else if (event == AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE) { |
| if (!compare_device_type(&config->device_info.devices, AUDIO_DEVICE_OUT_SPEAKER)) { |
| ALOGD("%s: Playback device doesn't include SPEAKER.", |
| __func__); |
| } else { |
| stdev->rx_active_count++; |
| } |
| } else { |
| ALOGW("%s: invalid event %d", __func__, event); |
| } |
| ALOGD("%s: updated rx_concurrency as %d", __func__, |
| stdev->rx_active_count); |
| } |
| |
| |
| static void update_sthal_conditions(struct knowles_sound_trigger_device *stdev, |
| audio_event_type_t event, |
| struct audio_event_info *config) |
| { |
| if (event == AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE) { |
| // get correct voice/voip mode in sthal |
| if (stdev->is_ahal_in_voice_voip_mode == false && |
| stdev->is_ahal_voice_voip_stop == true && |
| stdev->is_ahal_media_recording == true) { |
| ALOGD("%s: voice/voip didn't start, treat it as media recording inactive", |
| __func__); |
| stdev->is_ahal_voice_voip_stop = false; |
| stdev->is_ahal_media_recording = false; |
| } else if (stdev->is_ahal_voice_voip_stop == true) { |
| ALOGD("%s: voice/voip device is inactive", __func__); |
| stdev->is_ahal_in_voice_voip_mode = false; |
| stdev->is_ahal_voice_voip_stop = false; |
| } else if (stdev->is_ahal_in_voice_voip_mode == true && |
| stdev->is_ahal_voice_voip_stop == false && |
| stdev->is_ahal_voice_voip_start == false) { |
| ALOGD("%s: voice/voip usecase didn't start in incall mode, treat it as voice/voip is inactive", |
| __func__); |
| stdev->is_ahal_in_voice_voip_mode = false; |
| } |
| |
| if (stdev->is_concurrent_capture == true && |
| stdev->is_ahal_in_voice_voip_mode == false) { |
| if (stdev->is_ahal_media_recording == true) |
| stdev->is_con_mic_route_enabled = true; |
| else |
| stdev->is_con_mic_route_enabled = false; |
| ALOGD("%s: update mic con %d", __func__, |
| stdev->is_con_mic_route_enabled); |
| } |
| } else if (event == AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE) { |
| if (stdev->is_ahal_in_voice_voip_mode == false && |
| (config->u.usecase.type == USECASE_TYPE_VOICE_CALL || |
| config->u.usecase.type == USECASE_TYPE_VOIP_CALL)) { |
| ALOGD("%s: voice/voip is actvie, close ST mic and don't use mic concurrently", |
| __func__); |
| stdev->is_ahal_in_voice_voip_mode = true; |
| } |
| if (config->u.usecase.type == USECASE_TYPE_PCM_CAPTURE) { |
| stdev->is_ahal_media_recording = true; |
| } |
| if (stdev->is_concurrent_capture == true && |
| stdev->is_ahal_in_voice_voip_mode == false && |
| stdev->is_con_mic_route_enabled == false && |
| is_single_device_type_equal(&config->device_info.devices, ST_DEVICE_HANDSET_MIC)) { |
| ALOGD("%s: enable mic concurrency", __func__); |
| stdev->is_con_mic_route_enabled = true; |
| } |
| } else if (event == AUDIO_EVENT_CAPTURE_STREAM_INACTIVE) { |
| if (stdev->is_ahal_voice_voip_start == true && |
| (config->u.usecase.type == USECASE_TYPE_VOICE_CALL || |
| config->u.usecase.type == USECASE_TYPE_VOIP_CALL)) { |
| stdev->is_ahal_voice_voip_stop = true; |
| stdev->is_ahal_voice_voip_start = false; |
| } else if (config->u.usecase.type == USECASE_TYPE_PCM_CAPTURE) |
| stdev->is_ahal_media_recording = false; |
| } else if (event == AUDIO_EVENT_CAPTURE_STREAM_ACTIVE) { |
| if (config->u.usecase.type == USECASE_TYPE_VOICE_CALL || |
| config->u.usecase.type == USECASE_TYPE_VOIP_CALL) { |
| stdev->is_ahal_voice_voip_start = true; |
| } |
| } |
| } |
| |
| static bool do_handle_functions(struct knowles_sound_trigger_device *stdev, |
| enum sthal_mode pre_mode, |
| enum sthal_mode cur_mode, |
| audio_event_type_t event) |
| { |
| int ret = 0; |
| int i = 0; |
| |
| ALOGD("+%s+: pre %d, cur %d, event %d", __func__, pre_mode, cur_mode, event); |
| |
| // handle event AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE |
| if (event == AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE) { |
| if ((pre_mode == CON_DISABLED_ST && cur_mode == CON_DISABLED_CAPTURE) || |
| (pre_mode == CON_DISABLED_ST && cur_mode == IN_CALL) || |
| (pre_mode == CON_DISABLED_CAPTURE && cur_mode == IN_CALL) || |
| (pre_mode == CON_ENABLED_CAPTURE_ST && cur_mode == IN_CALL) || |
| (pre_mode == CON_ENABLED_ST && cur_mode == IN_CALL)) { |
| // disable all ST |
| // if tunnel is active, close it first |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->adnc_strm_handle[i] != 0) { |
| ALOGD("%s: stop tunnling for index:%d", __func__, i); |
| stdev->adnc_strm_close(stdev->adnc_strm_handle[i]); |
| stdev->adnc_strm_handle[i] = 0; |
| stdev->adnc_strm_last_read[i] = reset_time; |
| } |
| } |
| stdev->is_streaming = 0; |
| |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active == true) { |
| update_recover_list(stdev, i, true); |
| tear_package_route(stdev, stdev->models[i].uuid, |
| stdev->is_bargein_route_enabled); |
| stdev->models[i].is_active = false; |
| if (!check_uuid_equality(stdev->models[i].uuid, |
| stdev->chre_model_uuid)) |
| destroy_package(stdev, &stdev->models[i]); |
| |
| if ((stdev->hotword_buffer_enable) && |
| !(stdev->current_enable & PLUGIN1_MASK)) { |
| tear_hotword_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| |
| if ((stdev->music_buffer_enable) && |
| !(stdev->current_enable & PLUGIN2_MASK)) { |
| tear_music_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| |
| setup_buffer(stdev, &stdev->models[i], false); |
| } |
| } |
| handle_input_source(stdev, false); |
| check_and_destroy_buffer_package(stdev); |
| } else if (pre_mode == CON_ENABLED_ST && cur_mode == CON_ENABLED_CAPTURE_ST) { |
| //reconfig mic |
| if (stdev->is_mic_route_enabled == true) { |
| if (stdev->is_bargein_route_enabled == true) { |
| // close amp-ref first and reconfig it again with 48K after |
| // main mic is turned on by media recording |
| ret = enable_amp_ref_route(stdev->route_hdl, false, STRM_16K); |
| if (ret != 0) { |
| ALOGE("Failed to disable amp-ref route"); |
| } |
| ret = enable_mic_route(stdev->route_hdl, false, |
| EXTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to disable mic route with EXT OSC"); |
| } |
| |
| ret = enable_amp_ref_route(stdev->route_hdl, true, STRM_48K); |
| if (ret != 0) { |
| ALOGE("Failed to enable amp-ref route"); |
| } |
| } else { |
| ret = enable_mic_route(stdev->route_hdl, false, |
| INTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to disable mic route with INT OSC"); |
| } |
| } |
| } else { |
| ALOGD("%s: ST mic isn't enabled, recording mic is turned on", |
| __func__); |
| } |
| } |
| } |
| |
| // handle event AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE |
| if (event == AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE) { |
| if ((pre_mode == IN_CALL && cur_mode == CON_DISABLED_ST) || |
| (pre_mode == IN_CALL && cur_mode == CON_DISABLED_CAPTURE) || |
| (pre_mode == IN_CALL && cur_mode == CON_ENABLED_ST) || |
| (pre_mode == IN_CALL && cur_mode == CON_ENABLED_CAPTURE_ST) || |
| (pre_mode == CON_DISABLED_CAPTURE && cur_mode == CON_DISABLED_ST)) { |
| //recover all STs |
| for (i = 0; i < MAX_MODELS; i++) { |
| // recover all models from list |
| if (is_uuid_in_recover_list(stdev, i)) { |
| if (stdev->models[i].is_active == false) { |
| check_and_setup_buffer_package(stdev); |
| stdev->models[i].is_active = true; |
| handle_input_source(stdev, true); |
| |
| setup_buffer(stdev, &stdev->models[i], true); |
| if (stdev->hotword_buffer_enable && |
| !(stdev->current_enable & PLUGIN1_MASK)) { |
| set_hotword_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| if (stdev->music_buffer_enable && |
| !(stdev->current_enable & PLUGIN2_MASK)) { |
| set_music_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| |
| if (!check_uuid_equality(stdev->models[i].uuid, |
| stdev->chre_model_uuid)) |
| setup_package(stdev, &stdev->models[i]); |
| set_package_route(stdev, stdev->models[i].uuid, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| } |
| stdev->recover_model_list = 0; |
| } else if (pre_mode == CON_ENABLED_CAPTURE_ST && cur_mode == CON_ENABLED_ST) { |
| // reconfig mic |
| if (stdev->is_mic_route_enabled == true) { |
| if (stdev->is_bargein_route_enabled == true) { |
| ret = enable_amp_ref_route(stdev->route_hdl, false, STRM_48K); |
| if (ret != 0) { |
| ALOGE("Failed to disable amp-ref route"); |
| } |
| ret = enable_mic_route(stdev->route_hdl, true, |
| EXTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to enable mic route with EXT OSC"); |
| } |
| // turn on amp-ref with 16khz |
| ret = enable_amp_ref_route(stdev->route_hdl, true, STRM_16K); |
| if (ret != 0) { |
| ALOGE("Failed to enable amp-ref route"); |
| } |
| } else { |
| ret = enable_mic_route(stdev->route_hdl, true, |
| INTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to enable mic route with INT OSC"); |
| } |
| } |
| } else { |
| ALOGD("%s: ST mic isn't enabled, recording mic is turned off", |
| __func__); |
| } |
| } |
| } |
| |
| ALOGD("-%s-: pre %d, cur %d, event %d", __func__, pre_mode, cur_mode, event); |
| return ret; |
| } |
| |
| // stdev needs to be locked before calling this function |
| static int restart_recognition(struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| int i = 0; |
| enum strm_type strmt = STRM_16K; |
| enum clock_type ct = INTERNAL_OSCILLATOR; |
| /* |
| * The libaudioroute library doesn't set the mixer controls if previously |
| * applied values are the same or the active_count > 0, so we need to |
| * teardown the route so that it can clear up the value and active_count. |
| * Then we could setup the routes again. |
| */ |
| |
| stdev->current_enable = 0; |
| stdev->hotword_buffer_enable = 0; |
| stdev->music_buffer_enable = 0; |
| |
| if (stdev->rx_active_count == 0 && |
| stdev->is_bargein_route_enabled == true) { |
| /* rx stream is disabled during codec recovery. |
| * Need to reset the enabled flag |
| */ |
| stdev->is_bargein_route_enabled = false; |
| } |
| |
| if (stdev->is_buffer_package_loaded == true) { |
| err = setup_buffer_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("%s: Failed to restart Buffer package", __func__); |
| } |
| } |
| |
| if (stdev->is_src_package_loaded == true) { |
| err = setup_src_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("%s: Failed to restart SRC package", __func__); |
| } |
| } |
| |
| /* |
| * If ST mode is IN_CALL, make sure mic route as false, |
| * that would be reloaded after ending the call |
| */ |
| if (get_sthal_mode(stdev) == IN_CALL) { |
| ALOGD("%s: ST mode is in_call, reset all routes", __func__); |
| |
| stdev->is_mic_route_enabled = false; |
| stdev->is_bargein_route_enabled = false; |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active == true) { |
| update_recover_list(stdev, i, true); |
| stdev->models[i].is_active = false; |
| } |
| } |
| |
| // if chre enabled before crash during call, need to setup package for SLPI. |
| if (stdev->is_chre_loaded == true) { |
| err = setup_chre_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to load CHRE package"); |
| } |
| stdev->current_enable = stdev->current_enable | CHRE_MASK; |
| } |
| goto reload_oslo; |
| } |
| |
| |
| /* |
| * Reset mic and src package if sound trigger recording is active |
| * The src-mic, src-amp must be enable before AEC enable, because |
| * the endpoint sequence control. |
| */ |
| if (stdev->is_mic_route_enabled == true) { |
| // recover src package if sound trigger recording is active |
| err = setup_src_plugin(stdev->odsp_hdl, SRC_MIC); |
| if (err != 0) { |
| ALOGE("failed to load SRC package"); |
| } |
| err = enable_src_route(stdev->route_hdl, true, SRC_MIC); |
| if (err != 0) { |
| ALOGE("Failed to restart SRC-mic route"); |
| } |
| /* |
| * RX stream was enabled during codec recovery. |
| * Need to setup the barge-in package and routing. |
| */ |
| if (stdev->rx_active_count > 0) { |
| stdev->is_bargein_route_enabled = true; |
| ct = EXTERNAL_OSCILLATOR; |
| if (is_mic_controlled_by_ahal(stdev) == true) { |
| strmt = STRM_48K; |
| } |
| err = setup_src_plugin(stdev->odsp_hdl, SRC_AMP_REF); |
| if (err != 0) { |
| ALOGE("failed to load SRC package"); |
| } |
| err = enable_src_route(stdev->route_hdl, true, SRC_AMP_REF); |
| if (err != 0) { |
| ALOGE("Failed to restart SRC-amp route"); |
| } |
| |
| err = setup_aec_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("Failed to restart AEC package"); |
| } |
| err = enable_bargein_route(stdev->route_hdl, true); |
| if (err != 0) { |
| ALOGE("Failed to restart bargein route"); |
| } |
| err = enable_amp_ref_route(stdev->route_hdl, true, strmt); |
| if (err != 0) { |
| ALOGE("Failed to restart amp-ref route"); |
| } |
| } |
| // The stream 0 should be enable at last moment for the data alignment. |
| if (is_mic_controlled_by_ahal(stdev) == false) { |
| err = enable_mic_route(stdev->route_hdl, true, ct); |
| if (err != 0) { |
| ALOGE("failed to restart mic route"); |
| } |
| } |
| } |
| |
| // Download all the keyword models files that were previously loaded |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active == true) { |
| if (stdev->is_buffer_package_loaded == true) { |
| setup_buffer(stdev, &stdev->models[i], true); |
| } |
| if (check_uuid_equality(stdev->models[i].uuid, stdev->hotword_model_uuid) || |
| (check_uuid_equality(stdev->models[i].uuid, stdev->wakeup_model_uuid))) { |
| if ((stdev->hotword_buffer_enable) && |
| (!(stdev->current_enable & HOTWORD_MASK) || |
| (stdev->current_enable & WAKEUP_MASK))) { |
| set_hotword_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| if (check_uuid_equality(stdev->models[i].uuid, stdev->ambient_model_uuid) || |
| (check_uuid_equality(stdev->models[i].uuid, stdev->entity_model_uuid))) { |
| if ((stdev->music_buffer_enable) && |
| (!(stdev->current_enable & AMBIENT_MASK) || |
| (stdev->current_enable & ENTITY_MASK))) { |
| set_music_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| setup_package(stdev, &stdev->models[i]); |
| set_package_route(stdev, stdev->models[i].uuid, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| |
| reload_oslo: |
| // reload Oslo part after every package loaded to avoid HMD memory overlap |
| // issue, b/128914464 |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_loaded == true) { |
| if (check_uuid_equality(stdev->models[i].uuid, |
| stdev->sensor_model_uuid)) { |
| // setup the sensor route |
| err = setup_sensor_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("%s: setup Sensor package failed", __func__); |
| goto exit; |
| } |
| |
| err = set_sensor_route(stdev->route_hdl, true); |
| if (err != 0) { |
| ALOGE("%s: Sensor route fail", __func__); |
| goto exit; |
| } |
| stdev->is_sensor_route_enabled = true; |
| } |
| } |
| } |
| ALOGD("%s: recovery done", __func__); |
| exit: |
| return err; |
| } |
| |
| // stdev needs to be locked before calling this function |
| static int crash_recovery(struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| |
| set_default_apll_clk(stdev->mixer); |
| setup_slpi_wakeup_event(stdev->odsp_hdl, true); |
| |
| // Redownload the keyword model files and start recognition |
| err = restart_recognition(stdev); |
| if (err != 0) { |
| ALOGE("%s: ERROR: Failed to download the keyword models and restarting" |
| " recognition", __func__); |
| goto exit; |
| } |
| |
| // Reset the flag only after successful recovery |
| stdev->is_st_hal_ready = true; |
| |
| exit: |
| return err; |
| } |
| |
| static void sensor_crash_handler(struct knowles_sound_trigger_device *stdev) |
| { |
| int i; |
| |
| if (stdev->is_sensor_destroy_in_prog == false) |
| return; |
| |
| if (stdev->ss_timer_created) { |
| timer_delete(stdev->ss_timer); |
| stdev->ss_timer_created = false; |
| } |
| |
| if (stdev->is_sensor_route_enabled == true) { |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (check_uuid_equality(stdev->models[i].uuid, |
| stdev->sensor_model_uuid) && |
| stdev->models[i].is_loaded == true) { |
| stdev->models[i].is_loaded = false; |
| memset(&stdev->models[i].uuid, 0, |
| sizeof(sound_trigger_uuid_t)); |
| break; |
| } |
| } |
| stdev->is_sensor_route_enabled = false; |
| stdev->current_enable &= ~OSLO_MASK; |
| } |
| stdev->is_sensor_destroy_in_prog = false; |
| |
| // There could be another thread waiting for us to destroy |
| // so signal that thread, if no one is waiting then this signal |
| // will have no effect |
| pthread_cond_signal(&stdev->sensor_create); |
| } |
| |
| static void destroy_sensor_model(struct knowles_sound_trigger_device *stdev) |
| { |
| int ret, i; |
| ALOGD("+%s+", __func__); |
| |
| if (stdev->is_sensor_route_enabled == true) { |
| ret = set_sensor_route(stdev->route_hdl, false); |
| if (ret != 0) { |
| ALOGE("%s: tear Sensor route fail", __func__); |
| } |
| stdev->is_sensor_route_enabled = false; |
| |
| ret = destroy_sensor_package(stdev->odsp_hdl); |
| if (ret != 0) { |
| ALOGE("%s: destroy Sensor package failed %d", |
| __func__, ret); |
| } |
| stdev->current_enable = stdev->current_enable & ~OSLO_MASK; |
| } |
| |
| // now we can change the flag |
| for (i = 0 ; i < MAX_MODELS ; i++) { |
| if (check_uuid_equality(stdev->models[i].uuid, |
| stdev->sensor_model_uuid) && |
| stdev->models[i].is_loaded == true) { |
| memset(&stdev->models[i].uuid, 0, sizeof(sound_trigger_uuid_t)); |
| stdev->models[i].is_loaded = false; |
| break; |
| } |
| } |
| |
| stdev->is_sensor_destroy_in_prog = false; |
| check_and_destroy_buffer_package(stdev); |
| |
| // There could be another thread waiting for us to destroy so signal that |
| // thread, if no one is waiting then this signal will have no effect |
| pthread_cond_signal(&stdev->sensor_create); |
| |
| ALOGD("-%s-", __func__); |
| } |
| |
| static void sensor_timeout_recover() |
| { |
| int err = 0; |
| ALOGD("+%s+", __func__); |
| |
| struct knowles_sound_trigger_device *stdev = &g_stdev; |
| pthread_mutex_lock(&stdev->lock); |
| // We are here because we timed out so check if we still need to destroy |
| // the sensor package, if yes then reset the firmware |
| if (stdev->is_sensor_destroy_in_prog == true) { |
| if (stdev->is_st_hal_ready) { |
| stdev->is_st_hal_ready = false; |
| // reset the firmware and wait for firmware download complete |
| err = reset_fw(stdev->odsp_hdl); |
| if (err == -1) { |
| ALOGE("%s: ERROR: Failed to reset the firmware %d(%s)", |
| __func__, errno, strerror(errno)); |
| } |
| reset_all_route(stdev->route_hdl); |
| sensor_crash_handler(stdev); |
| } |
| } |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGD("-%s-", __func__); |
| } |
| |
| static int start_sensor_model(struct knowles_sound_trigger_device * stdev) |
| { |
| struct timespec ts; |
| int wait_counter = 0, err = 0; |
| |
| while (stdev->is_sensor_destroy_in_prog == true && |
| wait_counter < SENSOR_CREATE_WAIT_MAX_COUNT) { |
| // We wait for 1sec * MAX_COUNT times for the HOST 1 to respond, if |
| // within that time we don't get any response, we will go ahead with the |
| // sensor model creation. Note this might result in an error which would |
| // be better than blocking the thread indefinitely. |
| clock_gettime(CLOCK_REALTIME, &ts); |
| ts.tv_sec += SENSOR_CREATE_WAIT_TIME_IN_S; |
| err = pthread_cond_timedwait(&stdev->sensor_create, &stdev->lock, &ts); |
| if (err == ETIMEDOUT) { |
| ALOGE("%s: WARNING: Sensor create timed out after %ds", |
| __func__, SENSOR_CREATE_WAIT_TIME_IN_S); |
| wait_counter++; |
| } |
| } |
| |
| // If firmware crashed when we are waiting |
| if (stdev->is_st_hal_ready == false) { |
| err = -ENODEV; |
| goto exit; |
| } |
| |
| if (stdev->is_sensor_destroy_in_prog == true) { |
| ALOGE("%s: ERROR: Waited for %ds but we didn't get the event from " |
| "Host 1, and fw reset is not yet complete", __func__, |
| SENSOR_CREATE_WAIT_TIME_IN_S * SENSOR_CREATE_WAIT_MAX_COUNT); |
| err = -ENODEV; |
| goto exit; |
| } |
| |
| // setup the sensor route |
| err = check_and_setup_buffer_package(stdev); |
| if (err != 0) { |
| ALOGE("%s: ERROR: Failed to load the buffer package", __func__); |
| goto exit; |
| } |
| |
| if(stdev->is_sensor_route_enabled == false) { |
| err = setup_sensor_package(stdev->odsp_hdl); |
| if (err) { |
| ALOGE("%s: Failed to setup sensor package", __func__); |
| goto exit; |
| } |
| // Don't download the keyword model file, just setup the |
| // sensor route |
| err = set_sensor_route(stdev->route_hdl, true); |
| if (err) { |
| ALOGE("%s: Sensor route fail", __func__); |
| goto exit; |
| } |
| stdev->is_sensor_route_enabled = true; |
| stdev->current_enable = stdev->current_enable | OSLO_MASK; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| static void chre_crash_handler(struct knowles_sound_trigger_device *stdev) |
| { |
| int i; |
| |
| if (stdev->is_chre_destroy_in_prog == false) |
| return; |
| |
| if (stdev->chre_timer_created) { |
| timer_delete(stdev->chre_timer); |
| stdev->chre_timer_created = false; |
| } |
| |
| if (stdev->is_chre_loaded == true) { |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (check_uuid_equality(stdev->models[i].uuid, |
| stdev->chre_model_uuid)) { |
| stdev->models[i].is_active = false; |
| stdev->models[i].is_loaded = false; |
| memset(&stdev->models[i].uuid, 0, |
| sizeof(sound_trigger_uuid_t)); |
| break; |
| } |
| } |
| stdev->is_chre_loaded = false; |
| stdev->current_enable &= ~CHRE_MASK; |
| } |
| stdev->is_chre_destroy_in_prog = false; |
| |
| // There could be another thread waiting for us to destroy |
| // so signal that thread, if no one is waiting then this signal |
| // will have no effect |
| pthread_cond_signal(&stdev->chre_create); |
| } |
| |
| static int start_chre_model(struct knowles_sound_trigger_device *stdev, |
| int model_id) |
| { |
| struct timespec ts; |
| int wait_counter = 0, err = 0; |
| |
| while (stdev->is_chre_destroy_in_prog == true && |
| wait_counter < CHRE_CREATE_WAIT_MAX_COUNT) { |
| // We wait for 1sec * MAX_COUNT times for the HOST 1 to respond, if |
| // within that time we don't get any response, we will go ahead with the |
| // sensor model creation. Note this might result in an error which would |
| // be better than blocking the thread indefinitely. |
| clock_gettime(CLOCK_REALTIME, &ts); |
| ts.tv_sec += CHRE_CREATE_WAIT_TIME_IN_S; |
| err = pthread_cond_timedwait(&stdev->chre_create, &stdev->lock, &ts); |
| if (err == ETIMEDOUT) { |
| ALOGE("%s: WARNING: CHRE create timed out after %ds", |
| __func__, CHRE_CREATE_WAIT_TIME_IN_S); |
| wait_counter++; |
| } |
| } |
| |
| // If firmware crashed when we are waiting |
| if (stdev->is_st_hal_ready == false) { |
| err = -ENODEV; |
| goto exit; |
| } |
| |
| if (stdev->is_chre_destroy_in_prog == true) { |
| ALOGE("%s: ERROR: Waited for %ds but we didn't get the event from " |
| "Host 1, and fw reset is not yet complete", __func__, |
| CHRE_CREATE_WAIT_TIME_IN_S * CHRE_CREATE_WAIT_MAX_COUNT); |
| err = -ENODEV; |
| goto exit; |
| } |
| |
| err = check_and_setup_buffer_package(stdev); |
| if (err != 0) { |
| ALOGE("%s: ERROR: Failed to load the buffer package", __func__); |
| goto exit; |
| } |
| |
| // add chre to recover list |
| if (can_enable_chre(stdev)) { |
| if(stdev->is_chre_loaded == false) { |
| stdev->models[model_id].is_active = true; |
| handle_input_source(stdev, true); |
| setup_chre_package(stdev->odsp_hdl); |
| set_chre_audio_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| stdev->is_chre_loaded = true; |
| stdev->current_enable = stdev->current_enable | CHRE_MASK; |
| } |
| } else { |
| ALOGW("%s: device is in call, setup CHRE for SLPI", |
| __func__); |
| //Setup CHRE package and allow SLPI connect |
| //during in-call mode. |
| if (stdev->is_chre_loaded == false) { |
| setup_chre_package(stdev->odsp_hdl); |
| stdev->models[model_id].uuid = stdev->chre_model_uuid; |
| stdev->is_chre_loaded = true; |
| stdev->current_enable = stdev->current_enable | CHRE_MASK; |
| if (can_update_recover_list(stdev) == true) |
| update_recover_list(stdev, model_id, true); |
| } |
| } |
| |
| exit: |
| return err; |
| } |
| |
| static void destroy_chre_model(struct knowles_sound_trigger_device *stdev) |
| { |
| int err = 0; |
| ALOGD("+%s+", __func__); |
| |
| if (stdev->is_chre_loaded == true) { |
| int i; |
| tear_chre_audio_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| err = destroy_chre_package(stdev->odsp_hdl); |
| if (err != 0) { |
| ALOGE("%s: ERROR: Failed to destroy chre package", __func__); |
| } |
| |
| //Need force reset the flag for chre due to in-call state |
| //The model is inactive, but need to clean if user disable it |
| //during call. |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (check_uuid_equality(stdev->models[i].uuid, |
| stdev->chre_model_uuid)) { |
| stdev->models[i].is_active = false; |
| stdev->models[i].is_loaded = false; |
| memset(&stdev->models[i].uuid, 0, |
| sizeof(sound_trigger_uuid_t)); |
| break; |
| } |
| } |
| handle_input_source(stdev, false); |
| stdev->is_chre_loaded = false; |
| stdev->current_enable = stdev->current_enable & ~CHRE_MASK; |
| } |
| |
| stdev->is_chre_destroy_in_prog = false; |
| |
| // setup the sensor route |
| err = check_and_destroy_buffer_package(stdev); |
| if (err != 0) { |
| ALOGE("%s: ERROR: Failed to destroy buffer package", __func__); |
| } |
| |
| // There could be another thread waiting for us to destroy so signal that |
| // thread, if no one is waiting then this signal will have no effect |
| pthread_cond_signal(&stdev->chre_create); |
| |
| ALOGD("-%s-", __func__); |
| } |
| |
| static void chre_timeout_recover() |
| { |
| int err = 0; |
| ALOGD("+%s+", __func__); |
| |
| struct knowles_sound_trigger_device *stdev = &g_stdev; |
| pthread_mutex_lock(&stdev->lock); |
| // We are here because we timed out so check if we still need to destroy |
| // the chre package, if yes then reset the firmware |
| if (stdev->is_chre_destroy_in_prog == true) { |
| if (stdev->is_st_hal_ready) { |
| stdev->is_st_hal_ready = false; |
| // reset the firmware and wait for firmware download complete |
| err = reset_fw(stdev->odsp_hdl); |
| if (err == -1) { |
| ALOGE("%s: ERROR: Failed to reset the firmware %d(%s)", |
| __func__, errno, strerror(errno)); |
| } |
| reset_all_route(stdev->route_hdl); |
| chre_crash_handler(stdev); |
| } |
| } |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGD("-%s-", __func__); |
| } |
| |
| static void *transitions_thread_loop(void *context) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)context; |
| |
| int err = 0; |
| pthread_mutex_lock(&stdev->lock); |
| while (1) { |
| if (stdev->transit_case == TRANSIT_NONE) |
| pthread_cond_wait(&stdev->transition_cond, &stdev->lock); |
| |
| acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); |
| switch (stdev->transit_case) { |
| case TRANSIT_NONE: |
| break; |
| case TRANSIT_SETUP_AEC: |
| err = async_setup_aec(stdev); |
| break; |
| } |
| stdev->transit_case = TRANSIT_NONE; |
| release_wake_lock(WAKE_LOCK_NAME); |
| } |
| pthread_mutex_unlock(&stdev->lock); |
| } |
| |
| |
| static void *monitor_thread_loop(void *context) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)context; |
| struct timespec now; |
| double diff; |
| |
| pthread_mutex_lock(&stdev->lock); |
| while (1) { |
| if (!stdev->is_streaming) |
| pthread_cond_wait(&stdev->tunnel_create, &stdev->lock); |
| pthread_mutex_unlock(&stdev->lock); |
| |
| sleep(TUNNEL_TIMEOUT); |
| |
| pthread_mutex_lock(&stdev->lock); |
| |
| acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); |
| clock_gettime(CLOCK_REALTIME, &now); |
| for (int i = 0; i < MAX_MODELS; i++) { |
| if (stdev->adnc_strm_handle[i] != 0) { |
| diff = (now.tv_sec - stdev->adnc_strm_last_read[i].tv_sec) |
| + (double) ((now.tv_nsec) - (stdev->adnc_strm_last_read[i].tv_nsec)) |
| / 1000000000.0; |
| |
| if (diff > TUNNEL_TIMEOUT) { |
| ALOGE("%s: Waiting timeout for %f sec", __func__, diff); |
| stdev->adnc_strm_close(stdev->adnc_strm_handle[i]); |
| stdev->adnc_strm_handle[i] = 0; |
| stdev->is_streaming--; |
| stdev->adnc_strm_last_read[i] = reset_time; |
| } |
| } |
| } |
| release_wake_lock(WAKE_LOCK_NAME); |
| } |
| pthread_mutex_unlock(&stdev->lock); |
| } |
| |
| static void *callback_thread_loop(void *context) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)context; |
| struct pollfd fds[2]; |
| char msg[UEVENT_MSG_LEN]; |
| int exit_sockets[2]; |
| int err = 0; |
| int i, n; |
| int kwid = 0; |
| struct iaxxx_get_event_info ge; |
| void *payload = NULL; |
| unsigned int payload_size = 0, fw_status = IAXXX_FW_IDLE; |
| int fw_status_retries = 0; |
| |
| ALOGI("%s", __func__); |
| prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0); |
| |
| pthread_mutex_lock(&stdev->lock); |
| |
| if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) { |
| ALOGE("%s: Failed to create termination socket", __func__); |
| goto exit; |
| } |
| |
| stdev_close_term_sock(stdev); |
| stdev->send_sock = exit_sockets[0]; |
| stdev->recv_sock = exit_sockets[1]; |
| |
| memset(fds, 0, 2 * sizeof(struct pollfd)); |
| int timeout = -1; // Wait for event indefinitely |
| fds[0].events = POLLIN; |
| fds[0].fd = uevent_open_socket(64*1024, true); |
| if (fds[0].fd == -1) { |
| ALOGE("Error opening socket for hotplug uevent errno %d(%s)", |
| errno, strerror(errno)); |
| goto exit; |
| } |
| fds[1].events = POLLIN; |
| fds[1].fd = stdev->recv_sock; |
| |
| ge.event_id = -1; |
| |
| // Try to get the firmware status, if we fail, try for 10 times with a gap |
| // of 500ms, if we are unable to get the status after that then exit |
| do { |
| err = get_fw_status(stdev->odsp_hdl, &fw_status); |
| if (err == -1) { |
| ALOGE("%s: ERROR: Failed to get the firmware status %d(%s)", |
| __func__, errno, strerror(errno)); |
| usleep(RETRY_US); |
| fw_status_retries++; |
| } |
| } while (err != 0 && fw_status_retries < RETRY_NUMBER); |
| |
| if (err != 0) { |
| ALOGE("%s: ERROR: Failed to get firmware status after %d tries", |
| __func__, RETRY_NUMBER); |
| goto exit; |
| } |
| |
| if (fw_status == IAXXX_FW_ACTIVE) { |
| stdev->is_st_hal_ready = false; |
| // query version during reset progress. |
| get_hotword_info(stdev->odsp_hdl, |
| &stdev->hotword_version, |
| &hw_properties.supported_model_arch); |
| // reset the firmware and wait for firmware download complete |
| err = reset_fw(stdev->odsp_hdl); |
| if (err == -1) { |
| ALOGE("%s: ERROR: Failed to reset the firmware %d(%s)", |
| __func__, errno, strerror(errno)); |
| } |
| stdev->fw_reset_done_by_hal = true; |
| } else if (fw_status == IAXXX_FW_CRASH) { |
| // Firmware has crashed wait till it recovers |
| stdev->is_st_hal_ready = false; |
| } else if (fw_status == IAXXX_FW_IDLE) { |
| err = get_hotword_info(stdev->odsp_hdl, |
| &stdev->hotword_version, |
| &hw_properties.supported_model_arch); |
| if (stdev->hotword_version == HOTWORD_DEFAULT_VER) { |
| /* This is unlikely use-case, the codec is abnormal at the beginning |
| * reset_fw the firmware to recovery. |
| */ |
| stdev->is_st_hal_ready = false; |
| err = reset_fw(stdev->odsp_hdl); |
| if (err == -1) { |
| ALOGE("%s: ERROR: Failed to reset the firmware %d(%s)", |
| __func__, errno, strerror(errno)); |
| } |
| stdev->fw_reset_done_by_hal = true; |
| } else { |
| stdev->route_hdl = audio_route_init(stdev->snd_crd_num, |
| stdev->mixer_path_xml); |
| if (stdev->route_hdl == NULL) { |
| ALOGE("Failed to init the audio_route library"); |
| goto exit; |
| } |
| set_default_apll_clk(stdev->mixer); |
| setup_slpi_wakeup_event(stdev->odsp_hdl, true); |
| stdev->is_st_hal_ready = true; |
| } |
| } |
| pthread_mutex_unlock(&stdev->lock); |
| |
| while (1) { |
| err = poll(fds, 2, timeout); |
| |
| pthread_mutex_lock(&stdev->lock); |
| if (err < 0) { |
| ALOGE("%s: Error in poll: %d (%s)", |
| __func__, errno, strerror(errno)); |
| break; |
| } |
| |
| if (fds[0].revents & POLLIN) { |
| n = uevent_kernel_multicast_recv(fds[0].fd, msg, UEVENT_MSG_LEN); |
| if (n <= 0) { |
| pthread_mutex_unlock(&stdev->lock); |
| continue; |
| } |
| for (i = 0; i < n;) { |
| if (strstr(msg + i, IAXXX_VQ_EVENT_STR)) { |
| ALOGI("%s", IAXXX_VQ_EVENT_STR); |
| |
| err = get_event(stdev->odsp_hdl, &ge); |
| if (err == 0) { |
| if (ge.event_id == OK_GOOGLE_KW_ID) { |
| kwid = OK_GOOGLE_KW_ID; |
| } else if (ge.event_id == AMBIENT_KW_ID) { |
| kwid = AMBIENT_KW_ID; |
| reset_ambient_plugin(stdev->odsp_hdl); |
| } else if (ge.event_id == OSLO_EP_DISCONNECT) { |
| ALOGD("Eventid received is OSLO_EP_DISCONNECT %d", |
| OSLO_EP_DISCONNECT); |
| if (stdev->is_sensor_destroy_in_prog == true) { |
| // A timer would have been created during stop, |
| // check and delete it |
| if (stdev->ss_timer_created) { |
| timer_delete(stdev->ss_timer); |
| stdev->ss_timer_created = false; |
| } |
| |
| destroy_sensor_model(stdev); |
| } else { |
| ALOGE("Unexpected OSLO_EP_DISCONNECT received" |
| ", ignoring.."); |
| } |
| break; |
| } else if (ge.event_id == CHRE_EP_DISCONNECT) { |
| ALOGD("Eventid received is CHRE_EP_DISCONNECT %d", |
| CHRE_EP_DISCONNECT); |
| if (stdev->is_chre_destroy_in_prog == true) { |
| // A timer would have been created during stop, |
| // check and delete it |
| if (stdev->chre_timer_created) { |
| timer_delete(stdev->chre_timer); |
| stdev->chre_timer_created = false; |
| } |
| |
| destroy_chre_model(stdev); |
| } else { |
| ALOGE("Unexpected CHRE_EP_DISCONNECT received" |
| ", ignoring.."); |
| } |
| break; |
| } else if (ge.event_id == ENTITY_KW_ID) { |
| kwid = ENTITY_KW_ID; |
| } else if (ge.event_id == WAKEUP_KW_ID) { |
| kwid = WAKEUP_KW_ID; |
| } else { |
| ALOGE("Unknown event id received, ignoring %d", |
| ge.event_id); |
| } |
| break; |
| } else { |
| ALOGE("get_event failed with error %d", err); |
| } |
| } else if (strstr(msg + i, IAXXX_RECOVERY_EVENT_STR)) { |
| /* If the ST HAL did the firmware reset then that means |
| * that the userspace crashed so we need to reinit the audio |
| * route library, if we didn't reset the firmware, then it |
| * was genuine firmware crash and we don't need to reinit |
| * the audio route library. |
| */ |
| ALOGD("Firmware has redownloaded, start the recovery"); |
| if (stdev->fw_reset_done_by_hal == true) { |
| stdev->route_hdl = audio_route_init(stdev->snd_crd_num, |
| stdev->mixer_path_xml); |
| if (stdev->route_hdl == NULL) { |
| ALOGE("Failed to init the audio_route library"); |
| goto exit; |
| } |
| |
| stdev->fw_reset_done_by_hal = false; |
| } |
| |
| int err = crash_recovery(stdev); |
| if (err != 0) { |
| ALOGE("Crash recovery failed"); |
| } |
| pthread_cond_signal(&stdev->firmware_ready_cond); |
| } else if (strstr(msg + i, IAXXX_FW_DWNLD_SUCCESS_STR)) { |
| ALOGD("Firmware downloaded successfully"); |
| stdev->is_st_hal_ready = true; |
| set_default_apll_clk(stdev->mixer); |
| } else if (strstr(msg + i, IAXXX_FW_CRASH_EVENT_STR)) { |
| ALOGD("Firmware has crashed"); |
| // Don't allow any op on ST HAL until recovery is complete |
| stdev->is_st_hal_ready = false; |
| reset_all_route(stdev->route_hdl); |
| stdev->is_streaming = 0; |
| |
| // Firmware crashed, clear CHRE/Oslo timer and flags here |
| sensor_crash_handler(stdev); |
| chre_crash_handler(stdev); |
| } |
| |
| i += strlen(msg + i) + 1; |
| } |
| |
| if (ge.event_id == OK_GOOGLE_KW_ID || |
| ge.event_id == AMBIENT_KW_ID || |
| ge.event_id == ENTITY_KW_ID || |
| ge.event_id == WAKEUP_KW_ID) { |
| ALOGD("%s: Keyword ID %d", __func__, kwid); |
| |
| if (ge.data != 0) { |
| ALOGD("Size of payload is %d", ge.data); |
| payload_size = ge.data; |
| payload = malloc(payload_size); |
| if (payload != NULL) { |
| if (ge.event_id == AMBIENT_KW_ID || |
| ge.event_id == ENTITY_KW_ID) |
| err = get_entity_param_blk(stdev->odsp_hdl, |
| payload, |
| payload_size); |
| else if (ge.event_id == OK_GOOGLE_KW_ID || |
| ge.event_id == WAKEUP_KW_ID) |
| err = get_wakeup_param_blk(stdev->odsp_hdl, |
| payload, |
| payload_size); |
| if (err != 0) { |
| ALOGE("Failed to get payload data"); |
| free(payload); |
| payload = NULL; |
| payload_size = 0; |
| } |
| } else { |
| ALOGE("Failed to allocate memory for payload"); |
| } |
| } |
| int idx = find_handle_for_kw_id(stdev, kwid); |
| if (idx < MAX_MODELS && stdev->models[idx].is_active == true) { |
| int recognition_status = RECOGNITION_STATUS_SUCCESS; |
| if (stdev->models[idx].is_state_query == true) { |
| recognition_status = |
| RECOGNITION_STATUS_GET_STATE_RESPONSE; |
| |
| // We need to send this only once, so reset now |
| stdev->models[idx].is_state_query = false; |
| } |
| if (stdev->models[idx].type == SOUND_MODEL_TYPE_KEYPHRASE && |
| stdev->adnc_strm_handle[idx] == 0) { |
| struct sound_trigger_phrase_recognition_event *event; |
| event = (struct sound_trigger_phrase_recognition_event*) |
| stdev_keyphrase_event_alloc( |
| stdev->models[idx].model_handle, |
| stdev->models[idx].config, |
| recognition_status); |
| if (event) { |
| struct model_info *model; |
| model = &stdev->models[idx]; |
| |
| ALOGD("Sending recognition callback for id %d", |
| kwid); |
| /* Exit the critical section of interaction with |
| * firmware, release the lock to avoid deadlock |
| * when processing recognition event */ |
| pthread_mutex_unlock(&stdev->lock); |
| model->recognition_callback(&event->common, |
| model->recognition_cookie); |
| pthread_mutex_lock(&stdev->lock); |
| |
| free(event); |
| } else { |
| ALOGE("Failed to allocate memory for the event"); |
| } |
| } else if (stdev->models[idx].type == SOUND_MODEL_TYPE_GENERIC && |
| stdev->adnc_strm_handle[idx] == 0) { |
| struct sound_trigger_generic_recognition_event *event; |
| event = (struct sound_trigger_generic_recognition_event*) |
| stdev_generic_event_alloc( |
| stdev->models[idx].model_handle, |
| payload, |
| payload_size, |
| recognition_status); |
| if (event) { |
| struct model_info *model; |
| model = &stdev->models[idx]; |
| |
| ALOGD("Sending recognition callback for id %d", |
| kwid); |
| /* Exit the critical section of interaction with |
| * firmware, release the lock to avoid deadlock |
| * when processing recognition event */ |
| pthread_mutex_unlock(&stdev->lock); |
| model->recognition_callback(&event->common, |
| model->recognition_cookie); |
| pthread_mutex_lock(&stdev->lock); |
| free(event); |
| } else { |
| ALOGE("Failed to allocate memory for the event"); |
| } |
| } else if (stdev->adnc_strm_handle[idx] != 0) { |
| ALOGD("model %d is streaming.", idx); |
| } |
| } else { |
| ALOGE("Invalid id or keyword is not active, Subsume the event"); |
| } |
| // Reset all event related data |
| ge.event_id = -1; |
| ge.data = 0; |
| kwid = -1; |
| } |
| // Free the payload data |
| if (payload) { |
| free(payload); |
| payload = NULL; |
| payload_size = 0; |
| } |
| } else if (fds[1].revents & POLLIN) { |
| read(fds[1].fd, &n, sizeof(n)); /* clear the socket */ |
| ALOGD("%s: Termination message", __func__); |
| break; |
| } |
| else { |
| ALOGI("%s: Message ignored", __func__); |
| } |
| pthread_mutex_unlock(&stdev->lock); |
| } |
| |
| exit: |
| stdev_close_term_sock(stdev); |
| pthread_mutex_unlock(&stdev->lock); |
| |
| return (void *)(long)err; |
| } |
| |
| static int stdev_get_properties( |
| const struct sound_trigger_hw_device *dev __unused, |
| struct sound_trigger_properties *properties) |
| { |
| ALOGV("+%s+", __func__); |
| if (properties == NULL) |
| return -EINVAL; |
| memcpy(properties, &hw_properties.base, sizeof(struct sound_trigger_properties)); |
| ALOGV("-%s-", __func__); |
| return 0; |
| } |
| |
| /* Insert a decimal numeral for Prefix into the beginning of String. |
| Length specifies the total number of bytes available at str. |
| */ |
| static int int_prefix_str(char *str, size_t str_len, int prefix, const char *prefix_format) |
| { |
| int prefix_len = snprintf(NULL, 0, prefix_format, prefix); |
| size_t str_cur_len = strlen(str); |
| |
| if (str_len < prefix_len + str_cur_len + 1) |
| { |
| ALOGE("%s: Error, not enough space in string to insert prefix: %d, %s\n", |
| __func__, prefix, str); |
| return -1; |
| } |
| |
| // Move the string to make room for the prefix. |
| memmove(str + prefix_len, str, str_cur_len + 1); |
| |
| /* Remember the first character, because snprintf will overwrite it with a |
| null character. |
| */ |
| char tmp = str[0]; |
| |
| snprintf(str, prefix_len + 1, prefix_format, prefix); |
| str[prefix_len] = tmp; |
| return 0; |
| } |
| |
| static const struct sound_trigger_properties_header* stdev_get_properties_extended( |
| const struct sound_trigger_hw_device *dev __unused) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)dev; |
| ALOGV("+%s+", __func__); |
| pthread_mutex_lock(&stdev->lock); |
| if (hw_properties.header.version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_3) { |
| hw_properties.base.version = stdev->hotword_version; |
| |
| /* Per GMS requirement new to Android R, the supported_model_arch field |
| must be the Google hotword firmware version comma separated with the |
| supported_model_arch platform identifier. |
| */ |
| int_prefix_str(hw_properties.supported_model_arch, SOUND_TRIGGER_MAX_STRING_LEN, |
| hw_properties.base.version, "%u,"); |
| ALOGW("SoundTrigger supported model arch identifier is %s", |
| hw_properties.supported_model_arch); |
| } else { |
| ALOGE("STHAL Version is %u", hw_properties.header.version); |
| pthread_mutex_unlock(&stdev->lock); |
| return NULL; |
| } |
| |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGV("-%s-", __func__); |
| return &hw_properties.header; |
| } |
| |
| |
| static int stop_recognition(struct knowles_sound_trigger_device *stdev, |
| sound_model_handle_t handle) |
| { |
| int status = 0; |
| struct model_info *model = &stdev->models[handle]; |
| |
| if (model->config != NULL) { |
| dereg_hal_event_session(model->config, handle); |
| free(model->config); |
| model->config = NULL; |
| } |
| |
| model->recognition_callback = NULL; |
| model->recognition_cookie = NULL; |
| if (check_uuid_equality(model->uuid, stdev->chre_model_uuid) || |
| check_uuid_equality(model->uuid, stdev->sensor_model_uuid)) { |
| // This avoids any processing of chre/oslo. |
| goto exit; |
| } |
| if (can_update_recover_list(stdev) == true) { |
| update_recover_list(stdev, handle, false); |
| model->is_active = false; |
| goto exit; |
| } |
| if (model->is_active == false) { |
| ALOGW("%s: the model was disabled already", __func__); |
| goto exit; |
| } |
| |
| if (stdev->adnc_strm_handle[handle] != 0) { |
| ALOGD("%s: stop tunnling for index:%d", __func__, handle); |
| stdev->adnc_strm_close(stdev->adnc_strm_handle[handle]); |
| stdev->adnc_strm_handle[handle] = 0; |
| stdev->is_streaming--; |
| stdev->adnc_strm_last_read[handle] = reset_time; |
| } |
| |
| model->is_active = false; |
| |
| tear_package_route(stdev, model->uuid, stdev->is_bargein_route_enabled); |
| |
| destroy_package(stdev, model); |
| |
| |
| if (check_uuid_equality(model->uuid, stdev->hotword_model_uuid) || |
| (check_uuid_equality(model->uuid, stdev->wakeup_model_uuid))) { |
| if ((stdev->hotword_buffer_enable) && |
| !(stdev->current_enable & PLUGIN1_MASK)) { |
| tear_hotword_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| |
| if (check_uuid_equality(model->uuid, stdev->ambient_model_uuid) || |
| (check_uuid_equality(model->uuid, stdev->entity_model_uuid))) { |
| if ((stdev->music_buffer_enable) && |
| !(stdev->current_enable & PLUGIN2_MASK)) { |
| tear_music_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| |
| setup_buffer(stdev, model, false); |
| |
| handle_input_source(stdev, false); |
| |
| check_and_destroy_buffer_package(stdev); |
| |
| exit: |
| return status; |
| } |
| |
| static int stdev_load_sound_model(const struct sound_trigger_hw_device *dev, |
| struct sound_trigger_sound_model *sound_model, |
| sound_model_callback_t callback, |
| void *cookie, |
| sound_model_handle_t *handle) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)dev; |
| int ret = 0; |
| int kw_model_sz = 0; |
| int i = 0; |
| sound_trigger_uuid_t empty_uuid = {0}; |
| unsigned char *kw_buffer = NULL; |
| |
| ALOGD("+%s+", __func__); |
| pthread_mutex_lock(&stdev->lock); |
| |
| ret = check_firmware_ready(stdev); |
| if (ret != 0) |
| goto exit; |
| |
| if (handle == NULL || sound_model == NULL) { |
| ALOGE("%s: handle/sound_model is NULL", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (sound_model->data_size == 0 || |
| sound_model->data_offset < sizeof(struct sound_trigger_sound_model)) { |
| ALOGE("%s: Invalid sound model data", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (check_uuid_equality(sound_model->vendor_uuid, empty_uuid)) { |
| ALOGE("%s Invalid vendor uuid", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| // When a delayed CHRE/Oslo destroy process is in progress, |
| // we should not skip the new model and return the existing handle |
| // which will be destroyed soon. |
| if ((check_uuid_equality(sound_model->vendor_uuid, |
| stdev->chre_model_uuid) && |
| stdev->is_chre_destroy_in_prog) || |
| (check_uuid_equality(sound_model->vendor_uuid, |
| stdev->sensor_model_uuid) && |
| stdev->is_sensor_destroy_in_prog)) { |
| ALOGD("%s: CHRE/Oslo destroy in progress, skipped handle check.", |
| __func__); |
| } else { |
| i = find_handle_for_uuid(stdev, sound_model->vendor_uuid); |
| if (i != -1) { |
| ALOGW("%s: model is existed at index %d", __func__, i); |
| *handle = i; |
| goto exit; |
| } |
| } |
| |
| // Find an empty slot to load the model |
| i = find_empty_model_slot(stdev); |
| if (i == -1) { |
| ALOGE("%s: Can't load model no free slots available", __func__); |
| ret = -ENOSYS; |
| goto exit; |
| } |
| |
| kw_buffer = (unsigned char *) sound_model + sound_model->data_offset; |
| kw_model_sz = sound_model->data_size; |
| ALOGV("%s: kw_model_sz %d", __func__, kw_model_sz); |
| |
| stdev->models[i].data = malloc(kw_model_sz); |
| if (stdev->models[i].data == NULL) { |
| stdev->models[i].data_sz = 0; |
| ALOGE("%s: could not allocate memory for keyword model data", |
| __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } else { |
| memcpy(stdev->models[i].data, kw_buffer, kw_model_sz); |
| stdev->models[i].data_sz = kw_model_sz; |
| } |
| |
| // Send the keyword model to the chip only for hotword and ambient audio |
| if (check_uuid_equality(sound_model->vendor_uuid, |
| stdev->hotword_model_uuid)) { |
| stdev->models[i].kw_id = OK_GOOGLE_KW_ID; |
| } else if (check_uuid_equality(sound_model->vendor_uuid, |
| stdev->wakeup_model_uuid)) { |
| stdev->models[i].kw_id = WAKEUP_KW_ID; |
| } else if (check_uuid_equality(sound_model->vendor_uuid, |
| stdev->ambient_model_uuid)) { |
| stdev->models[i].kw_id = AMBIENT_KW_ID; |
| } else if (check_uuid_equality(sound_model->vendor_uuid, |
| stdev->entity_model_uuid)) { |
| stdev->models[i].kw_id = ENTITY_KW_ID; |
| } else if (check_uuid_equality(sound_model->vendor_uuid, |
| stdev->sensor_model_uuid)) { |
| ret = start_sensor_model(stdev); |
| if (ret) { |
| ALOGE("%s: ERROR: Failed to start sensor model", __func__); |
| goto error; |
| } |
| stdev->models[i].kw_id = USELESS_KW_ID; |
| } else if (check_uuid_equality(sound_model->vendor_uuid, |
| stdev->chre_model_uuid)) { |
| ret = start_chre_model(stdev, i); |
| if (ret) { |
| ALOGE("%s: ERROR: Failed to start chre model", __func__); |
| goto error; |
| } |
| stdev->models[i].kw_id = USELESS_KW_ID; |
| } else { |
| ALOGE("%s: ERROR: unknown model uuid", __func__); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| *handle = i; |
| ALOGV("%s: Loading model handle(%d) type(%d)", __func__, |
| *handle, sound_model->type); |
| // This will need to be replaced with UUID once they are fixed |
| stdev->models[i].model_handle = *handle; |
| stdev->models[i].type = sound_model->type; |
| stdev->models[i].uuid = sound_model->vendor_uuid; |
| stdev->models[i].sound_model_callback = callback; |
| stdev->models[i].sound_model_cookie = cookie; |
| stdev->models[i].recognition_callback = NULL; |
| stdev->models[i].recognition_cookie = NULL; |
| |
| stdev->models[i].is_loaded = true; |
| |
| error: |
| if (ret != 0) { |
| if (stdev->models[i].data) { |
| free(stdev->models[i].data); |
| stdev->models[i].data = NULL; |
| stdev->models[i].data_sz = 0; |
| } |
| if (!is_any_model_loaded(stdev) && stdev->is_buffer_package_loaded) { |
| destroy_buffer_package(stdev->odsp_hdl); |
| stdev->is_buffer_package_loaded = false; |
| } |
| } |
| |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGD("-%s handle %d-", __func__, *handle); |
| return ret; |
| } |
| |
| static int stdev_unload_sound_model(const struct sound_trigger_hw_device *dev, |
| sound_model_handle_t handle) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)dev; |
| int ret = 0; |
| ALOGD("+%s handle %d+", __func__, handle); |
| pthread_mutex_lock(&stdev->lock); |
| |
| ret = check_firmware_ready(stdev); |
| if (ret != 0) |
| goto exit; |
| |
| // Just confirm the model was previously loaded |
| if (stdev->models[handle].is_loaded == false) { |
| ALOGE("%s: Invalid model(%d) being called for unload", |
| __func__, handle); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (stdev->models[handle].is_active == true) { |
| ret = stop_recognition(stdev, handle); |
| if (ret) |
| goto exit; |
| } |
| |
| if (check_uuid_equality(stdev->models[handle].uuid, |
| stdev->sensor_model_uuid)) { |
| // Inform the Host 1 that sensor route/packages are about to be |
| // torndown and then wait for confirmation from Host 1 that it can be |
| // torndown. Also start a timer for 5 seconds, if the Host 1 doesn't |
| // send us the event within 5 seconds we force remove the sensor pkgs |
| if (stdev->is_sensor_route_enabled == true) { |
| struct itimerspec ss_timer_spec; |
| struct sigevent ss_sigevent; |
| |
| // Inform the host 1 |
| stdev->is_sensor_destroy_in_prog = true; |
| trigger_sensor_destroy_event(stdev->odsp_hdl); |
| |
| // Start timer for 5 seconds |
| ss_sigevent.sigev_notify = SIGEV_THREAD; |
| ss_sigevent.sigev_notify_function = sensor_timeout_recover; |
| ss_sigevent.sigev_notify_attributes = NULL; |
| |
| ss_timer_spec.it_interval.tv_sec = 0; |
| ss_timer_spec.it_interval.tv_nsec = 0; |
| ss_timer_spec.it_value.tv_sec = |
| SENSOR_CREATE_WAIT_TIME_IN_S * SENSOR_CREATE_WAIT_MAX_COUNT; |
| ss_timer_spec.it_value.tv_nsec = 0; |
| |
| if (stdev->ss_timer_created) { |
| timer_delete(stdev->ss_timer); |
| stdev->ss_timer_created = false; |
| } |
| |
| if (timer_create(CLOCK_REALTIME, |
| &ss_sigevent, &stdev->ss_timer) == -1) { |
| ALOGE("%s: Timer Create Failed", __func__); |
| } else { |
| stdev->ss_timer_created = true; |
| if (timer_settime(stdev->ss_timer, |
| 0, &ss_timer_spec, NULL) == -1) { |
| ALOGE("%s: Timer Set Failed", __func__); |
| } |
| } |
| } |
| } else if (check_uuid_equality(stdev->models[handle].uuid, |
| stdev->chre_model_uuid)) { |
| // remove chre from recover list |
| if (can_update_recover_list(stdev) == true) |
| update_recover_list(stdev, handle, false); |
| |
| // Disable the CHRE route |
| if (stdev->is_chre_loaded == true) { |
| struct itimerspec chre_timer_spec; |
| struct sigevent chre_sigevent; |
| |
| // Inform the host 1 |
| stdev->is_chre_destroy_in_prog = true; |
| trigger_chre_destroy_event(stdev->odsp_hdl); |
| |
| // Start timer for 5 seconds |
| chre_sigevent.sigev_notify = SIGEV_THREAD; |
| chre_sigevent.sigev_notify_function = chre_timeout_recover; |
| chre_sigevent.sigev_notify_attributes = NULL; |
| |
| chre_timer_spec.it_interval.tv_sec = 0; |
| chre_timer_spec.it_interval.tv_nsec = 0; |
| chre_timer_spec.it_value.tv_sec = |
| CHRE_CREATE_WAIT_TIME_IN_S * CHRE_CREATE_WAIT_MAX_COUNT; |
| chre_timer_spec.it_value.tv_nsec = 0; |
| |
| if (stdev->chre_timer_created) { |
| timer_delete(stdev->chre_timer); |
| stdev->chre_timer_created = false; |
| } |
| |
| if (timer_create(CLOCK_REALTIME, |
| &chre_sigevent, &stdev->chre_timer) == -1) { |
| ALOGE("%s: Timer Create Failed", __func__); |
| } else { |
| stdev->chre_timer_created = true; |
| if (timer_settime(stdev->chre_timer, |
| 0, &chre_timer_spec, NULL) == -1) { |
| ALOGE("%s: Timer Set Failed", __func__); |
| } |
| } |
| } |
| } |
| |
| stdev->models[handle].sound_model_callback = NULL; |
| stdev->models[handle].sound_model_cookie = NULL; |
| |
| if (!(check_uuid_equality(stdev->models[handle].uuid, |
| stdev->sensor_model_uuid) && |
| stdev->is_sensor_destroy_in_prog) && |
| !(check_uuid_equality(stdev->models[handle].uuid, |
| stdev->chre_model_uuid) && |
| stdev->is_chre_destroy_in_prog)) { |
| memset(&stdev->models[handle].uuid, 0, sizeof(sound_trigger_uuid_t)); |
| stdev->models[handle].is_loaded = false; |
| } |
| |
| if (stdev->models[handle].data) { |
| free(stdev->models[handle].data); |
| stdev->models[handle].data = NULL; |
| stdev->models[handle].data_sz = 0; |
| } |
| |
| ALOGD("%s: Successfully unloaded the model, handle - %d", |
| __func__, handle); |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGD("-%s handle %d-", __func__, handle); |
| return ret; |
| } |
| |
| static int stdev_start_recognition( |
| const struct sound_trigger_hw_device *dev, |
| sound_model_handle_t handle, |
| const struct sound_trigger_recognition_config *config, |
| recognition_callback_t callback, |
| void *cookie) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)dev; |
| int status = 0; |
| struct model_info *model = &stdev->models[handle]; |
| |
| ALOGD("%s stdev %p, sound model %d", __func__, stdev, handle); |
| pthread_mutex_lock(&stdev->lock); |
| |
| status = check_firmware_ready(stdev); |
| if (status != 0) |
| goto exit; |
| |
| if (callback == NULL) { |
| ALOGE("%s: recognition_callback is null", __func__); |
| status = -EINVAL; |
| goto exit; |
| } |
| |
| if (model->config != NULL) { |
| dereg_hal_event_session(model->config, handle); |
| free(model->config); |
| model->config = NULL; |
| } |
| |
| if (config != NULL) { |
| model->config = (struct sound_trigger_recognition_config *) |
| malloc(sizeof(*config)); |
| if (model->config == NULL) { |
| ALOGE("%s: Failed to allocate memory for model config", __func__); |
| status = -ENOMEM; |
| goto exit; |
| } |
| |
| memcpy(model->config, config, sizeof(*config)); |
| reg_hal_event_session(model->config, handle); |
| |
| ALOGD("%s: Is capture requested %d", |
| __func__, config->capture_requested); |
| } else { |
| ALOGD("%s: config is null", __func__); |
| model->config = NULL; |
| } |
| |
| model->recognition_callback = callback; |
| model->recognition_cookie = cookie; |
| if (check_uuid_equality(model->uuid, stdev->chre_model_uuid) || |
| check_uuid_equality(model->uuid, stdev->sensor_model_uuid)) { |
| // This avoids any processing of chre/oslo. |
| goto exit; |
| } |
| if (model->is_active == true) { |
| // This model is already active, do nothing except updating callbacks, |
| // configs and cookie |
| goto exit; |
| } |
| if (can_update_recover_list(stdev) == true) { |
| // Device is in voice/VoIP call, add model to recover list first |
| // recover model once voice/VoIP is ended. |
| update_recover_list(stdev, handle, true); |
| goto exit; |
| } |
| |
| status = check_and_setup_buffer_package(stdev); |
| if (status != 0) { |
| ALOGE("%s: ERROR: Failed to load the buffer package", __func__); |
| } |
| |
| model->is_active = true; |
| |
| handle_input_source(stdev, true); |
| |
| if (stdev->is_buffer_package_loaded == true) { |
| setup_buffer(stdev, model, true); |
| } |
| |
| if (check_uuid_equality(model->uuid, stdev->hotword_model_uuid) || |
| (check_uuid_equality(model->uuid, stdev->wakeup_model_uuid))) { |
| if ((stdev->hotword_buffer_enable) && |
| (!(stdev->current_enable & HOTWORD_MASK) || |
| (stdev->current_enable & WAKEUP_MASK))) { |
| set_hotword_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| |
| if (check_uuid_equality(model->uuid, stdev->ambient_model_uuid) || |
| (check_uuid_equality(model->uuid, stdev->entity_model_uuid))) { |
| if ((stdev->music_buffer_enable) && |
| (!(stdev->current_enable & AMBIENT_MASK) || |
| (stdev->current_enable & ENTITY_MASK))) { |
| set_music_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| } |
| } |
| |
| setup_package(stdev, model); |
| |
| set_package_route(stdev, model->uuid, stdev->is_bargein_route_enabled); |
| |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGD("-%s sound model %d-", __func__, handle); |
| return status; |
| } |
| |
| static int stdev_start_recognition_extended( |
| const struct sound_trigger_hw_device *dev, |
| sound_model_handle_t sound_model_handle, |
| const struct sound_trigger_recognition_config_header *header, |
| recognition_callback_t callback, |
| void *cookie) |
| { |
| struct sound_trigger_recognition_config_extended_1_3 *config_1_3 = |
| (struct sound_trigger_recognition_config_extended_1_3 *)header; |
| int status = 0; |
| |
| if (header->version >= SOUND_TRIGGER_DEVICE_API_VERSION_1_3) { |
| /* Use old version before we have real usecase */ |
| ALOGD("%s: Running 2_3", __func__); |
| status = stdev_start_recognition(dev, sound_model_handle, |
| &config_1_3->base, |
| callback, |
| cookie); |
| } else { |
| /* Rollback into old start recognition */ |
| ALOGD("%s: Running 2_1", __func__); |
| status = stdev_start_recognition(dev, sound_model_handle, |
| &config_1_3->base, |
| callback, |
| cookie); |
| } |
| |
| return status; |
| } |
| |
| |
| static int stdev_stop_recognition( |
| const struct sound_trigger_hw_device *dev, |
| sound_model_handle_t handle) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)dev; |
| int status = 0; |
| ALOGD("+%s sound model %d+", __func__, handle); |
| pthread_mutex_lock(&stdev->lock); |
| |
| status = check_firmware_ready(stdev); |
| if (status != 0) |
| goto exit; |
| |
| status = stop_recognition(stdev, handle); |
| |
| if (status != 0) |
| goto exit; |
| |
| ALOGD("-%s sound model %d-", __func__, handle); |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| |
| return status; |
| } |
| |
| /** |
| * Get the state of a given model. |
| * The model state is returned asynchronously as a RecognitionEvent via |
| * the callback that was registered in StartRecognition(). |
| * @param modelHandle The handle of the sound model whose state is being |
| * queried. |
| * @return retval Operation completion status: 0 in case of success, |
| * -ENOSYS in case of invalid model handle, |
| * -ENOMEM in case of memory allocation failure, |
| * -ENODEV in case of initialization error, |
| * -EINVAL in case where a recognition event is already |
| * being processed. |
| */ |
| static int stdev_get_model_state(const struct sound_trigger_hw_device *dev, |
| sound_model_handle_t sound_model_handle) { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)dev; |
| struct model_info *model = &stdev->models[sound_model_handle]; |
| int ret = 0; |
| ALOGD("+%s+", __func__); |
| pthread_mutex_lock(&stdev->lock); |
| |
| ret = check_firmware_ready(stdev); |
| if (ret != 0) |
| goto exit; |
| |
| if (!stdev->opened) { |
| ALOGE("%s: stdev isn't initialized", __func__); |
| ret = -ENODEV; |
| goto exit; |
| } |
| |
| if (model->is_active == false && |
| !is_uuid_in_recover_list(stdev, sound_model_handle)) { |
| ALOGE("%s: ERROR: %d model is not active", |
| __func__, sound_model_handle); |
| ret = -ENOSYS; |
| goto exit; |
| } else if (is_uuid_in_recover_list(stdev, sound_model_handle)) { |
| ALOGD("%s: Ignore %d model request due to call active", |
| __func__, sound_model_handle); |
| goto exit; |
| } |
| |
| if (model->is_state_query == true) { |
| ALOGE("%s: ERROR: model %d is already processing", |
| __func__, sound_model_handle); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| model->is_state_query = true; |
| |
| if (check_uuid_equality(model->uuid, stdev->hotword_model_uuid)) |
| ret = get_model_state(stdev->odsp_hdl, HOTWORD_INSTANCE_ID, |
| HOTWORD_SLOT_ID); |
| else if (check_uuid_equality(model->uuid, stdev->wakeup_model_uuid)) |
| ret = get_model_state(stdev->odsp_hdl, HOTWORD_INSTANCE_ID, |
| WAKEUP_SLOT_ID); |
| else if (check_uuid_equality(model->uuid, stdev->ambient_model_uuid)) |
| ret = get_model_state(stdev->odsp_hdl, AMBIENT_INSTANCE_ID, |
| AMBIENT_SLOT_ID); |
| else if (check_uuid_equality(model->uuid, stdev->entity_model_uuid)) { |
| ret = get_model_state(stdev->odsp_hdl, AMBIENT_INSTANCE_ID, |
| ENTITY_SLOT_ID); |
| } else { |
| ALOGE("%s: ERROR: %d model is not supported", |
| __func__, sound_model_handle); |
| ret = -ENOSYS; |
| } |
| |
| if (ret != 0) { |
| model->is_state_query = false; |
| ALOGE("%s: ERROR: Failed to get the model state", __func__); |
| } |
| |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGD("-%s-", __func__); |
| return ret; |
| } |
| |
| static int stdev_query_parameter( |
| const struct sound_trigger_hw_device *dev __unused, |
| sound_model_handle_t sound_model_handle __unused, |
| sound_trigger_model_parameter_t model_param __unused, |
| sound_trigger_model_parameter_range_t* param_range) |
| { |
| ALOGW("%s: NOT SUPPORTED", __func__); |
| param_range->is_supported = false; |
| return 0; |
| } |
| |
| static int stdev_set_parameter( |
| const struct sound_trigger_hw_device *dev __unused, |
| sound_model_handle_t sound_model_handle __unused, |
| sound_trigger_model_parameter_t model_param __unused, |
| int32_t value __unused) |
| { |
| ALOGW("%s: NOT SUPPORTED", __func__); |
| return -EINVAL; |
| } |
| |
| static int stdev_get_parameter( |
| const struct sound_trigger_hw_device *dev __unused, |
| sound_model_handle_t sound_model_handle __unused, |
| sound_trigger_model_parameter_t model_param __unused, |
| int32_t* value __unused) |
| { |
| ALOGW("%s: NOT SUPPORTED", __func__); |
| return -EINVAL; |
| } |
| |
| |
| static int stdev_close(hw_device_t *device) |
| { |
| struct knowles_sound_trigger_device *stdev = |
| (struct knowles_sound_trigger_device *)device; |
| int ret = 0; |
| ALOGD("+%s+", __func__); |
| pthread_mutex_lock(&stdev->lock); |
| |
| ret = check_firmware_ready(stdev); |
| if (ret != 0) |
| goto exit; |
| |
| if (!stdev->opened) { |
| ALOGE("%s: device already closed", __func__); |
| ret = -EFAULT; |
| goto exit; |
| } |
| |
| setup_slpi_wakeup_event(stdev->odsp_hdl, false); |
| |
| stdev->opened = false; |
| |
| if (stdev->send_sock >= 0) |
| write(stdev->send_sock, "T", 1); |
| pthread_join(stdev->callback_thread, (void **)NULL); |
| |
| if (stdev->route_hdl) |
| audio_route_free(stdev->route_hdl); |
| if (stdev->odsp_hdl) |
| iaxxx_odsp_deinit(stdev->odsp_hdl); |
| |
| if (stdev->ss_timer_created) { |
| timer_delete(stdev->ss_timer); |
| stdev->ss_timer_created = false; |
| } |
| |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| ALOGD("-%s-", __func__); |
| return ret; |
| } |
| |
| static int open_streaming_lib(struct knowles_sound_trigger_device *stdev) { |
| int ret = 0; |
| |
| if (access(ADNC_STRM_LIBRARY_PATH, R_OK) == 0) { |
| stdev->adnc_cvq_strm_lib = dlopen(ADNC_STRM_LIBRARY_PATH, RTLD_NOW); |
| if (stdev->adnc_cvq_strm_lib == NULL) { |
| char const *err_str = dlerror(); |
| ALOGE("%s: module = %s error = %s", __func__, |
| ADNC_STRM_LIBRARY_PATH, err_str ? err_str : "unknown"); |
| ALOGE("%s: DLOPEN failed for %s", __func__, ADNC_STRM_LIBRARY_PATH); |
| } else { |
| ALOGV("%s: DLOPEN successful for %s", |
| __func__, ADNC_STRM_LIBRARY_PATH); |
| for (int index = 0; index < MAX_MODELS; index++) { |
| stdev->adnc_strm_handle[index] = 0; |
| stdev->adnc_strm_last_read[index] = reset_time; |
| } |
| stdev->adnc_strm_open = |
| (int (*)(bool, int, int))dlsym(stdev->adnc_cvq_strm_lib, |
| "adnc_strm_open"); |
| stdev->adnc_strm_read = |
| (size_t (*)(long, void *, size_t))dlsym(stdev->adnc_cvq_strm_lib, |
| "adnc_strm_read"); |
| stdev->adnc_strm_close = |
| (int (*)(long))dlsym(stdev->adnc_cvq_strm_lib, |
| "adnc_strm_close"); |
| if (!stdev->adnc_strm_open || !stdev->adnc_strm_read || |
| !stdev->adnc_strm_close) { |
| ALOGE("%s: Error grabbing functions in %s", __func__, |
| ADNC_STRM_LIBRARY_PATH); |
| stdev->adnc_strm_open = 0; |
| stdev->adnc_strm_read = 0; |
| stdev->adnc_strm_close = 0; |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static struct mixer* find_stdev_mixer_path(int card_num, char *mixer_path_xml) |
| { |
| struct mixer *mixer = NULL; |
| const char *in_snd_card_name; |
| char *snd_card_name = NULL; |
| char *tmp = NULL; |
| char *platform = NULL; |
| char *snd_card = NULL; |
| char *device = NULL; |
| |
| mixer = mixer_open(card_num); |
| |
| if (!mixer) { |
| ALOGE("%s: Unable to open the mixer: %d", __func__, |
| card_num); |
| return NULL; |
| } |
| |
| in_snd_card_name = mixer_get_name(mixer); |
| snd_card_name = strdup(in_snd_card_name); |
| |
| if (snd_card_name == NULL) { |
| ALOGE("%s: snd_card_name is NULL", __func__); |
| goto on_error; |
| } |
| |
| platform = strtok_r(snd_card_name, "-", &tmp); |
| if (platform == NULL) { |
| ALOGE("%s: snd card is invalid", __func__); |
| goto on_error; |
| } |
| |
| snd_card = strtok_r(NULL, "-", &tmp); |
| if (snd_card == NULL) { |
| ALOGE("%s: snd card is invalid", __func__); |
| goto on_error; |
| } |
| |
| device = strtok_r(NULL, "-", &tmp); |
| if (device != NULL) { |
| snprintf(mixer_path_xml, NAME_MAX_SIZE, "%s_%s.xml", |
| SOUND_TRIGGER_MIXER_PATH_BASE, device); |
| } else { |
| ALOGE("%s: Unknown device, try to use default xml", __func__); |
| snprintf(mixer_path_xml, NAME_MAX_SIZE, "%s", |
| SOUND_TRIGGER_MIXER_PATH_XML); |
| } |
| |
| ALOGD("%s: using %s", __func__, mixer_path_xml); |
| |
| on_error: |
| if (snd_card_name) |
| free(snd_card_name); |
| return mixer; |
| } |
| |
| static int find_sound_card() { |
| int retry_num = 0, snd_card_num = 0, ret = -1; |
| const char *snd_card_name; |
| struct mixer *mixer = NULL; |
| bool card_verifed[MAX_SND_CARD] = {false}; |
| const int retry_limit = property_get_int32("vendor.audio.snd_card.open.retries", |
| RETRY_NUMBER); |
| ALOGD("+%s+", __func__); |
| |
| for (;;) { |
| if (snd_card_num >= MAX_SND_CARD) { |
| if (retry_num++ >= retry_limit) { |
| ALOGE("%s: iaxxx sound card not found", __func__); |
| goto exit; |
| } |
| snd_card_num = 0; |
| usleep(RETRY_US); |
| continue; |
| } |
| if (card_verifed[snd_card_num]) { |
| snd_card_num++; |
| continue; |
| } |
| |
| mixer = mixer_open(snd_card_num); |
| if (!mixer) { |
| snd_card_num++; |
| continue; |
| } |
| |
| snd_card_name = mixer_get_name(mixer); |
| if (strstr(snd_card_name, CARD_NAME)) { |
| ALOGD("%s: find card %d has iaxxx - %s", |
| __func__, snd_card_num, snd_card_name); |
| ret = snd_card_num; |
| break; |
| } |
| |
| ALOGD("%s: sound card %s does NOT have iaxxx", __func__, snd_card_name); |
| mixer_close(mixer); |
| mixer = NULL; |
| card_verifed[snd_card_num] = true; |
| snd_card_num++; |
| } |
| |
| exit: |
| if (mixer) |
| mixer_close(mixer); |
| |
| ALOGD("-%s-", __func__); |
| return ret; |
| } |
| |
| static int load_audio_hal() |
| { |
| char audio_hal_lib[100]; |
| void *sthal_prop_api_version = NULL; |
| struct knowles_sound_trigger_device *stdev = &g_stdev; |
| int ret = 0; |
| |
| snprintf(audio_hal_lib, sizeof(audio_hal_lib), "%s/%s.%s.so", |
| AUDIO_HAL_LIBRARY_PATH, AUDIO_HAL_NAME_PREFIX, |
| SOUND_TRIGGER_PLATFORM); |
| if (access(audio_hal_lib, R_OK)) { |
| ALOGE("%s: ERROR. %s not found", __func__, audio_hal_lib); |
| return -ENOENT; |
| } |
| |
| stdev->audio_hal_handle = dlopen(audio_hal_lib, RTLD_NOW); |
| if (stdev->audio_hal_handle == NULL) { |
| ALOGE("%s: ERROR. %s", __func__, dlerror()); |
| return -ENODEV; |
| } |
| |
| stdev->audio_hal_cb = dlsym(stdev->audio_hal_handle, "audio_hw_call_back"); |
| if (stdev->audio_hal_cb == NULL) { |
| ALOGE("%s: ERROR. %s", __func__, dlerror()); |
| ret = -ENODEV; |
| goto error; |
| } |
| |
| sthal_prop_api_version = dlsym(stdev->audio_hal_handle, |
| "sthal_prop_api_version"); |
| if (sthal_prop_api_version == NULL) { |
| stdev->sthal_prop_api_version = 0; |
| ret = 0; /* passthru for backward compability */ |
| } else { |
| stdev->sthal_prop_api_version = *(int *)sthal_prop_api_version; |
| if (MAJOR_VERSION(stdev->sthal_prop_api_version) != |
| MAJOR_VERSION(STHAL_PROP_API_CURRENT_VERSION)) { |
| ALOGE("%s: Incompatible API versions sthal:0x%x != ahal:0x%x", |
| __func__, STHAL_PROP_API_CURRENT_VERSION, |
| stdev->sthal_prop_api_version); |
| goto error; |
| } |
| ALOGD("%s: ahal is using proprietary API version 0x%04x", __func__, |
| stdev->sthal_prop_api_version); |
| } |
| |
| ALOGD("%s: load AHAL successfully.", __func__); |
| return ret; |
| |
| error: |
| dlclose(stdev->audio_hal_handle); |
| stdev->audio_hal_handle = NULL; |
| return ret; |
| } |
| |
| static int stdev_open(const hw_module_t *module, const char *name, |
| hw_device_t **device) |
| { |
| struct knowles_sound_trigger_device *stdev; |
| int ret = 0, i = 0; |
| int snd_card_num = 0; |
| |
| ALOGE("!! Knowles SoundTrigger v1!!"); |
| |
| if (strcmp(name, SOUND_TRIGGER_HARDWARE_INTERFACE) != 0) |
| return -EINVAL; |
| |
| if (device == NULL) |
| return -EINVAL; |
| |
| stdev = &g_stdev; |
| pthread_mutex_lock(&stdev->lock); |
| |
| snd_card_num = find_sound_card(); |
| if (snd_card_num == -1) { |
| ALOGE("%s: Unable to find the sound card %s", __func__, CARD_NAME); |
| ret = -EAGAIN; |
| goto exit; |
| } |
| |
| if (stdev->opened) { |
| ALOGE("%s: Only one sountrigger can be opened at a time", __func__); |
| ret = -EBUSY; |
| goto exit; |
| } |
| |
| ret = open_streaming_lib(stdev); |
| if (ret != 0) { |
| ALOGE("%s: Couldnot open the streaming library", __func__); |
| goto error; |
| } |
| |
| ret = load_audio_hal(); |
| if (ret != 0) { |
| ALOGE("%s: Couldn't load AHAL", __func__); |
| goto error; |
| } |
| |
| stdev->device.common.tag = HARDWARE_DEVICE_TAG; |
| stdev->device.common.version = SOUND_TRIGGER_DEVICE_API_VERSION_1_3; |
| stdev->device.common.module = (struct hw_module_t *)module; |
| stdev->device.common.close = stdev_close; |
| stdev->device.get_properties = stdev_get_properties; |
| stdev->device.load_sound_model = stdev_load_sound_model; |
| stdev->device.unload_sound_model = stdev_unload_sound_model; |
| stdev->device.start_recognition = stdev_start_recognition; |
| stdev->device.start_recognition_extended = stdev_start_recognition_extended; |
| stdev->device.stop_recognition = stdev_stop_recognition; |
| stdev->device.get_model_state = stdev_get_model_state; |
| stdev->device.query_parameter = stdev_query_parameter; |
| stdev->device.set_parameter = stdev_set_parameter; |
| stdev->device.get_parameter = stdev_get_parameter; |
| stdev->device.get_properties_extended = stdev_get_properties_extended; |
| |
| stdev->opened = true; |
| stdev->send_sock = -1; |
| stdev->recv_sock = -1; |
| |
| /* Initialize all member variable */ |
| for (i = 0; i < MAX_MODELS; i++) { |
| stdev->models[i].type = SOUND_MODEL_TYPE_UNKNOWN; |
| memset(&stdev->models[i].uuid, 0, sizeof(sound_trigger_uuid_t)); |
| stdev->models[i].config = NULL; |
| stdev->models[i].data = NULL; |
| stdev->models[i].data_sz = 0; |
| stdev->models[i].is_loaded = false; |
| stdev->models[i].is_active = false; |
| stdev->models[i].is_state_query = false; |
| } |
| |
| stdev->is_mic_route_enabled = false; |
| stdev->is_con_mic_route_enabled = false; |
| stdev->is_ahal_in_voice_voip_mode = false; |
| stdev->is_ahal_voice_voip_stop = false; |
| stdev->is_ahal_voice_voip_start = false; |
| stdev->is_bargein_route_enabled = false; |
| stdev->is_chre_loaded = false; |
| stdev->is_buffer_package_loaded = false; |
| stdev->hotword_buffer_enable = 0; |
| stdev->music_buffer_enable = 0; |
| stdev->current_enable = 0; |
| stdev->is_sensor_route_enabled = false; |
| stdev->recover_model_list = 0; |
| stdev->rx_active_count = 0; |
| stdev->is_ahal_media_recording = false; |
| stdev->is_concurrent_capture = hw_properties.base.concurrent_capture; |
| |
| stdev->is_sensor_destroy_in_prog = false; |
| stdev->ss_timer_created = false; |
| |
| stdev->is_chre_destroy_in_prog = false; |
| stdev->chre_timer_created = false; |
| |
| stdev->snd_crd_num = snd_card_num; |
| stdev->fw_reset_done_by_hal = false; |
| stdev->hotword_version = 0; |
| |
| str_to_uuid(HOTWORD_AUDIO_MODEL, &stdev->hotword_model_uuid); |
| str_to_uuid(WAKEUP_MODEL, &stdev->wakeup_model_uuid); |
| str_to_uuid(SENSOR_MANAGER_MODEL, &stdev->sensor_model_uuid); |
| str_to_uuid(AMBIENT_AUDIO_MODEL, &stdev->ambient_model_uuid); |
| str_to_uuid(CHRE_AUDIO_MODEL, &stdev->chre_model_uuid); |
| str_to_uuid(ENTITY_AUDIO_MODEL, &stdev->entity_model_uuid); |
| |
| stdev->odsp_hdl = iaxxx_odsp_init(); |
| if (stdev->odsp_hdl == NULL) { |
| ALOGE("%s: Failed to get handle to ODSP HAL", __func__); |
| ret = -EIO; |
| goto error; |
| } |
| stdev->mixer = find_stdev_mixer_path(stdev->snd_crd_num, stdev->mixer_path_xml); |
| if (stdev->mixer == NULL) { |
| ALOGE("Failed to init the mixer"); |
| ret = -EAGAIN; |
| goto error; |
| } |
| |
| ALOGD("stdev before pthread_create %p", stdev); |
| // Create a thread to handle all events from kernel |
| pthread_create(&stdev->callback_thread, (const pthread_attr_t *) NULL, |
| callback_thread_loop, stdev); |
| |
| pthread_create(&stdev->monitor_thread, (const pthread_attr_t *) NULL, |
| monitor_thread_loop, stdev); |
| |
| pthread_create(&stdev->transitions_thread, (const pthread_attr_t *) NULL, |
| transitions_thread_loop, stdev); |
| |
| *device = &stdev->device.common; /* same address as stdev */ |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| return ret; |
| |
| error: |
| if (stdev->adnc_cvq_strm_lib) |
| dlclose(stdev->adnc_cvq_strm_lib); |
| if (stdev->audio_hal_handle) |
| dlclose(stdev->audio_hal_handle); |
| if (stdev->route_hdl) |
| audio_route_free(stdev->route_hdl); |
| if (stdev->odsp_hdl) |
| iaxxx_odsp_deinit(stdev->odsp_hdl); |
| if (stdev->mixer) |
| mixer_close(stdev->mixer); |
| |
| pthread_mutex_unlock(&stdev->lock); |
| return ret; |
| } |
| |
| /* AHAL calls this callback to communicate with STHAL */ |
| int sound_trigger_hw_call_back(audio_event_type_t event, |
| struct audio_event_info *config) |
| { |
| int ret = 0; |
| int i = 0; |
| int index = -1; |
| struct knowles_sound_trigger_device *stdev = &g_stdev; |
| enum sthal_mode pre_mode, cur_mode; |
| |
| if (!stdev) |
| return -ENODEV; |
| |
| if (!stdev->opened) { |
| ALOGE("%s: Error SoundTrigger has not been opened", __func__); |
| return -EINVAL; |
| } |
| |
| pthread_mutex_lock(&stdev->lock); |
| |
| // update conditions for mic concurrency whatever firmware status may be. |
| if (event == AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE || |
| event == AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE || |
| event == AUDIO_EVENT_CAPTURE_STREAM_INACTIVE || |
| event == AUDIO_EVENT_CAPTURE_STREAM_ACTIVE) { |
| pre_mode = get_sthal_mode(stdev); |
| update_sthal_conditions(stdev, event, config); |
| cur_mode = get_sthal_mode(stdev); |
| } |
| |
| // update conditions for bargein whatever firmware status may be. |
| if (event == AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE || |
| event == AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE) { |
| update_rx_conditions(stdev, event, config); |
| } |
| |
| if (stdev->is_st_hal_ready == false) { |
| ALOGE("%s: ST HAL is not ready yet", __func__); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| switch (event) { |
| case AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE: |
| case AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE: |
| ALOGD("%s: handle capture device event %d", __func__, event); |
| |
| //handle capture device on/off event |
| do_handle_functions(stdev, pre_mode, cur_mode, event); |
| |
| break; |
| case AUDIO_EVENT_CAPTURE_STREAM_INACTIVE: |
| case AUDIO_EVENT_CAPTURE_STREAM_ACTIVE: |
| ALOGD("%s: handle capture stream event %d, usecase %d", |
| __func__, event, config->u.usecase.type); |
| |
| break; |
| case AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE: |
| ALOGD("%s: handle playback stream inactive", __func__); |
| |
| if (stdev->rx_active_count == 0) { |
| if (stdev->is_mic_route_enabled != false) { |
| // Atleast one keyword model is active so update the routes |
| // Check if the bargein route is enabled if not enable bargein route |
| // Check each model, if it is active then update it's route |
| if (stdev->is_bargein_route_enabled != false) { |
| ALOGD("Bargein disabling"); |
| stdev->is_bargein_route_enabled = false; |
| // Check each model, if it is active then update it's route |
| // Disable the bargein route |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active == true) { |
| // teardown the package route with bargein |
| ret = tear_package_route(stdev, |
| stdev->models[i].uuid, |
| !stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to tear old package route"); |
| } |
| // resetup the package route with out bargein |
| ret = set_package_route(stdev, |
| stdev->models[i].uuid, |
| stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to enable package route"); |
| } |
| } |
| } |
| |
| //Switch buffer input source |
| if (stdev->hotword_buffer_enable) { |
| ret = tear_hotword_buffer_route(stdev->route_hdl, |
| !stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to tear old buffer route"); |
| } |
| ret = set_hotword_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to enable buffer route"); |
| } |
| } |
| |
| if (stdev->music_buffer_enable) { |
| ret = tear_music_buffer_route(stdev->route_hdl, |
| !stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to tear old music buffer route"); |
| } |
| ret = set_music_buffer_route(stdev->route_hdl, |
| stdev->is_bargein_route_enabled); |
| if (ret != 0) { |
| ALOGE("Failed to enable buffer route"); |
| } |
| } |
| |
| ret = enable_bargein_route(stdev->route_hdl, false); |
| if (ret != 0) { |
| ALOGE("Failed to enable buffer route"); |
| } |
| |
| ret = destroy_aec_package(stdev->odsp_hdl); |
| if (ret != 0) { |
| ALOGE("Failed to destroy AEC package"); |
| } |
| |
| ret = enable_src_route(stdev->route_hdl, false, SRC_AMP_REF); |
| if (ret != 0) { |
| ALOGE("Failed to disable SRC-amp route"); |
| } |
| |
| ret = destroy_src_plugin(stdev->odsp_hdl, SRC_AMP_REF); |
| if (ret != 0) { |
| ALOGE("Failed to destroy SRC-amp package"); |
| } |
| |
| if (is_mic_controlled_by_ahal(stdev) == false) { |
| ret = enable_amp_ref_route(stdev->route_hdl, false, STRM_16K); |
| if (ret != 0) { |
| ALOGE("Failed to disable amp-ref route"); |
| } |
| ret = enable_mic_route(stdev->route_hdl, false, |
| EXTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to disable mic route with INT OSC"); |
| } |
| ret = enable_mic_route(stdev->route_hdl, true, |
| INTERNAL_OSCILLATOR); |
| if (ret != 0) { |
| ALOGE("Failed to enable mic route with EXT OSC"); |
| } |
| } else { |
| // main mic is turned by media record, close it by 48khz |
| ret = enable_amp_ref_route(stdev->route_hdl, false, STRM_48K); |
| if (ret != 0) { |
| ALOGE("Failed to disable amp-ref route"); |
| } |
| } |
| } else { |
| ALOGW("%s: barge-in isn't enabled", __func__); |
| } |
| } |
| } else { |
| ALOGD("%s: rx stream is still active", __func__); |
| } |
| break; |
| case AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE: |
| ALOGD("%s: handle playback stream active", __func__); |
| |
| if (stdev->rx_active_count > 0) { |
| if (stdev->is_mic_route_enabled != false) { |
| // Atleast one keyword model is active so update the routes |
| // Check if the bargein route is enabled if not enable bargein route |
| // Check each model, if it is active then update it's route |
| stdev->transit_case = TRANSIT_SETUP_AEC; |
| pthread_cond_signal(&stdev->transition_cond); |
| } |
| } else { |
| ALOGW("%s: unexpeted stream active event", __func__); |
| } |
| break; |
| case AUDIO_EVENT_STOP_LAB: |
| /* Close Stream Driver */ |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active && |
| stdev->models[i].config && |
| (stdev->models[i].config->capture_handle == |
| config->u.ses_info.capture_handle)) { |
| index = i; |
| break; |
| } |
| } |
| |
| /* |
| * Close unused adnc if ... |
| * 1. No capture handle is found |
| * 2. Model is inactive |
| * 3. adnc stream handle is existed |
| */ |
| if (index == -1 && stdev->is_streaming > 0) { |
| ALOGD("%s: close unused adnc handle, cap_handle:%d", __func__, |
| config->u.ses_info.capture_handle); |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->adnc_strm_handle[i] != 0 && |
| !stdev->models[i].is_active) { |
| stdev->adnc_strm_close(stdev->adnc_strm_handle[i]); |
| stdev->adnc_strm_handle[i] = 0; |
| stdev->is_streaming--; |
| stdev->adnc_strm_last_read[i] = reset_time; |
| } |
| } |
| goto exit; |
| } |
| |
| ALOGD("%s: close streaming %d, cap_handle:%d, index:%d", |
| __func__, event, config->u.ses_info.capture_handle, index); |
| if (index != -1 && stdev->adnc_strm_handle[index] != 0) { |
| stdev->adnc_strm_close(stdev->adnc_strm_handle[index]); |
| stdev->adnc_strm_handle[index] = 0; |
| stdev->is_streaming--; |
| stdev->adnc_strm_last_read[index] = reset_time; |
| } |
| |
| break; |
| |
| case AUDIO_EVENT_SSR: |
| /*[TODO] Do we need to handle adsp SSR event ? */ |
| ALOGD("%s: handle audio subsystem restart %d", __func__, event); |
| break; |
| |
| case AUDIO_EVENT_READ_SAMPLES: |
| /* It is possible to change session info, check config */ |
| if (config->u.aud_info.ses_info == NULL) { |
| ALOGE("%s: Invalid config, event:%d", __func__, event); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| for (i = 0; i < MAX_MODELS; i++) { |
| if (stdev->models[i].is_active && |
| stdev->models[i].config && |
| (stdev->models[i].config->capture_handle == |
| config->u.aud_info.ses_info->capture_handle)) { |
| index = i; |
| break; |
| } |
| } |
| |
| /* There is not valid model to provide pcm samples. */ |
| if (i == MAX_MODELS) { |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| /* Open Stream Driver */ |
| if (index != -1 && stdev->adnc_strm_handle[index] == 0) { |
| if (stdev->adnc_strm_open == NULL) { |
| ALOGE("%s: Error adnc streaming not supported", __func__); |
| } else { |
| bool keyword_stripping_enabled = false; |
| int stream_end_point; |
| if (check_uuid_equality(stdev->models[index].uuid, |
| stdev->hotword_model_uuid)) { |
| stream_end_point = CVQ_ENDPOINT; |
| } else if (check_uuid_equality(stdev->models[index].uuid, |
| stdev->ambient_model_uuid)) { |
| stream_end_point = MUSIC_BUF_ENDPOINT; |
| } else if (check_uuid_equality(stdev->models[index].uuid, |
| stdev->entity_model_uuid)) { |
| stream_end_point = MUSIC_BUF_ENDPOINT; |
| } else { |
| stream_end_point = CVQ_ENDPOINT; |
| } |
| stdev->adnc_strm_handle[index] = stdev->adnc_strm_open( |
| keyword_stripping_enabled, 0, |
| stream_end_point); |
| if (stdev->adnc_strm_handle[index]) { |
| ALOGD("Opened adnc index %d handle %d endpoint 0x%x", |
| index, config->u.aud_info.ses_info->capture_handle, |
| stream_end_point); |
| stdev->is_streaming++; |
| |
| clock_gettime(CLOCK_REALTIME, &stdev->adnc_strm_last_read[index]); |
| if (stdev->is_streaming == 1) |
| pthread_cond_signal(&stdev->tunnel_create); |
| } else { |
| ALOGE("%s: DSP is currently not streaming", __func__); |
| } |
| } |
| } |
| |
| if (index != -1 && stdev->adnc_strm_handle[index] != 0) { |
| //ALOGD("%s: soundtrigger HAL adnc_strm_read", __func__); |
| clock_gettime(CLOCK_REALTIME, &stdev->adnc_strm_last_read[index]); |
| pthread_mutex_unlock(&stdev->lock); |
| stdev->adnc_strm_read(stdev->adnc_strm_handle[index], |
| config->u.aud_info.buf, |
| config->u.aud_info.num_bytes); |
| pthread_mutex_lock(&stdev->lock); |
| } else { |
| ALOGE("%s: soundtrigger is not streaming", __func__); |
| } |
| |
| break; |
| |
| case AUDIO_EVENT_NUM_ST_SESSIONS: |
| case AUDIO_EVENT_DEVICE_CONNECT: |
| case AUDIO_EVENT_DEVICE_DISCONNECT: |
| case AUDIO_EVENT_SVA_EXEC_MODE: |
| case AUDIO_EVENT_SVA_EXEC_MODE_STATUS: |
| ALOGV("%s: useless event %d", __func__, event); |
| break; |
| |
| default: |
| ALOGW("%s: Unknown event %d", __func__, event); |
| break; |
| } |
| |
| exit: |
| pthread_mutex_unlock(&stdev->lock); |
| return ret; |
| } |
| |
| static struct hw_module_methods_t hal_module_methods = { |
| .open = stdev_open, |
| }; |
| |
| struct sound_trigger_module HAL_MODULE_INFO_SYM = { |
| .common = { |
| .tag = HARDWARE_MODULE_TAG, |
| .module_api_version = SOUND_TRIGGER_MODULE_API_VERSION_1_0, |
| .hal_api_version = HARDWARE_HAL_API_VERSION, |
| .id = SOUND_TRIGGER_HARDWARE_MODULE_ID, |
| .name = "Knowles Sound Trigger HAL", |
| .author = "Knowles Electronics", |
| .methods = &hal_module_methods, |
| }, |
| }; |