| /* |
| * 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); |
|