| /* st_second_stage.c |
| * |
| * This file contrains the thread for a single second stage sound |
| * trigger user session. This thread implements logic for passing |
| * buffered data to various modules on ARM via capi wrappers. It also |
| * retrieves the detection results via capi wrappers and notifies the |
| * sound trigger state machine. |
| * |
| * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #define LOG_TAG "sound_trigger_hw:ss" |
| #define ATRACE_TAG (ATRACE_TAG_HAL) |
| /* #define LOG_NDEBUG 0 */ |
| #define LOG_NDDEBUG 0 |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <dlfcn.h> |
| #include <fcntl.h> |
| #include <sys/resource.h> |
| #include <sys/prctl.h> |
| #include <sys/ioctl.h> |
| #include <cutils/log.h> |
| #include <cutils/atomic.h> |
| #include <cutils/trace.h> |
| #include <system/thread_defs.h> |
| #include <hardware/sound_trigger.h> |
| |
| #include "st_second_stage.h" |
| #include "st_common_defs.h" |
| #include "sound_trigger_platform.h" |
| #include "sound_trigger_hw.h" |
| #include "st_session.h" |
| #include "st_hw_common.h" |
| #include "capi_v2_extn.h" |
| #include "st_hw_session_gcs.h" |
| |
| ST_DBG_DECLARE(static int ss_fd_cnt_kw_det = 0); |
| ST_DBG_DECLARE(static int ss_fd_cnt_user_ver = 0); |
| |
| static int process_frame_keyword_detection(st_arm_ss_session_t *ss_session, |
| uint8_t *frame, capi_v2_stream_data_t *stream_input, |
| sva_result_t *result_cfg_ptr, unsigned int *det_status) |
| { |
| capi_v2_buf_t capi_result_ptr; |
| capi_v2_err_t rc; |
| int ret = 0; |
| |
| stream_input->bufs_num = 1; /* 1 channel */ |
| stream_input->buf_ptr->max_data_len = ss_session->buff_sz; |
| stream_input->buf_ptr->actual_data_len = ss_session->buff_sz; |
| stream_input->buf_ptr->data_ptr = (int8_t *)frame; |
| |
| ALOGV("%s: Issuing capi_process", __func__); |
| ATRACE_BEGIN("sthal:second_stage: process keyword detection (CNN/RNN)"); |
| rc = ss_session->capi_handle->vtbl_ptr->process(ss_session->capi_handle, |
| &stream_input, NULL); |
| ATRACE_END(); |
| if (CAPI_V2_EFAILED == rc) { |
| ALOGD("%s: capi process function failed, result = %d", __func__, rc); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| capi_result_ptr.data_ptr = (int8_t *)result_cfg_ptr; |
| capi_result_ptr.actual_data_len = sizeof(sva_result_t); |
| capi_result_ptr.max_data_len = sizeof(sva_result_t); |
| |
| ALOGV("%s: Issuing capi_get_param for param %d", __func__, SVA_ID_RESULT); |
| rc = ss_session->capi_handle->vtbl_ptr->get_param(ss_session->capi_handle, |
| SVA_ID_RESULT, NULL, &capi_result_ptr); |
| if (CAPI_V2_EOK != rc) { |
| ALOGE("%s: get_param failed, result = %d", __func__, rc); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (result_cfg_ptr->is_detected) { |
| *det_status = KEYWORD_DETECTION_SUCCESS; |
| ss_session->exit_buffering = true; |
| ss_session->kw_start_idx = (result_cfg_ptr->start_position * |
| CNN_FRAME_SIZE) + ss_session->buf_start; |
| ss_session->kw_end_idx = (result_cfg_ptr->end_position * |
| CNN_FRAME_SIZE) + ss_session->buf_start; |
| } |
| ss_session->confidence_score = result_cfg_ptr->best_confidence; |
| |
| exit: |
| return ret; |
| } |
| |
| static int start_keyword_detection(st_arm_second_stage_t *st_sec_stage) |
| { |
| struct timespec tspec; |
| int ret = 0; |
| uint8_t *process_input_buff = NULL; |
| capi_v2_err_t rc; |
| st_arm_ss_session_t *ss_session = st_sec_stage->ss_session; |
| capi_v2_stream_data_t *stream_input = NULL; |
| sva_result_t *result_cfg_ptr = NULL; |
| unsigned int det_status = KEYWORD_DETECTION_PENDING; |
| uint64_t start_time = 0, end_time = 0; |
| uint32_t bytes_processed_ms = 0; |
| bool first_frame_processed = false; |
| |
| ALOGV("%s: Enter", __func__); |
| |
| start_time = get_current_time_ns(); |
| |
| process_input_buff = calloc(1, ss_session->buff_sz); |
| if (!process_input_buff) { |
| ALOGE("%s: failed to allocate process_input_buff", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| stream_input = calloc(1, sizeof(capi_v2_stream_data_t)); |
| if (!stream_input) { |
| ALOGE("%s: failed to allocate stream_input", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| stream_input->buf_ptr = calloc(1, sizeof(capi_v2_buf_t)); |
| if (!stream_input->buf_ptr) { |
| ALOGE("%s: failed to allocate stream_input->buf_ptr", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| result_cfg_ptr = calloc(1, sizeof(sva_result_t)); |
| if (!result_cfg_ptr) { |
| ALOGE("%s: failed to allocate result_cfg_ptr", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| while (!ss_session->exit_buffering && |
| (ss_session->bytes_processed < |
| (ss_session->buf_end - ss_session->buf_start))) { |
| if (ss_session->unread_bytes < ss_session->buff_sz) { |
| ALOGV("%s: waiting on cond, unread_bytes = %d", __func__, |
| ss_session->unread_bytes); |
| /* Time out to unblock read thread in case if write thread is |
| stuck filling the buffers */ |
| clock_gettime(CLOCK_MONOTONIC, &tspec); |
| tspec.tv_sec += ST_READ_WAIT_TIME_OUT_SEC; |
| ret = pthread_cond_timedwait(&ss_session->cond, &ss_session->lock, |
| &tspec); |
| ALOGV("%s: done waiting on cond", __func__); |
| if (ret) { |
| ALOGE("%s: ERROR. read wait timed out, ret %d", __func__, ret); |
| break; |
| } |
| if (ss_session->exit_buffering || |
| (ss_session->bytes_processed >= |
| (ss_session->buf_end - ss_session->buf_start))) { |
| ALOGW("%s: buffering stopped while waiting on cond, exiting", |
| __func__); |
| break; |
| } |
| if (ss_session->unread_bytes < ss_session->buff_sz) |
| continue; |
| } |
| |
| ret = st_buffer_read(ss_session->st_ses->hw_ses_current->buffer, |
| process_input_buff, ss_session->buff_sz, &ss_session->hw_rd_ptr, |
| false); |
| if (ret) { |
| ALOGE("%s: st_buffer_read failed, status %d", __func__, ret); |
| goto exit; |
| } |
| ss_session->unread_bytes -= ss_session->buff_sz; |
| |
| if (st_sec_stage->stdev->enable_debug_dumps) { |
| ST_DBG_FILE_WRITE(st_sec_stage->dump_fp, process_input_buff, |
| ss_session->buff_sz); |
| } |
| pthread_mutex_unlock(&ss_session->lock); |
| ret = process_frame_keyword_detection(ss_session, process_input_buff, |
| stream_input, result_cfg_ptr, &det_status); |
| pthread_mutex_lock(&ss_session->lock); |
| if (ret) { |
| ALOGE("%s: Processing through capi wrapper failed", __func__); |
| break; |
| } |
| ss_session->bytes_processed += ss_session->buff_sz; |
| /* |
| * The CNN algorithm requires the first frame to contain the buf_start |
| * up to the kwd_end_idx, followed by any variable size for subsequent |
| * frames. Reset the subsequent frame sizes to the driver requested |
| * buffer size (ex. 120ms) |
| */ |
| if (!first_frame_processed) { |
| ss_session->buff_sz = ss_session->lab_buf_sz; |
| first_frame_processed = true; |
| } |
| } |
| |
| exit: |
| /* |
| * The CNN/RNN algorithm doesn't set reject because it is continuously |
| * called until the keyword has passed. So if a detection success has not |
| * been declared inside the above loop, it is set to detection reject. |
| */ |
| pthread_mutex_unlock(&ss_session->lock); |
| pthread_mutex_lock(&ss_session->st_ses->ss_detections_lock); |
| end_time = get_current_time_ns(); |
| bytes_processed_ms = convert_bytes_to_ms(ss_session->bytes_processed, |
| &ss_session->st_ses->hw_ses_current->config); |
| ALOGD("%s: Processed %dms of data in %llums", __func__, |
| bytes_processed_ms, (end_time - start_time) / NSECS_PER_MSEC); |
| if (det_status == KEYWORD_DETECTION_SUCCESS) { |
| uint32_t kw_start_ms = 0, kw_end_ms = 0; |
| |
| ss_session->det_status = KEYWORD_DETECTION_SUCCESS; |
| kw_start_ms = convert_bytes_to_ms(ss_session->kw_start_idx, |
| &ss_session->st_ses->hw_ses_current->config); |
| kw_end_ms = convert_bytes_to_ms(ss_session->kw_end_idx, |
| &ss_session->st_ses->hw_ses_current->config); |
| ALOGD("%s: Detection success, confidence level = %d", __func__, |
| ss_session->confidence_score); |
| ALOGD("%s: 2nd stage kw_start = %dms, kw_end = %dms", __func__, |
| kw_start_ms, kw_end_ms); |
| } else { |
| ss_session->det_status = KEYWORD_DETECTION_REJECT; |
| ALOGD("%s: Detection reject, confidence level = %d", __func__, |
| ss_session->confidence_score); |
| } |
| /* Signal aggregator thread with detection result */ |
| pthread_cond_signal(&ss_session->st_ses->ss_detections_cond); |
| pthread_mutex_unlock(&ss_session->st_ses->ss_detections_lock); |
| pthread_mutex_lock(&ss_session->lock); |
| |
| /* Reinit the module */ |
| ALOGV("%s: Issuing capi_set_param for param %d", __func__, |
| SVA_ID_REINIT_ALL); |
| rc = ss_session->capi_handle->vtbl_ptr->set_param(ss_session->capi_handle, |
| SVA_ID_REINIT_ALL, NULL, NULL); |
| if (CAPI_V2_EOK != rc) { |
| ret = -EINVAL; |
| ALOGE("%s: Failed to reinit capi module, result = %d", __func__, rc); |
| } |
| |
| if (result_cfg_ptr) |
| free(result_cfg_ptr); |
| if (stream_input) { |
| if (stream_input->buf_ptr) |
| free(stream_input->buf_ptr); |
| free(stream_input); |
| } |
| if (process_input_buff) |
| free(process_input_buff); |
| |
| ALOGV("%s: Exiting", __func__); |
| return ret; |
| } |
| |
| static int process_frame_user_verification(st_arm_ss_session_t *ss_session, |
| uint8_t *frame, capi_v2_stream_data_t *stream_input, |
| voiceprint2_result_t *result_cfg_ptr, unsigned int *det_status) |
| { |
| capi_v2_buf_t capi_result_ptr; |
| capi_v2_err_t rc; |
| int ret = 0; |
| |
| stream_input->bufs_num = 1; /* 1 channel */ |
| stream_input->buf_ptr->max_data_len = ss_session->buff_sz; |
| stream_input->buf_ptr->actual_data_len = ss_session->buff_sz; |
| stream_input->buf_ptr->data_ptr = (int8_t *)frame; |
| |
| ALOGV("%s: Issuing capi_process", __func__); |
| ATRACE_BEGIN("sthal:second_stage: process user verification (VOP)"); |
| rc = ss_session->capi_handle->vtbl_ptr->process(ss_session->capi_handle, |
| &stream_input, NULL); |
| ATRACE_END(); |
| if (CAPI_V2_EFAILED == rc) { |
| ALOGD("%s: capi process function failed, result = %d", __func__, rc); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| capi_result_ptr.data_ptr = (int8_t *)result_cfg_ptr; |
| capi_result_ptr.actual_data_len = sizeof(voiceprint2_result_t); |
| capi_result_ptr.max_data_len = sizeof(voiceprint2_result_t); |
| |
| ALOGV("%s: Issuing capi_get_param for param %d", __func__, |
| VOICEPRINT2_ID_RESULT); |
| ATRACE_BEGIN("sthal:second_stage: get_result for user verification (VOP)"); |
| rc = ss_session->capi_handle->vtbl_ptr->get_param(ss_session->capi_handle, |
| VOICEPRINT2_ID_RESULT, NULL, &capi_result_ptr); |
| ATRACE_END(); |
| if (CAPI_V2_EOK != rc) { |
| ALOGE("%s: get_param failed, result = %d", __func__, rc); |
| ret = -EINVAL; |
| goto exit; |
| } |
| |
| if (result_cfg_ptr->is_detected) |
| *det_status = USER_VERIFICATION_SUCCESS; |
| ss_session->confidence_score = |
| (int32_t)result_cfg_ptr->combined_user_score; |
| |
| exit: |
| return ret; |
| } |
| |
| static int start_user_verification(st_arm_second_stage_t *st_sec_stage) |
| { |
| struct timespec tspec; |
| int ret = 0; |
| uint8_t *process_input_buff = NULL; |
| capi_v2_err_t rc; |
| st_arm_ss_session_t *ss_session = st_sec_stage->ss_session; |
| capi_v2_stream_data_t *stream_input = NULL; |
| capi_v2_buf_t capi_uv_ptr; |
| voiceprint2_result_t *result_cfg_ptr = NULL; |
| voiceprint2_sva_uv_score_t *uv_cfg_ptr = NULL; |
| unsigned int det_status = USER_VERIFICATION_PENDING; |
| uint64_t start_time = 0, end_time = 0; |
| uint32_t bytes_processed_ms = 0; |
| |
| ALOGV("%s: Enter", __func__); |
| |
| process_input_buff = calloc(1, ss_session->buff_sz); |
| if (!process_input_buff) { |
| ALOGE("%s: failed to allocate process_input_buff", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| stream_input = calloc(1, sizeof(capi_v2_stream_data_t)); |
| if (!stream_input) { |
| ALOGE("%s: failed to allocate stream_input", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| stream_input->buf_ptr = calloc(1, sizeof(capi_v2_buf_t)); |
| if (!stream_input->buf_ptr) { |
| ALOGE("%s: failed to allocate stream_input->buf_ptr", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| result_cfg_ptr = calloc(1, sizeof(voiceprint2_result_t)); |
| if (!result_cfg_ptr) { |
| ALOGE("%s: failed to allocate result_cfg_ptr", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| uv_cfg_ptr = calloc(1, sizeof(voiceprint2_sva_uv_score_t)); |
| if (!uv_cfg_ptr) { |
| ALOGE("%s: failed to allocate uv_cfg_ptr", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| if (ss_session->st_ses->hw_ses_current->f_stage_version == ST_MODULE_TYPE_GMM) { |
| uv_cfg_ptr->sva_uv_confidence_score = |
| ss_session->st_ses->hw_ses_current->user_level; |
| capi_uv_ptr.data_ptr = (int8_t *)uv_cfg_ptr; |
| capi_uv_ptr.actual_data_len = sizeof(voiceprint2_sva_uv_score_t); |
| capi_uv_ptr.max_data_len = sizeof(voiceprint2_sva_uv_score_t); |
| |
| ALOGV("%s: Issuing capi_set_param for param %d, uv_conf_score %f", __func__, |
| VOICEPRINT2_ID_SVA_UV_SCORE, uv_cfg_ptr->sva_uv_confidence_score); |
| rc = ss_session->capi_handle->vtbl_ptr->set_param(ss_session->capi_handle, |
| VOICEPRINT2_ID_SVA_UV_SCORE, NULL, &capi_uv_ptr); |
| if (CAPI_V2_EOK != rc) { |
| ALOGE("%s: set_param VOICEPRINT2_ID_SVA_UV_SCORE failed, result = %d", |
| __func__, rc); |
| ret = -EINVAL; |
| goto exit; |
| } |
| } |
| |
| while (!ss_session->exit_buffering) { |
| if (ss_session->unread_bytes < ss_session->buff_sz) { |
| ALOGV("%s: waiting on cond, unread_bytes = %d", __func__, |
| ss_session->unread_bytes); |
| /* Time out to unblock read thread in case if write thread is |
| stuck filling the buffers */ |
| clock_gettime(CLOCK_MONOTONIC, &tspec); |
| tspec.tv_sec += ST_READ_WAIT_TIME_OUT_SEC; |
| ret = pthread_cond_timedwait(&ss_session->cond, &ss_session->lock, |
| &tspec); |
| ALOGV("%s: done waiting on cond", __func__); |
| |
| start_time = get_current_time_ns(); |
| |
| if (ret) { |
| ALOGE("%s: ERROR. read wait timed out, ret %d", __func__, ret); |
| break; |
| } |
| if (ss_session->exit_buffering) { |
| ALOGW("%s: buffering stopped while waiting on cond, exiting", |
| __func__); |
| break; |
| } |
| if (ss_session->unread_bytes < ss_session->buff_sz) |
| continue; |
| } |
| |
| ret = st_buffer_read(ss_session->st_ses->hw_ses_current->buffer, |
| process_input_buff, ss_session->buff_sz, &ss_session->hw_rd_ptr, |
| false); |
| if (ret) { |
| ALOGE("%s: st_buffer_read failed, status %d", __func__, ret); |
| goto exit; |
| } |
| ss_session->unread_bytes -= ss_session->buff_sz; |
| |
| if (st_sec_stage->stdev->enable_debug_dumps) { |
| ST_DBG_FILE_WRITE(st_sec_stage->dump_fp, process_input_buff, |
| ss_session->buff_sz); |
| } |
| pthread_mutex_unlock(&ss_session->lock); |
| ret = process_frame_user_verification(ss_session, process_input_buff, |
| stream_input, result_cfg_ptr, &det_status); |
| pthread_mutex_lock(&ss_session->lock); |
| if (ret) { |
| ALOGE("%s: Processing through capi wrapper failed", __func__); |
| break; |
| } |
| ss_session->bytes_processed += ss_session->buff_sz; |
| ss_session->exit_buffering = true; |
| } |
| |
| exit: |
| |
| pthread_mutex_unlock(&ss_session->lock); |
| pthread_mutex_lock(&ss_session->st_ses->ss_detections_lock); |
| end_time = get_current_time_ns(); |
| bytes_processed_ms = convert_bytes_to_ms(ss_session->bytes_processed, |
| &ss_session->st_ses->hw_ses_current->config); |
| ALOGD("%s: Processed %dms of data in %llums", __func__, |
| bytes_processed_ms, (end_time - start_time) / NSECS_PER_MSEC); |
| if (det_status == USER_VERIFICATION_SUCCESS) { |
| ss_session->det_status = USER_VERIFICATION_SUCCESS; |
| ALOGD("%s: Detection success, confidence level = %d", __func__, |
| ss_session->confidence_score); |
| } else { |
| ss_session->det_status = USER_VERIFICATION_REJECT; |
| ALOGD("%s: Detection reject, confidence level = %d", __func__, |
| ss_session->confidence_score); |
| } |
| /* Signal aggregator thread with detection result */ |
| pthread_cond_signal(&ss_session->st_ses->ss_detections_cond); |
| pthread_mutex_unlock(&ss_session->st_ses->ss_detections_lock); |
| pthread_mutex_lock(&ss_session->lock); |
| |
| /* Reinit the module */ |
| ALOGV("%s: Issuing capi_set_param for param %d", __func__, |
| VOICEPRINT2_ID_REINIT); |
| rc = ss_session->capi_handle->vtbl_ptr->set_param(ss_session->capi_handle, |
| VOICEPRINT2_ID_REINIT, NULL, NULL); |
| if (CAPI_V2_EOK != rc) { |
| ALOGE("%s: Failed to reinit capi module, result = %d", __func__, rc); |
| ret = -EINVAL; |
| } |
| |
| if (result_cfg_ptr) |
| free(result_cfg_ptr); |
| if (uv_cfg_ptr) |
| free(uv_cfg_ptr); |
| if (stream_input) { |
| if (stream_input->buf_ptr) |
| free(stream_input->buf_ptr); |
| free(stream_input); |
| } |
| if (process_input_buff) |
| free(process_input_buff); |
| |
| ALOGV("%s: Exiting", __func__); |
| return ret; |
| } |
| |
| static void *buffer_thread_loop(void *st_second_stage) |
| { |
| st_arm_second_stage_t *st_sec_stage; |
| st_arm_ss_session_t *ss_session = NULL; |
| |
| if (st_second_stage == NULL) { |
| ALOGE("%s: input is NULL, exiting", __func__); |
| return NULL; |
| } |
| |
| st_sec_stage = (st_arm_second_stage_t *)st_second_stage; |
| if (st_sec_stage->ss_session) { |
| ss_session = st_sec_stage->ss_session; |
| } else { |
| ALOGE("%s: st_arm_ss_session_t is NULL, exiting", __func__); |
| return NULL; |
| } |
| |
| pthread_mutex_lock(&ss_session->lock); |
| while (!ss_session->exit_thread) { |
| ss_session->exit_buffering = true; |
| ALOGV("%s: waiting on cond", __func__); |
| /* Wait for keyword buffer data from DSP */ |
| pthread_cond_wait(&ss_session->cond, &ss_session->lock); |
| ALOGV("%s: done waiting on cond, exit_buffering = %d", __func__, |
| ss_session->exit_buffering); |
| if (ss_session->exit_thread) { |
| pthread_mutex_unlock(&ss_session->lock); |
| return NULL; |
| } |
| |
| /* |
| * If 1st stage buffering overflows before 2nd stage starts processing, |
| * the below functions need to be called to reset the 1st stage session |
| * for the next detection. |
| */ |
| if (ss_session->exit_buffering && |
| (((st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_KEYWORD_DETECTION) && |
| (ss_session->det_status != KEYWORD_DETECTION_PENDING)) || |
| ((st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_USER_VERIFICATION) && |
| (ss_session->det_status != USER_VERIFICATION_PENDING)))) { |
| continue; |
| } |
| |
| if (st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_KEYWORD_DETECTION) { |
| if (st_sec_stage->stdev->enable_debug_dumps) { |
| ST_DBG_FILE_OPEN_WR(st_sec_stage->dump_fp, |
| ST_DEBUG_DUMP_LOCATION, "ss_buf_kw_det", "bin", |
| ss_fd_cnt_kw_det); |
| ALOGD("%s: 2nd stage KWD buffer stored in: ss_buf_kw_det_%d.bin", |
| __func__, ss_fd_cnt_kw_det); |
| ss_fd_cnt_kw_det++; |
| } |
| start_keyword_detection(st_sec_stage); |
| if (st_sec_stage->stdev->enable_debug_dumps) |
| ST_DBG_FILE_CLOSE(st_sec_stage->dump_fp); |
| } else { |
| if (st_sec_stage->stdev->enable_debug_dumps) { |
| ST_DBG_FILE_OPEN_WR(st_sec_stage->dump_fp, |
| ST_DEBUG_DUMP_LOCATION, "ss_buf_user_ver", "bin", |
| ss_fd_cnt_user_ver); |
| ALOGD("%s: 2nd stage UV buffer stored in: ss_buf_user_ver_%d.bin", |
| __func__, ss_fd_cnt_user_ver); |
| ss_fd_cnt_user_ver++; |
| } |
| start_user_verification(st_sec_stage); |
| if (st_sec_stage->stdev->enable_debug_dumps) |
| ST_DBG_FILE_CLOSE(st_sec_stage->dump_fp); |
| } |
| } |
| pthread_mutex_unlock(&ss_session->lock); |
| return NULL; |
| } |
| |
| int st_second_stage_start_session(st_arm_second_stage_t *st_sec_stage) |
| { |
| int ret = 0; |
| capi_v2_err_t rc = CAPI_V2_EOK; |
| int8_t *cfg_ptr_threshold = NULL; |
| capi_v2_buf_t capi_buf; |
| st_arm_ss_session_t *ss_session; |
| |
| |
| if (!st_sec_stage || !st_sec_stage->ss_info || !st_sec_stage->ss_session) { |
| ALOGE("%s: Receivd null params, exiting", __func__); |
| return -EINVAL; |
| } |
| |
| ss_session = st_sec_stage->ss_session; |
| |
| if (st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_KEYWORD_DETECTION) { |
| sva_threshold_config_t *threshold_cfg; |
| |
| cfg_ptr_threshold = calloc(1, sizeof(sva_threshold_config_t)); |
| if (!cfg_ptr_threshold) { |
| ALOGE("%s: failed to allocate cfg_ptr_threshold", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| capi_buf.data_ptr = cfg_ptr_threshold; |
| capi_buf.actual_data_len = sizeof(sva_threshold_config_t); |
| capi_buf.max_data_len = sizeof(sva_threshold_config_t); |
| threshold_cfg = (sva_threshold_config_t *)capi_buf.data_ptr; |
| threshold_cfg->smm_threshold = ss_session->confidence_threshold; |
| ALOGD("%s: Keyword detection %s confidence level = %d", __func__, |
| st_sec_stage->ss_info->sm_id == ST_SM_ID_SVA_CNN ? "(CNN)" : "(RNN)", |
| ss_session->confidence_threshold); |
| |
| ALOGV("%s: Issuing capi_set_param for param %d", __func__, |
| SVA_ID_THRESHOLD_CONFIG); |
| rc = ss_session->capi_handle->vtbl_ptr->set_param( |
| ss_session->capi_handle, SVA_ID_THRESHOLD_CONFIG, NULL, &capi_buf); |
| if (CAPI_V2_EOK != rc) { |
| ret = -EINVAL; |
| ALOGE("%s: set_param SVA_ID_THRESHOLD_CONFIG failed, result = %d", |
| __func__, rc); |
| goto exit; |
| } |
| |
| ALOGV("%s: Issuing capi_set_param for param %d", __func__, |
| SVA_ID_REINIT_ALL); |
| rc = ss_session->capi_handle->vtbl_ptr->set_param( |
| ss_session->capi_handle, SVA_ID_REINIT_ALL, NULL, NULL); |
| if (CAPI_V2_EOK != rc) { |
| ret = -EINVAL; |
| ALOGE("%s: set_param SVA_ID_REINIT_ALL failed, result = %d", |
| __func__, rc); |
| goto exit; |
| } |
| } else if (st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_USER_VERIFICATION) { |
| voiceprint2_threshold_config_t *threshold_cfg; |
| |
| ALOGV("%s: Issuing capi_set_param for param %d", __func__, |
| VOICEPRINT2_ID_REINIT); |
| rc = ss_session->capi_handle->vtbl_ptr->set_param( |
| ss_session->capi_handle, VOICEPRINT2_ID_REINIT, NULL, NULL); |
| if (CAPI_V2_EOK != rc) { |
| ret = -EINVAL; |
| ALOGE("%s: set_param VOICEPRINT2_ID_REINIT failed, result = %d", |
| __func__, rc); |
| goto exit; |
| } |
| |
| cfg_ptr_threshold = calloc(1, sizeof(voiceprint2_threshold_config_t)); |
| if (!cfg_ptr_threshold) { |
| ALOGE("%s: failed to allocate cfg_ptr_threshold", __func__); |
| ret = -ENOMEM; |
| goto exit; |
| } |
| |
| capi_buf.data_ptr = cfg_ptr_threshold; |
| capi_buf.actual_data_len = sizeof(voiceprint2_threshold_config_t); |
| capi_buf.max_data_len = sizeof(voiceprint2_threshold_config_t); |
| threshold_cfg = (voiceprint2_threshold_config_t *)capi_buf.data_ptr; |
| threshold_cfg->user_verification_threshold = |
| ss_session->confidence_threshold; |
| ALOGD("%s: User verification (VOP) confidence level = %d", __func__, |
| ss_session->confidence_threshold); |
| |
| ALOGV("%s: Issuing capi_set_param for param %d", __func__, |
| VOICEPRINT2_ID_THRESHOLD_CONFIG); |
| rc = ss_session->capi_handle->vtbl_ptr->set_param( |
| ss_session->capi_handle, VOICEPRINT2_ID_THRESHOLD_CONFIG, NULL, |
| &capi_buf); |
| if (CAPI_V2_EOK != rc) { |
| ret = -EINVAL; |
| ALOGE("%s: set_param VOICEPRINT2_ID_THRESHOLD_CONFIG failed" |
| ", result = %d", __func__, rc); |
| goto exit; |
| } |
| } |
| |
| exit: |
| if (cfg_ptr_threshold) |
| free(cfg_ptr_threshold); |
| return ret; |
| } |
| |
| int st_second_stage_module_init(st_arm_second_stage_t *st_sec_stage, |
| void *lib_name) |
| { |
| int ret = 0; |
| const char *lib = (const char *)lib_name; |
| capi_v2_proplist_t init_set_proplist; |
| capi_v2_prop_t sm_prop_ptr; |
| capi_v2_err_t rc; |
| st_arm_ss_session_t *ss_session = NULL; |
| |
| if (!st_sec_stage || !st_sec_stage->ss_session) { |
| ALOGE("%s: Received NULL params, exiting", __func__); |
| return -EINVAL; |
| } |
| ss_session = st_sec_stage->ss_session; |
| st_sec_stage->dump_fp = NULL; |
| |
| /* Allocate extra pointers needed in the capi wrappers */ |
| if (st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_KEYWORD_DETECTION) |
| ss_session->capi_handle = calloc(1, |
| sizeof(capi_v2_t) + sizeof(char *)); |
| else if (st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_USER_VERIFICATION) |
| ss_session->capi_handle = calloc(1, |
| sizeof(capi_v2_t) + (3 * sizeof(char *))); |
| /* else TODO: ST_SM_TYPE_CUSTOM_DETECTION */ |
| |
| if (!ss_session->capi_handle) { |
| ALOGE("%s: failed to allocate ss_session->capi_handle", __func__); |
| ret = -ENOMEM; |
| goto err_exit; |
| } |
| |
| ss_session->capi_lib_handle = dlopen(lib, RTLD_NOW); |
| if (!ss_session->capi_lib_handle) { |
| ALOGE("%s: ERROR. %s", __func__, dlerror()); |
| ret = -ENODEV; |
| goto err_exit; |
| } |
| dlerror(); |
| |
| DLSYM(ss_session->capi_lib_handle, ss_session->capi_init, |
| capi_v2_init, ret); |
| if (ret) |
| goto err_exit; |
| |
| sm_prop_ptr.id = CAPI_V2_CUSTOM_INIT_DATA; |
| sm_prop_ptr.payload.data_ptr = (int8_t *)ss_session->sound_model; |
| sm_prop_ptr.payload.actual_data_len = (uint32_t)ss_session->sm_size; |
| sm_prop_ptr.payload.max_data_len = (uint32_t)ss_session->sm_size; |
| init_set_proplist.props_num = 1; |
| init_set_proplist.prop_ptr = &sm_prop_ptr; |
| |
| ALOGV("%s: Issuing capi_init", __func__); |
| rc = ss_session->capi_init(ss_session->capi_handle, &init_set_proplist); |
| if (rc != CAPI_V2_EOK) { |
| ALOGE("%s: capi_init result is %d, exiting", __func__, rc); |
| ret = -EINVAL; |
| goto err_exit; |
| } |
| if (NULL == ss_session->capi_handle) { |
| ALOGE("%s: capi_handle is NULL, exiting", __func__); |
| ret = -EINVAL; |
| goto err_exit; |
| } |
| if (NULL == ss_session->capi_handle->vtbl_ptr) { |
| ALOGE("%s: capi_handle->vtbl_ptr is NULL, exiting", __func__); |
| ret = -EINVAL; |
| goto err_exit; |
| } |
| |
| return 0; |
| |
| err_exit: |
| if (ss_session->capi_handle) { |
| free(ss_session->capi_handle); |
| ss_session->capi_handle = NULL; |
| } |
| if (ss_session->capi_lib_handle) { |
| dlclose(ss_session->capi_lib_handle); |
| ss_session->capi_lib_handle = NULL; |
| } |
| return ret; |
| } |
| |
| int st_second_stage_module_deinit(st_arm_second_stage_t *st_sec_stage) |
| { |
| if (st_sec_stage) { |
| if (st_sec_stage->ss_session) { |
| if (st_sec_stage->ss_session->capi_lib_handle) { |
| dlclose(st_sec_stage->ss_session->capi_lib_handle); |
| st_sec_stage->ss_session->capi_lib_handle = NULL; |
| } |
| if (st_sec_stage->ss_session->capi_handle) { |
| st_sec_stage->ss_session->capi_handle->vtbl_ptr = NULL; |
| free(st_sec_stage->ss_session->capi_handle); |
| st_sec_stage->ss_session->capi_handle = NULL; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| int st_second_stage_prepare_session(st_arm_second_stage_t *st_sec_stage) |
| { |
| int status = 0; |
| pthread_condattr_t attr; |
| |
| pthread_mutex_init(&st_sec_stage->ss_session->lock, NULL); |
| pthread_condattr_init(&attr); |
| pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); |
| pthread_cond_init(&st_sec_stage->ss_session->cond, &attr); |
| pthread_condattr_destroy(&attr); |
| st_sec_stage->ss_session->exit_thread = false; |
| |
| status = pthread_create(&st_sec_stage->ss_session->thread, NULL, |
| buffer_thread_loop, st_sec_stage); |
| if (status) |
| ALOGE("%s: Error creating second stage buffer thread. status = %d", |
| __func__, status); |
| |
| return status; |
| } |
| |
| int st_second_stage_stop_session(st_arm_second_stage_t *st_sec_stage) |
| { |
| int status = 0; |
| capi_v2_err_t rc; |
| |
| if (((st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_USER_VERIFICATION) || |
| (st_sec_stage->ss_info->sm_detection_type == |
| ST_SM_TYPE_KEYWORD_DETECTION)) && |
| !st_sec_stage->stdev->ssr_offline_received) { |
| ALOGV("%s: Issuing capi_end", __func__); |
| rc = st_sec_stage->ss_session->capi_handle->vtbl_ptr->end( |
| st_sec_stage->ss_session->capi_handle); |
| if (rc != CAPI_V2_EOK) |
| ALOGE("%s: Capi end function failed, result = %d", |
| __func__, rc); |
| } |
| |
| st_sec_stage->ss_session->exit_thread = true; |
| st_sec_stage->ss_session->exit_buffering = true; |
| pthread_mutex_lock(&st_sec_stage->ss_session->lock); |
| pthread_cond_signal(&st_sec_stage->ss_session->cond); |
| pthread_mutex_unlock(&st_sec_stage->ss_session->lock); |
| status = pthread_join(st_sec_stage->ss_session->thread, NULL); |
| if (status) |
| ALOGE("%s: Error joining second stage thread, status = %d", |
| __func__, status); |
| pthread_cond_destroy(&st_sec_stage->ss_session->cond); |
| pthread_mutex_destroy(&st_sec_stage->ss_session->lock); |
| |
| return 0; |
| } |