blob: 3b4e6e091b2f1e5cd2ebfbd76a81d1c1e10e64e8 [file] [log] [blame]
/* st_hw_session_pcm.c
*
* This file implements the hw session functionality of SVA using capture path
*
* Copyright (c) 2017-2020, 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"
/* #define LOG_NDEBUG 0 */
#define LOG_NDDEBUG 0
#include <errno.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#include <system/thread_defs.h>
#include <stdarg.h>
#include <unistd.h>
#include "st_common_defs.h"
#include "sound_trigger_platform.h"
#include "st_hw_session_pcm.h"
#include "sound_trigger_hw.h"
#include "ffv_interface.h"
#include "esp_api.h"
/* uncomment if process buffers need to be queued */
//#define QUEUE_PROCESS_BUFFERS
typedef struct {
struct listnode node;
FfvEventType event_type;
void *ev_payload;
} callback_event_t;
typedef union {
ffv_history_buffer_length_param_t buffer_length_params;
ffv_src_tracking_param_t src_tracking_params;
ffv_target_channel_index_param_t ch_index_params;
esp_energy_levels_t esp_energy_levels_params;
} st_get_param_payload_t;
static int reg_sm(st_hw_session_t* p_ses, void *sm_data,
unsigned int sm_size, uint32_t model_id);
static int reg_sm_params(st_hw_session_t* p_ses, unsigned int recognition_mode,
bool capture_requested, struct sound_trigger_recognition_config *rc_config);
static int dereg_sm(st_hw_session_t* p_ses,uint32_t model_id __unused);
static int dereg_sm_params(st_hw_session_t* p_ses);
static int start(st_hw_session_t* p_ses);
static int stop(st_hw_session_t* p_ses);
static int stop_buffering(st_hw_session_t* p_ses);
static int set_device(st_hw_session_t *p_ses,
bool enable);
static int disable_device(st_hw_session_t *p_ses, bool setting_device);
static int enable_device(st_hw_session_t *p_ses, bool setting_device);
static void process_lab_capture(st_hw_session_t *p_ses);
static int restart(st_hw_session_t* p_ses, unsigned int recognition_mode,
struct sound_trigger_recognition_config *rc_config __unused);
static int read_pcm(st_hw_session_t *p_ses,
unsigned char *buf,
unsigned int bytes);
static int allocate_buffers(st_hw_session_pcm_t* p_ses);
static int deallocate_buffers(st_hw_session_pcm_t* p_ses);
static int get_param_data(st_hw_session_t* p_ses, const char *param,
void *payload, size_t payload_size, size_t *param_data_size);
static void st_event_callback(void *cb_data, void *handle, FfvEventType event_type,
void *event_payload, size_t event_payload_size);
static FfvStatusType (*ffv_init_fn)(void** handle, int num_tx_in_ch,
int num_out_ch, int num_ec_ref_ch, int frame_len, int sample_rate,
const char *config_file_name, char *svaModelBuffer,
uint32_t svaModelSize, int* totMemSize, int product_id, const char* license);
static void (*ffv_deinit_fn)(void* handle);
static void (*ffv_process_fn)(void *handle, const int16_t *in_pcm,
int16_t *out_pcm, const int16_t *ec_ref_pcm);
static void (*ffv_process_v2_fn)(void *handle, const int16_t *in_pcm,
int16_t *out_pcm, int16_t *bsp_out_pcm, const int16_t *ec_ref_pcm);
static int (*ffv_read_fn)(void* handle, int16_t *buf_pcm,
int max_buf_len);
static FfvStatusType (*ffv_get_param_fn)(void *handle, char *params_buffer_ptr,
int param_id, int buffer_size, int *param_size_ptr);
static FfvStatusType (*ffv_set_param_fn)(void *handle, char *params_buffer_ptr,
int param_id, int param_size);
static FfvStatusType (*ffv_register_event_callback_fn)(void *handle,
ffv_event_callback_fn_t fun_ptr, void *cb_data);
static EspStatusType (*esp_init_fn)(void **handle, int frame_len_ms,
int sample_rate, int* totMemSize);
static void (*esp_deinit_fn)(void *handle);
static void (*esp_process_fn)(void *handle, const int16 *in_pcm);
static EspStatusType (*esp_get_param_fn)(void *handle, char *params_buffer_ptr,
int param_id, int buffer_size, int *param_size_ptr);
static int send_detection_request(st_hw_session_t *p_ses __unused);
static struct pcm_config stdev_arm_pcm_config = {
.channels = SOUND_TRIGGER_CHANNEL_MODE_MONO,
.rate = SOUND_TRIGGER_SAMPLING_RATE_16000,
.period_size = SOUND_TRIGGER_PCM_PERIOD_SIZE,
.period_count = SOUND_TRIGGER_PCM_PERIOD_COUNT,
.format = PCM_FORMAT_S16_LE,
};
struct st_session_fptrs pcm_fptrs = {
.reg_sm = reg_sm,
.reg_sm_params = reg_sm_params,
.dereg_sm = dereg_sm,
.dereg_sm_params = dereg_sm_params,
.start = start,
.restart = restart,
.stop = stop,
.stop_buffering = stop_buffering,
.set_device = set_device,
.read_pcm = read_pcm,
.process_lab_capture = process_lab_capture,
.disable_device = disable_device,
.enable_device = enable_device,
.get_param_data = get_param_data,
.send_detection_request = send_detection_request,
};
struct st_hw_pcm_data {
void *ffv_lib_handle;
void *esp_lib_handle;
};
static struct st_hw_pcm_data pcm_data = {.ffv_lib_handle = NULL, .esp_lib_handle = NULL};
#if LINUX_ENABLED
#define restart_session(p_ses) (0)
#else
/*
* While running FFV with barge-in usecase, there is a timestamp
* discontinuity observed in one of the sessions (capture/EC capture)
* randomly. So encoder is dropping data which has timestamp discontinuity
* in one of the sessions. This leads to EC convergence issue where one of
* the sessions has old data and other has new data which is leading to
* zero detections.
* Restart both the sessions once, to flush out the data after setting up
* FFV session to fix the issue.
*/
static int restart_session(st_hw_session_t* p_ses)
{
int status = 0;
status = stop(p_ses);
if (status) {
ALOGE("%s: failed to stop err %d", __func__, status);
goto exit;
}
status = start(p_ses);
if (status)
ALOGE("%s: failed to start err %d", __func__, status);
exit:
return status;
}
#endif
#ifndef SOUND_TRIGGER_CPU_AFFINITY_SET
#define st_cpu_affinity_set(p_pcm_ses) (0)
#else
/* affine soundtrigger thread to CPU core one */
static void st_cpu_affinity_set(st_hw_session_pcm_t *p_pcm_ses)
{
cpu_set_t cpuset;
struct sched_param sched_param;
int policy = SCHED_FIFO, rc = 0;
ALOGV("%s: Set CPU affinity for SVA thread, pcm_ses %p", __func__, p_pcm_ses);
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
#ifdef LINUX_ENABLED
if (pthread_setaffinity_np(p_pcm_ses->capture_thread, sizeof(cpuset),
&cpuset) != 0)
#else
if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
#endif
ALOGE("%s: CPU Affinity allocation failed for Capture thread",
__func__);
sched_param.sched_priority = sched_get_priority_min(policy);
rc = sched_setscheduler(0, policy, &sched_param);
if (rc != 0)
ALOGE("%s: Failed to set realtime priority", __func__);
}
#endif
static void get_lib_path(char * lib_path, int path_size)
{
#ifdef LINUX_ENABLED
#ifdef __LP64__
/* libs are stored in /usr/lib64 */
snprintf(lib_path, path_size, "%s", "/usr/lib64");
#else
/* libs are stored in /usr/lib */
snprintf(lib_path, path_size, "%s", "/usr/lib");
#endif
#else
#ifdef __LP64__
/* libs are stored in /vendor/lib64 */
snprintf(lib_path, path_size, "%s", "/vendor/lib64");
#else
/* libs are stored in /vendor/lib */
snprintf(lib_path, path_size, "%s", "/vendor/lib");
#endif
#endif
}
static size_t get_ffv_read_buffer_len(st_hw_session_pcm_t *p_ses)
{
st_get_param_payload_t *params_buffer_ptr;
int param_id, buffer_size, param_size;
size_t buf_size = 0;
FfvStatusType status_type;
buffer_size = sizeof(st_get_param_payload_t);
params_buffer_ptr = (st_get_param_payload_t *)calloc(1, buffer_size);
if (!params_buffer_ptr) {
ALOGE("%s: ERROR. Can not allocate buffer size %d", __func__, buffer_size);
goto exit;
}
param_id = FFV_HISTORY_BUFFER_LENGTH_PARAM;
status_type = ffv_get_param_fn(p_ses->handle, (char *)params_buffer_ptr,
param_id, buffer_size, &param_size);
if (status_type) {
ALOGE("%s: ERROR. ffv_get_param_fn ret %d", __func__, status_type);
goto exit;
}
if (param_size != sizeof(ffv_history_buffer_length_param_t)) {
ALOGE("%s: ERROR. Invalid param size returned %d", __func__, param_size);
goto exit;
}
buf_size = params_buffer_ptr->buffer_length_params.bufSize;
exit:
ALOGD("%s: read buffer len %zu", __func__, buf_size);
return buf_size;
}
static int check_and_set_ec_ref_device(st_hw_session_t *p_ses, bool enable)
{
char st_device_name[DEVICE_NAME_MAX_SIZE];
struct st_vendor_info* v_info = p_ses->vendor_uuid_info;
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
FfvStatusType status_type;
ffv_quadrx_use_dwnmix_param_t quad_downmix;
if (v_info->split_ec_ref_data) {
ALOGV("%s: Ignore ec ref set device %p", __func__, p_ses);
return 0;
}
if (enable) {
if (v_info->ec_ref_channel_cnt == SOUND_TRIGGER_CHANNEL_MODE_MONO) {
strlcpy(st_device_name, ST_EC_REF_LOOPBACK_DEVICE_MONO,
DEVICE_NAME_MAX_SIZE);
} else if (v_info->ec_ref_channel_cnt == SOUND_TRIGGER_CHANNEL_MODE_STEREO) {
strlcpy(st_device_name, ST_EC_REF_LOOPBACK_DEVICE_STEREO,
DEVICE_NAME_MAX_SIZE);
} else if (v_info->ec_ref_channel_cnt == SOUND_TRIGGER_CHANNEL_MODE_QUAD) {
strlcpy(st_device_name, ST_EC_REF_LOOPBACK_DEVICE_QUAD,
DEVICE_NAME_MAX_SIZE);
quad_downmix.quadrx_dwnmix_enable = true;
ALOGD("set param for 4 ch ec");
status_type = ffv_set_param_fn(p_pcm_ses->handle,
(char *)&quad_downmix,
FFV_QUADRX_USE_DWNMIX_PARAM,
sizeof(ffv_quadrx_use_dwnmix_param_t));
if (status_type) {
ALOGE("%s: ERROR. ffv_set_param_fn for quad channel ec ref %d",
__func__, status_type);
return -EINVAL;
}
} else {
ALOGE("%s: Invalid ec ref channel count %d",
__func__, v_info->ec_ref_channel_cnt);
return -EINVAL;
}
if (platform_stdev_compare_device_type(p_ses->stdev->ec_ref_dev,
AUDIO_DEVICE_OUT_LINE))
strlcat(st_device_name, " lineout", DEVICE_NAME_MAX_SIZE);
ALOGD("%s: enable device = %s", __func__,
st_device_name);
audio_route_apply_and_update_path(p_ses->stdev->audio_route,
st_device_name);
p_pcm_ses->ec_ref_dev_name = strdup(st_device_name);
} else {
if (!p_pcm_ses->ec_ref_dev_name) {
ALOGE("%s: Invalid ec ref device name", __func__);
return -EINVAL;
}
ALOGD("%s: disable device = %s", __func__,
p_pcm_ses->ec_ref_dev_name);
audio_route_reset_and_update_path(p_ses->stdev->audio_route,
p_pcm_ses->ec_ref_dev_name);
free(p_pcm_ses->ec_ref_dev_name);
p_pcm_ses->ec_ref_dev_name = NULL;
}
return 0;
}
static int check_and_start_ec_ref_ses(st_hw_session_t *p_ses, bool start)
{
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
int status = 0;
int retry_num = 0;
if (p_ses->vendor_uuid_info->split_ec_ref_data) {
ALOGV("%s:[%d] Ignore ec ref start", __func__, p_pcm_ses->common.sm_handle);
return status;
}
if (!p_pcm_ses->ec_ref_pcm) {
ALOGW("%s: ec ref pcm NULL", __func__);
return status;
}
if (start) {
status = pcm_start(p_pcm_ses->ec_ref_pcm);
while (status && (retry_num < SOUND_TRIGGER_PCM_MAX_RETRY)) {
usleep(SOUND_TRIGGER_PCM_SLEEP_WAIT);
retry_num++;
ALOGI("%s: pcm_start retrying..status %d errno %d, retry cnt %d",
__func__, status, errno, retry_num);
status = pcm_start(p_pcm_ses->ec_ref_pcm);
}
if (status) {
ALOGE("%s: ERROR. pcm_start failed, returned status %d, err=%s",
__func__, status, pcm_get_error(p_pcm_ses->ec_ref_pcm));
}
ALOGD("%s: ec ref session started", __func__);
} else {
pcm_stop(p_pcm_ses->ec_ref_pcm);
}
return status;
}
static int check_and_enable_ec_ref_use_case(bool enable, st_hw_session_t *p_ses)
{
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
char use_case[USECASE_STRING_SIZE];
audio_devices_t capture_device;
int status = 0;
if (p_ses->vendor_uuid_info->split_ec_ref_data) {
ALOGV("%s:[%d] Ignore ec ref enable usecase", __func__, p_pcm_ses->common.sm_handle);
return status;
}
strlcpy(use_case,
p_ses->stdev->arm_pcm_use_cases[p_pcm_ses->ec_ref_use_case_idx].use_case,
USECASE_STRING_SIZE);
if (enable) {
ALOGD("%s: enable use case = %s", __func__, use_case);
audio_route_apply_and_update_path(p_ses->stdev->audio_route, use_case);
/* send device calibration for ec ref session */
capture_device = platform_stdev_get_capture_device(p_ses->stdev->platform);
platform_stdev_send_calibration(p_ses->stdev->platform,
capture_device,
p_ses->exec_mode,
p_ses->vendor_uuid_info,
SOUND_TRIGGER_DEVICE_DEFAULT_APP_TYPE,
false, ST_DEVICE_CAL);
status = pcm_prepare(p_pcm_ses->ec_ref_pcm);
if (status < 0) {
ALOGE("%s: pcm_prepare returned %s", __func__,
pcm_get_error(p_pcm_ses->ec_ref_pcm));
return status;
}
} else {
ALOGD("%s: disable use case = %s", __func__, use_case);
audio_route_reset_and_update_path(p_ses->stdev->audio_route, use_case);
}
return status;
}
static int enable_use_case(bool enable, st_hw_session_t *p_ses)
{
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
char use_case[USECASE_STRING_SIZE];
int status = 0;
strlcpy(use_case,
p_ses->stdev->arm_pcm_use_cases[p_ses->use_case_idx].use_case,
USECASE_STRING_SIZE);
if (enable) {
ALOGD("%s: enable use case = %s", __func__, use_case);
audio_route_apply_and_update_path(p_ses->stdev->audio_route, use_case);
status = pcm_prepare(p_pcm_ses->pcm);
if (status < 0) {
ALOGE("%s: pcm_prepare returned %s", __func__,
pcm_get_error(p_pcm_ses->pcm));
return status;
}
} else {
ALOGD("%s: disable use case = %s", __func__, use_case);
audio_route_reset_and_update_path(p_ses->stdev->audio_route, use_case);
}
return status;
}
#ifdef QUEUE_PROCESS_BUFFERS
static void process_buf_queue_push(struct process_buf_queue **queue,
struct process_buf_queue *node)
{
struct process_buf_queue *iter;
node->next = NULL;
if ((*queue) == NULL) {
*queue = node;
} else {
iter = *queue;
while (iter->next) {
iter = iter->next;
}
iter->next = node;
}
}
static struct process_buf_queue *process_buf_queue_pop(struct process_buf_queue **queue)
{
struct process_buf_queue *node = (*queue);
if (node != NULL) {
*queue = node->next;
node->next = NULL;
}
return node;
}
static int init_process_buffers(st_hw_session_pcm_t *p_pcm_ses)
{
unsigned int data_size, in_buf_size, i;
bool split_ec_ref_data = p_pcm_ses->common.vendor_uuid_info->split_ec_ref_data;
in_buf_size = split_ec_ref_data ? p_pcm_ses->split_in_buf_size : p_pcm_ses->in_buf_size;
data_size = in_buf_size + p_pcm_ses->ec_ref_buf_size + p_pcm_ses->out_buf_size;
p_pcm_ses->process_buf_data = (void *)calloc(data_size, NUM_PROCESS_BUFS);
if (p_pcm_ses->process_buf_data == NULL) {
ALOGE("%s: failed to allocate process buf data %d", __func__, data_size);
return -ENOMEM;
}
p_pcm_ses->process_buf = NULL;
p_pcm_ses->process_buf_free = NULL;
for (i = 0; i < NUM_PROCESS_BUFS; i++) {
struct process_buf_queue *buf = &p_pcm_ses->process_buf_nodes[i];
buf->buffer.data = &(((char *)p_pcm_ses->process_buf_data)[i * data_size]);
buf->buffer.length = data_size;
buf->buffer.in_buf_ptr = (int16_t *)buf->buffer.data;
buf->buffer.ec_ref_buf_ptr = (int16_t *) ((char *)buf->buffer.data + in_buf_size);
buf->buffer.out_buf_ptr = (int16_t *)((char *)buf->buffer.data + p_pcm_ses->ec_ref_buf_size);
process_buf_queue_push(&p_pcm_ses->process_buf_free, buf);
}
return 0;
}
static int deinit_process_buffers(st_hw_session_pcm_t *p_pcm_ses)
{
if(p_pcm_ses->process_buf_data != NULL)
free(p_pcm_ses->process_buf_data);
p_pcm_ses->process_buf_data = NULL;
p_pcm_ses->process_buf = NULL;
p_pcm_ses->process_buf_free = NULL;
}
static void *st_ffv_process_thread_loop(void *context)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)context;
bool split_ec_ref_data = p_pcm_ses->common.vendor_uuid_info->split_ec_ref_data;
unsigned int in_buf_size =
split_ec_ref_data ? p_pcm_ses->split_in_buf_size : p_pcm_ses->in_buf_size;
struct process_buf_queue *p_buf;
if (p_pcm_ses == NULL) {
ALOGE("%s: ERROR: invalid context", __func__);
return NULL;
}
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_DECLARE(FILE *fptr_cap = NULL; static int file_cnt = 0);
ST_DBG_FILE_OPEN_WR(fptr_cap, ST_DEBUG_DUMP_LOCATION,
"ffv_capture_data", "pcm", file_cnt++);
ST_DBG_DECLARE(FILE *fptr_ec = NULL; static int file_cnt_2 = 0);
ST_DBG_FILE_OPEN_WR(fptr_ec, ST_DEBUG_DUMP_LOCATION,
"ffv_ec_ref_data", "pcm", file_cnt_2++);
ST_DBG_DECLARE(FILE *fptr_out = NULL; static int file_cnt_3 = 0);
ST_DBG_FILE_OPEN_WR(fptr_out, ST_DEBUG_DUMP_LOCATION,
"ffv_out_data", "pcm", file_cnt_3++);
}
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO);
prctl(PR_SET_NAME, (unsigned long)"sound trigger process", 0, 0, 0);
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
pthread_mutex_lock(&p_pcm_ses->st_ffv_process_lock);
init_process_buffers(p_pcm_ses);
while (!p_pcm_ses->exit_st_ffv_process_thread) {
struct process_buf_queue *p_buf;
int16_t *process_in_ptr, *process_out_ptr, *process_ec_ref_ptr;
if (p_pcm_ses->process_buf == NULL) {
ALOGV("%s: waiting for buffer", __func__);
pthread_cond_wait(&p_pcm_ses->st_ffv_process_cond,
&p_pcm_ses->st_ffv_process_lock);
}
if (p_pcm_ses->exit_st_ffv_process_thread)
break;
p_buf = process_buf_queue_pop(&p_pcm_ses->process_buf);
pthread_mutex_unlock(&p_pcm_ses->st_ffv_process_lock);
process_in_ptr = p_buf->buffer.in_buf_ptr;
process_ec_ref_ptr = p_buf->buffer.ec_ref_buf_ptr;
process_out_ptr = p_buf->buffer.out_buf_ptr;
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_WRITE(fptr_cap, process_in_ptr, in_buf_size);
ST_DBG_FILE_WRITE(fptr_ec, process_ec_ref_ptr,
p_pcm_ses->ec_ref_buf_size);
}
ffv_process_fn(p_pcm_ses->handle, process_in_ptr,
process_out_ptr, process_ec_ref_ptr);
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_WRITE(fptr_out, process_out_ptr,
p_pcm_ses->out_buf_size);
}
pthread_mutex_lock(&p_pcm_ses->st_ffv_process_lock);
process_buf_queue_push(&p_pcm_ses->process_buf_free, p_buf);
pthread_cond_signal(&p_pcm_ses->st_ffv_capture_cond);
}
exit:
p_pcm_ses->st_ffv_process_thread_started = false;
deinit_process_buffers(p_pcm_ses);
pthread_mutex_unlock(&p_pcm_ses->st_ffv_process_lock);
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_CLOSE(fptr_cap);
ST_DBG_FILE_CLOSE(fptr_ec);
ST_DBG_FILE_CLOSE(fptr_out);
}
ALOGD("%s: Exit", __func__);
return NULL;
}
static int push_process_buffers(st_hw_session_pcm_t *p_pcm_ses, int16_t *in_pcm,
int16_t *out_pcm, int16_t *ec_ref_pcm, unsigned int in_buf_size)
{
struct process_buf_queue *p_buf;
pthread_mutex_lock(&p_pcm_ses->st_ffv_process_lock);
if (!p_pcm_ses->st_ffv_process_thread_started) {
ALOGE("%s: ERROR. process thread not started", __func__);
return -EINVAL;
}
if (p_pcm_ses->process_buf_free == NULL) {
ALOGD("%s: waiting for process buffer free", __func__);
pthread_cond_wait(&p_pcm_ses->st_ffv_capture_cond,
&p_pcm_ses->st_ffv_process_lock);
if (p_pcm_ses->process_buf_free == NULL) {
pthread_mutex_unlock(&p_pcm_ses->st_ffv_process_lock);
ALOGE("%s: failed to acquire buffers", __func__);
return -EINVAL;
}
}
p_buf = process_buf_queue_pop(&p_pcm_ses->process_buf_free);
memcpy(p_buf->buffer.in_buf_ptr, in_pcm, in_buf_size);
memcpy(p_buf->buffer.ec_ref_buf_ptr, ec_ref_pcm, p_pcm_ses->ec_ref_buf_size);
memcpy(p_buf->buffer.out_buf_ptr, out_pcm, p_pcm_ses->out_buf_size);
process_buf_queue_push(&p_pcm_ses->process_buf, p_buf);
pthread_cond_signal(&p_pcm_ses->st_ffv_process_cond);
pthread_mutex_unlock(&p_pcm_ses->st_ffv_process_lock);
return 0;
}
#else
static void *st_ffv_process_thread_loop(void *context __unused)
{
return NULL;
}
#endif
static void *capture_thread_loop(void *context)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)context;
int status = 0;
int16_t *in_ptr = NULL, *process_in_ptr = NULL, *process_out_ptr = NULL;
int16_t *process_bsp_out_ptr = NULL;
int16_t *process_ec_ref_ptr = NULL;
int i, ch;
int total_in_ch, in_ch, ec_ref_ch;
unsigned int in_buf_size;
static bool write_1 = true;
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_DECLARE(FILE *fptr_cap = NULL; static int file_cnt = 0);
ST_DBG_DECLARE(FILE *fptr_ec = NULL; static int file_cnt_2 = 0);
ST_DBG_DECLARE(FILE *fptr_split = NULL; static int file_cnt_3 = 0);
ST_DBG_DECLARE(FILE *fptr_out = NULL; static int file_cnt_4 = 0);
ST_DBG_DECLARE(FILE *fptr_bsp_out = NULL; static int file_cnt_5 = 0);
}
if (p_pcm_ses == NULL) {
ALOGE("%s: ERROR: invalid context", __func__);
goto exit;
}
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_OPEN_WR(fptr_cap, ST_DEBUG_DUMP_LOCATION,
"st_capture_data", "pcm", file_cnt++);
ST_DBG_FILE_OPEN_WR(fptr_ec, ST_DEBUG_DUMP_LOCATION,
"st_ec_ref_data", "pcm", file_cnt_2++);
ST_DBG_FILE_OPEN_WR(fptr_split, ST_DEBUG_DUMP_LOCATION,
"st_split_capture_data", "pcm", file_cnt_3++);
ST_DBG_FILE_OPEN_WR(fptr_out, ST_DEBUG_DUMP_LOCATION,
"st_out_data", "pcm", file_cnt_4++);
ST_DBG_FILE_OPEN_WR(fptr_bsp_out, ST_DEBUG_DUMP_LOCATION,
"st_bsp_out_data", "pcm", file_cnt_5++);
}
st_cpu_affinity_set(p_pcm_ses);
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO);
prctl(PR_SET_NAME, (unsigned long)"sound trigger capture", 0, 0, 0);
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
ALOGV("%s:[%d] split ec ref %s", __func__, p_pcm_ses->common.sm_handle,
p_pcm_ses->common.vendor_uuid_info->split_ec_ref_data ? "set" : "not set");
pthread_mutex_lock(&p_pcm_ses->capture_thread_lock);
while (!p_pcm_ses->exit_capture_thread) {
pthread_mutex_unlock(&p_pcm_ses->capture_thread_lock);
ALOGVV("%s: pcm_read reading bytes=%d", __func__, p_pcm_ses->in_buf_size);
status = pcm_read(p_pcm_ses->pcm, p_pcm_ses->in_buf, p_pcm_ses->in_buf_size);
ALOGVV("%s: pcm_read done", __func__);
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_WRITE(fptr_cap, p_pcm_ses->in_buf,
p_pcm_ses->in_buf_size);
}
if (status) {
ALOGE("%s: pcm read failed status %d - %s", __func__, status,
pcm_get_error(p_pcm_ses->pcm));
p_pcm_ses->exit_capture_thread = true;
goto exit;
}
if (!p_pcm_ses->common.vendor_uuid_info->split_ec_ref_data) {
/* read EC ref data */
ALOGVV("%s: pcm_read reading bytes=%d", __func__, p_pcm_ses->ec_ref_buf_size);
status = pcm_read(p_pcm_ses->ec_ref_pcm, p_pcm_ses->ec_ref_buf, p_pcm_ses->ec_ref_buf_size);
ALOGVV("%s: pcm_read done", __func__);
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_WRITE(fptr_ec, p_pcm_ses->ec_ref_buf,
p_pcm_ses->ec_ref_buf_size);
}
if (status) {
ALOGE("%s: ec ref pcm read failed status %d - %s", __func__, status,
pcm_get_error(p_pcm_ses->ec_ref_pcm));
p_pcm_ses->exit_capture_thread = true;
goto exit;
}
process_in_ptr = (int16_t *)p_pcm_ses->in_buf;
process_ec_ref_ptr = (int16_t *)p_pcm_ses->ec_ref_buf;
in_buf_size = p_pcm_ses->in_buf_size;
} else {
/* split input buffer into actual input channels and EC ref channels */
in_ptr = (int16_t *)p_pcm_ses->in_buf;
process_in_ptr = (int16_t *)p_pcm_ses->split_in_buf;
process_ec_ref_ptr = (int16_t *)p_pcm_ses->ec_ref_buf;
total_in_ch = p_pcm_ses->capture_config.channels;
ec_ref_ch = p_pcm_ses->ec_ref_config.channels;
in_ch = total_in_ch - ec_ref_ch;
for (i = 0; i < (int) p_pcm_ses->capture_config.period_size; i++) {
for (ch = 0; ch < in_ch; ch++) {
process_in_ptr[i*in_ch+ch] =
in_ptr[i*total_in_ch+ch];
}
for (ch = 0; ch < ec_ref_ch; ch++) {
process_ec_ref_ptr[i*ec_ref_ch+ch] =
in_ptr[i*total_in_ch+in_ch+ch];
}
}
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_WRITE(fptr_split, p_pcm_ses->split_in_buf,
p_pcm_ses->split_in_buf_size);
ST_DBG_FILE_WRITE(fptr_ec, p_pcm_ses->ec_ref_buf,
p_pcm_ses->ec_ref_buf_size);
}
in_buf_size = p_pcm_ses->split_in_buf_size;
}
process_out_ptr = (int16_t *)p_pcm_ses->out_buf;
process_bsp_out_ptr = (int16_t *)p_pcm_ses->bsp_out_buf;
#ifdef QUEUE_PROCESS_BUFFERS
status = push_process_buffers(p_pcm_ses, process_in_ptr,
process_out_ptr, process_ec_ref_ptr, in_buf_size);
if (status) {
ALOGE("%s: failed to push process buffers %d", __func__, status);
p_pcm_ses->exit_capture_thread = true;
goto exit;
}
#else
if (ffv_process_v2_fn) {
if (write_1)
ALOGD("%s: calling ffv_process_v2_fn", __func__);
ffv_process_v2_fn(p_pcm_ses->handle, process_in_ptr,
process_out_ptr, process_bsp_out_ptr, process_ec_ref_ptr);
if (p_pcm_ses->esp_handle)
esp_process_fn(p_pcm_ses->esp_handle, process_bsp_out_ptr);
} else {
if (write_1)
ALOGD("%s: calling ffv_process_fn", __func__);
ffv_process_fn(p_pcm_ses->handle, process_in_ptr,
process_out_ptr, process_ec_ref_ptr);
}
if (write_1) {
ALOGD("%s: return from ffv_process(_v2)_fn", __func__);
write_1 = false;
}
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_WRITE(fptr_out, process_out_ptr,
p_pcm_ses->out_buf_size);
ST_DBG_FILE_WRITE(fptr_bsp_out, process_bsp_out_ptr,
p_pcm_ses->bsp_out_buf_size);
}
#endif
pthread_mutex_lock(&p_pcm_ses->capture_thread_lock);
}
pthread_mutex_unlock(&p_pcm_ses->capture_thread_lock);
exit:
ALOGD("%s: Exit status=%d", __func__, status);
p_pcm_ses->capture_thread_started = false;
write_1 = true;
if (p_pcm_ses->common.stdev->enable_debug_dumps) {
ST_DBG_FILE_CLOSE(fptr_cap);
ST_DBG_FILE_CLOSE(fptr_ec);
ST_DBG_FILE_CLOSE(fptr_split);
ST_DBG_FILE_CLOSE(fptr_out);
ST_DBG_FILE_CLOSE(fptr_bsp_out);
}
return NULL;
}
static void st_event_callback(void *cb_data, void *handle, FfvEventType event_type,
void *event_payload, size_t event_payload_size __unused)
{
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)cb_data;
callback_event_t *ev = NULL;
ALOGD("%s: handle %p", __func__, handle);
if (!handle || !event_payload) {
ALOGE("%s: ERROR. NULL params", __func__);
return;
}
if (!p_pcm_ses) {
ALOGE("%s: ERROR. NULL session", __func__);
return;
}
ALOGD("%s: sm handle [%d]", __func__, p_pcm_ses->common.sm_handle);
ev= (callback_event_t *)calloc(1, sizeof(callback_event_t));
if (!ev) {
ALOGE("%s: failed to allocate mem for event", __func__);
goto exit;
}
ev->event_type = event_type;
ev->ev_payload = event_payload;
exit:
pthread_mutex_lock(&p_pcm_ses->callback_thread_lock);
if (!ev)
p_pcm_ses->exit_callback_thread = true;
else
list_add_tail(&p_pcm_ses->ev_list, &ev->node);
pthread_cond_signal(&p_pcm_ses->ev_cond);
pthread_mutex_unlock(&p_pcm_ses->callback_thread_lock);
}
/*
* Print detection event payload for non LE case only as
* client/test app is parsing/printing the payload in LE case.
*/
#if LINUX_ENABLED
#define print_detection_event(hw_sess_event) (0)
#else
static void print_detection_event(st_hw_sess_event_t sess_info)
{
uint32_t *payload_32 = NULL;
uint32_t version, key, key_version, key_size;
int k;
payload_32 = (uint32_t *)sess_info.payload.detected.detect_payload;
version = *payload_32++;
ALOGVV("%s: version %d", __func__, version);
if (version == 0x1) {
for (k = 0; k < KWD_MAX; k++) {
key = *payload_32++;
key_version = *payload_32++;
key_size = *payload_32++;
if (key == KWD_INDEX) {
/* parse payload for key == KWD_INDEX */
if (key_version != 0x1) {
ALOGE("%s: Invalid version for keyword index key %d",
__func__, key_version);
goto exit;
}
ALOGD("%s: keyword start index %d", __func__, *payload_32++);
ALOGD("%s: keyword stop index %d", __func__, *payload_32++);
} else {
payload_32 = (uint32_t *)((char *)payload_32 + key_size);
}
}
} else {
ALOGE("%s: Invalid version for detection event payload %d",
__func__, version);
goto exit;
}
exit:
return;
}
#endif
static void *callback_thread_loop(void *context)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)context;
int event_status;
st_hw_sess_event_t hw_sess_event; /* used to report event to st_session */
void *payload;
int payload_size;
struct listnode *item;
callback_event_t *ev = NULL;
uint64_t timestamp;
ffv_keyword_detection_status_t *ev_payload;
bool send_event;
if (p_pcm_ses == NULL) {
ALOGE("%s: ERROR. null context.. exiting", __func__);
return NULL;
}
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DEFAULT);
prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0);
pthread_mutex_lock(&p_pcm_ses->callback_thread_lock);
while (!p_pcm_ses->exit_callback_thread) {
if (list_empty(&p_pcm_ses->ev_list)) {
ALOGI("%s:[%d] Waiting for event",
__func__, p_pcm_ses->common.sm_handle);
pthread_cond_wait(&p_pcm_ses->ev_cond, &p_pcm_ses->callback_thread_lock);
}
if (p_pcm_ses->exit_callback_thread)
break;
send_event = false;
item = list_head(&p_pcm_ses->ev_list);
ev = node_to_item(item, callback_event_t, node);
list_remove(item);
ALOGD("%s: processing event type %d", __func__, ev->event_type);
switch (ev->event_type) {
case EVENT_KEYWORD_DETECTION:
ev_payload = (ffv_keyword_detection_status_t *)ev->ev_payload;
timestamp = ((uint64_t)ev_payload->timestamp_msw << 32) |
ev_payload->timestamp_lsw;
payload_size = ev_payload->payload_size;
payload = (char *)ev_payload + sizeof(ffv_keyword_detection_status_t);
ALOGD("%s: detection status %d", __func__, ev_payload->detection_status);
switch(ev_payload->detection_status) {
case KWD_STATUS_DETECTED:
case KWD_STATUS_IN_DISCOVERY:
case KWD_STATUS_IN_PROCESS:
event_status = RECOGNITION_STATUS_SUCCESS;
send_event = true;
break;
case KWD_STATUS_END_SPEECH:
case KWD_STATUS_REJECTED:
event_status = RECOGNITION_STATUS_FAILURE;
send_event = true;
break;
case KWD_STATUS_NONE:
default:
break;
}
break;
default:
break;
}
free(ev);
if (!send_event)
continue;
ALOGI("%s:[%d] Recognition event %d",
__func__, p_pcm_ses->common.sm_handle, event_status);
/* inform st_session of the event */
hw_sess_event.event_id = ST_HW_SESS_EVENT_DETECTED;
hw_sess_event.payload.detected.timestamp = timestamp;
hw_sess_event.payload.detected.detect_status = event_status;
hw_sess_event.payload.detected.detect_payload = payload;
hw_sess_event.payload.detected.payload_size = payload_size;
print_detection_event(hw_sess_event);
p_pcm_ses->common.callback_to_st_session(&hw_sess_event,
p_pcm_ses->common.cookie);
}
while (!list_empty(&p_pcm_ses->ev_list)) {
item = list_head(&p_pcm_ses->ev_list);
list_remove(item);
free(node_to_item(item, callback_event_t, node));
}
pthread_mutex_unlock(&p_pcm_ses->callback_thread_lock);
ALOGD("%s:[%d] Exit", __func__, p_pcm_ses->common.sm_handle);
return NULL;
}
static int deallocate_lab_buffers(st_hw_session_pcm_t* p_ses)
{
ALOGVV("%s:[%d] Enter", __func__, p_ses->common.sm_handle);
if (p_ses->lab_out_buf) {
free(p_ses->lab_out_buf);
p_ses->lab_out_buf = NULL;
}
if (p_ses->lab_cap_buf) {
free(p_ses->lab_cap_buf);
p_ses->lab_cap_buf = NULL;
}
p_ses->lab_buffers_allocated = false;
return 0;
}
static int allocate_lab_buffers(st_hw_session_pcm_t* p_ses)
{
int status = 0;
ALOGVV("%s:[%d] Enter", __func__, p_ses->common.sm_handle);
p_ses->lab_cap_buf_size = p_ses->lab_config.period_size * p_ses->lab_config.channels *
(pcm_format_to_bits(p_ses->lab_config.format) >> 3);
p_ses->lab_cap_buf = (unsigned char *) calloc(1, p_ses->lab_cap_buf_size);
if (!p_ses->lab_cap_buf) {
ALOGE("%s: ERROR. Can not allcoate lab buffer size %d", __func__, p_ses->lab_cap_buf_size);
status = -ENOMEM;
goto error_exit;
}
ALOGV("%s: Allocated lab buffer period size bytes =%d",
__func__, p_ses->lab_cap_buf_size);
/* Allocate lab out buffer to hold library read buffer length returned
* by ffv library which includes history buffer latency + read latency
* of ffv/st_hal client.
* This helps to avoid overrun in library/HAL by the time client initiates
* read of LAB.
*/
p_ses->read_buf_len = get_ffv_read_buffer_len(p_ses);
if (p_ses->read_buf_len == 0) {
ALOGE("%s: ERROR. Invalid read buffer length", __func__);
status = -ENOMEM;
goto error_exit;
}
p_ses->lab_out_buf_size = p_ses->read_buf_len;
p_ses->lab_out_buf = (unsigned char *) calloc(1, p_ses->lab_out_buf_size);
if (!p_ses->lab_out_buf) {
ALOGE("%s: ERROR. Can not allcoate out buffer size %d",
__func__, p_ses->lab_out_buf_size);
status = -ENOMEM;
goto error_exit;
}
ALOGV("%s: Allocated out buffer size=%d", __func__, p_ses->lab_out_buf_size);
p_ses->lab_buffers_allocated = true;
return 0;
error_exit:
deallocate_lab_buffers(p_ses);
return status;
}
static int deallocate_buffers(st_hw_session_pcm_t* p_ses)
{
ALOGVV("%s:[%d] Enter", __func__, p_ses->common.sm_handle);
if (p_ses->in_buf) {
free(p_ses->in_buf);
p_ses->in_buf = NULL;
}
if (p_ses->ec_ref_buf) {
free(p_ses->ec_ref_buf);
p_ses->ec_ref_buf = NULL;
}
if (p_ses->split_in_buf) {
free(p_ses->split_in_buf);
p_ses->split_in_buf = NULL;
}
if (p_ses->out_buf) {
free(p_ses->out_buf);
p_ses->out_buf = NULL;
}
if (p_ses->bsp_out_buf) {
free(p_ses->bsp_out_buf);
p_ses->bsp_out_buf = NULL;
}
p_ses->buffers_allocated = false;
return 0;
}
static int allocate_buffers(st_hw_session_pcm_t* p_ses)
{
int status = 0;
ALOGVV("%s:[%d] Enter", __func__, p_ses->common.sm_handle);
/* in_buf - buffer read from capture session */
p_ses->in_buf_size = pcm_frames_to_bytes(p_ses->pcm, p_ses->capture_config.period_size);
p_ses->in_buf = (unsigned char *)calloc(1, p_ses->in_buf_size);
if (!p_ses->in_buf) {
ALOGE("%s: ERROR. Can not allocate in buffer size %d", __func__, p_ses->in_buf_size);
status = -ENOMEM;
goto error_exit;
}
ALOGD("%s: Allocated in buffer size bytes =%d",
__func__, p_ses->in_buf_size);
if (!p_ses->common.vendor_uuid_info->split_ec_ref_data) {
p_ses->ec_ref_buf_size = pcm_frames_to_bytes(p_ses->ec_ref_pcm, p_ses->ec_ref_config.period_size);
} else {
p_ses->ec_ref_buf_size = p_ses->ec_ref_config.period_size * p_ses->ec_ref_config.channels *
(pcm_format_to_bits(p_ses->ec_ref_config.format) >> 3);
/* split_in_buf - split buffer when ec ref and mic data is packed */
p_ses->split_in_buf_size = p_ses->in_buf_size - p_ses->ec_ref_buf_size;
p_ses->split_in_buf = (unsigned char *)calloc(1, p_ses->split_in_buf_size);
if (!p_ses->split_in_buf) {
ALOGE("%s: ERROR. Can not allocate split in buffer size %d",
__func__, p_ses->split_in_buf_size);
status = -ENOMEM;
goto error_exit;
}
ALOGD("%s: Allocated split in buffer size bytes =%d",
__func__, p_ses->split_in_buf_size);
}
/* ec_buf - buffer read from ec ref capture session */
p_ses->ec_ref_buf = (unsigned char *)calloc(1, p_ses->ec_ref_buf_size);
if (!p_ses->ec_ref_buf) {
ALOGE("%s: ERROR. Can not allocate ec ref buffer size %d",
__func__, p_ses->ec_ref_buf_size);
status = -ENOMEM;
goto error_exit;
}
ALOGD("%s: Allocated ec ref buffer size bytes =%d chann %d",
__func__, p_ses->ec_ref_buf_size, p_ses->ec_ref_config.channels);
/* out_buf - output buffer from FFV + SVA library */
p_ses->out_buf_size = p_ses->out_config.period_size * p_ses->out_config.channels *
(pcm_format_to_bits(p_ses->out_config.format) >> 3);
p_ses->out_buf = (unsigned char *)calloc(1, p_ses->out_buf_size);
if (!p_ses->out_buf) {
ALOGE("%s: ERROR. Can not allocate out buffer size %d", __func__, p_ses->out_buf_size);
status = -ENOMEM;
goto error_exit;
}
ALOGD("%s: Allocated out buffer size bytes =%d",
__func__, p_ses->out_buf_size);
/* bsp_out_buf - bsp output buffer from ESP library */
p_ses->bsp_out_buf_size = p_ses->out_config.period_size * p_ses->out_config.channels *
(pcm_format_to_bits(p_ses->out_config.format) >> 3);
p_ses->bsp_out_buf = (unsigned char *)calloc(1, p_ses->bsp_out_buf_size);
if (!p_ses->bsp_out_buf) {
ALOGE("%s: ERROR. Can not allocate out buffer size %d", __func__, p_ses->bsp_out_buf_size);
status = -ENOMEM;
goto error_exit;
}
ALOGD("%s: Allocated bsp out buffer size bytes =%d",
__func__, p_ses->bsp_out_buf_size);
p_ses->buffers_allocated = true;
return 0;
error_exit:
deallocate_buffers(p_ses);
return status;
}
static int sound_trigger_set_device
(
st_hw_session_t* p_ses,
bool enable
)
{
char st_device_name[DEVICE_NAME_MAX_SIZE] = { 0 };
int ref_cnt_idx = 0, ref_cnt = 0;
int status = 0;
st_device_t st_device = 0;
audio_devices_t capture_device = 0;
int app_type = 0;
st_profile_type_t profile_type = 0;
bool is_hwmad_device = false;
is_hwmad_device = platform_stdev_is_hwmad_backend(p_ses->stdev->platform,
p_ses->st_device, p_ses->exec_mode);
profile_type = p_ses->vendor_uuid_info->profile_type;
if (enable) {
capture_device = platform_stdev_get_capture_device(p_ses->stdev->platform);
st_device = platform_stdev_get_device(p_ses->stdev->platform,
p_ses->vendor_uuid_info, capture_device, p_ses->exec_mode);
if (platform_stdev_get_device_name(p_ses->stdev->platform, p_ses->exec_mode,
st_device, st_device_name) < 0) {
ALOGE("%s: Invalid sound trigger device name returned", __func__);
return -EINVAL;
}
pthread_mutex_lock(&p_ses->stdev->ref_cnt_lock);
ref_cnt_idx = (p_ses->exec_mode * ST_DEVICE_MAX) + st_device;
ref_cnt = ++(p_ses->stdev->dev_ref_cnt[ref_cnt_idx]);
if (1 == ref_cnt) {
app_type = platform_stdev_get_device_app_type(p_ses->stdev->platform,
profile_type);
status = platform_stdev_send_calibration(p_ses->stdev->platform,
capture_device,
p_ses->exec_mode,
p_ses->vendor_uuid_info,
app_type,
false, ST_DEVICE_CAL);
if (!status) {
/*
* If no dedicated path, make sure to disable device first and then
* enable with conc enabled to handle below scenario:
* 1. STHAL and AHAL runs concurrently with conc capture enabled
* 2. STHAL session closed then it will not disable device as AHAL
* usecase is going on. Then AHAL disables device when it closes session
* 3. If STHAL is started again then device will not be enabled as stdev
* mixer value in audio route is not reset, so detections won't work
* 4. To fix this call audio reset then enable device
*/
if (!platform_stdev_is_dedicated_sva_path(p_ses->stdev->platform) &&
p_ses->stdev->conc_capture_supported &&
p_ses->stdev->reset_backend &&
!is_hwmad_device) {
check_and_set_ec_ref_device(p_ses, 0);
audio_route_reset_and_update_path(p_ses->stdev->audio_route,
st_device_name);
}
ALOGD("%s: enable device (%x) = %s", __func__, st_device,
st_device_name);
audio_route_apply_and_update_path(p_ses->stdev->audio_route,
st_device_name);
check_and_set_ec_ref_device(p_ses, enable);
} else {
--(p_ses->stdev->dev_ref_cnt[ref_cnt_idx]);
ALOGE("%s: failed to send calibration %d", __func__, status);
}
}
pthread_mutex_unlock(&p_ses->stdev->ref_cnt_lock);
p_ses->st_device = st_device;
p_ses->st_device_name = strdup(st_device_name);
} else {
if (!p_ses->st_device_name) {
ALOGE("%s: Invalid sound trigger device name", __func__);
return -EINVAL;
}
ref_cnt_idx = (p_ses->exec_mode * ST_DEVICE_MAX) + p_ses->st_device;
pthread_mutex_lock(&p_ses->stdev->ref_cnt_lock);
ref_cnt = p_ses->stdev->dev_ref_cnt[ref_cnt_idx];
if (0 < ref_cnt) {
ref_cnt = --(p_ses->stdev->dev_ref_cnt[ref_cnt_idx]);
} else {
ALOGV("%s: ref_cnt = %d", __func__, ref_cnt);
pthread_mutex_unlock(&p_ses->stdev->ref_cnt_lock);
return status;
}
if (0 == ref_cnt) {
if(p_ses->stdev->reset_backend || is_hwmad_device) {
ALOGD("%s: disable device (%x) = %s", __func__, p_ses->st_device,
p_ses->st_device_name);
check_and_set_ec_ref_device(p_ses, enable);
audio_route_reset_and_update_path(p_ses->stdev->audio_route,
p_ses->st_device_name);
} else {
ALOGD("%s: Non-hwmad device, concurrent capture on, do not disable", __func__);
}
}
pthread_mutex_unlock(&p_ses->stdev->ref_cnt_lock);
free(p_ses->st_device_name);
}
return status;
}
static int reg_sm(st_hw_session_t *p_ses, void *sm_data,
unsigned int sm_size, uint32_t model_id __unused)
{
int status = 0;
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t*)p_ses;
struct st_vendor_info *v_info = p_ses->vendor_uuid_info;
pthread_attr_t attr;
int num_tx_in_ch, num_out_ch, num_ec_ref_ch;
int frame_len;
int sample_rate;
char vendor_config_path[MIXER_PATH_MAX_LENGTH];
char st_ffv_config_file[MIXER_PATH_MAX_LENGTH];
const char *config_file_path;
FfvStatusType status_type;
EspStatusType esp_status_type;
int total_mem_size;
int frame_len_ms = ST_ESP_FRAME_LENGTH_MS;
int product_id = 0;
char product_license[ST_LICENSE_STR_MAX_LENGTH + 1] = {0};
ALOGD("%s:[%d] Enter", __func__, p_ses->sm_handle);
p_pcm_ses->pcm_id =
platform_arm_get_pcm_device_id(p_ses->stdev->platform,
&p_ses->use_case_idx,
false);
if (p_pcm_ses->pcm_id < 0)
return -ENODEV;
p_pcm_ses->common.config = stdev_arm_pcm_config;
p_pcm_ses->out_config = p_pcm_ses->common.config;
p_pcm_ses->capture_config = p_pcm_ses->common.config;
p_pcm_ses->lab_config = p_pcm_ses->common.config;
platform_stdev_check_and_update_pcm_config(&p_pcm_ses->capture_config,
v_info);
p_pcm_ses->capture_config.period_size =
CALCULATE_PERIOD_SIZE(SOUND_TRIGGER_PCM_BUFFER_DURATION_MS,
p_pcm_ses->capture_config.rate,
SOUND_TRIGGER_PCM_PERIOD_COUNT, 32);
platform_stdev_get_vendor_config_path(vendor_config_path,
sizeof(vendor_config_path));
/* Get path for st_ffv_config_file_name in vendor */
snprintf(st_ffv_config_file, sizeof(st_ffv_config_file),
"%s/%s", vendor_config_path, ST_FFV_CONFIG_FILE_NAME);
config_file_path = st_ffv_config_file;
ALOGD("%s: opening pcm device=%d ", __func__, p_pcm_ses->pcm_id);
ALOGV("%s: config: channels=%d rate=%d, period_size=%d, period_cnt=%d, format=%d",
__func__, p_pcm_ses->capture_config.channels, p_pcm_ses->capture_config.rate,
p_pcm_ses->capture_config.period_size, p_pcm_ses->capture_config.period_count,
p_pcm_ses->capture_config.format);
p_pcm_ses->pcm = pcm_open(p_ses->stdev->snd_card, p_pcm_ses->pcm_id,
PCM_IN, &p_pcm_ses->capture_config);
if (!p_pcm_ses->pcm) {
ALOGE("%s: ERROR. pcm_open failed", __func__);
status = -ENODEV;
goto sm_error_1;
}
if (!pcm_is_ready(p_pcm_ses->pcm)) {
ALOGE("%s: ERROR. pcm_is_ready failed err=%s", __func__,
pcm_get_error(p_pcm_ses->pcm));
status = -ENODEV;
goto sm_error_1;
}
/* Set up EC ref session for the corresponding pcm session */
p_pcm_ses->ec_ref_config = p_pcm_ses->common.config;
platform_stdev_check_and_update_ec_ref_config(p_ses->stdev->platform,
v_info, &p_pcm_ses->ec_ref_config);
p_pcm_ses->ec_ref_config.period_size =
CALCULATE_PERIOD_SIZE(SOUND_TRIGGER_PCM_BUFFER_DURATION_MS,
p_pcm_ses->ec_ref_config.rate,
SOUND_TRIGGER_PCM_PERIOD_COUNT, 32);
ALOGV("%s: config: channels=%d rate=%d, period_size=%d, period_cnt=%d, format=%d",
__func__, p_pcm_ses->ec_ref_config.channels, p_pcm_ses->ec_ref_config.rate,
p_pcm_ses->ec_ref_config.period_size, p_pcm_ses->ec_ref_config.period_count,
p_pcm_ses->ec_ref_config.format);
if (!p_ses->vendor_uuid_info->split_ec_ref_data) {
p_pcm_ses->ec_ref_pcm_id =
platform_arm_get_pcm_device_id(p_ses->stdev->platform,
&p_pcm_ses->ec_ref_use_case_idx,
true);
if (p_pcm_ses->ec_ref_pcm_id < 0) {
status = -ENODEV;
goto sm_error_1;
}
ALOGD("%s: opening pcm device=%d ", __func__, p_pcm_ses->ec_ref_pcm_id);
p_pcm_ses->ec_ref_pcm = pcm_open(p_ses->stdev->snd_card, p_pcm_ses->ec_ref_pcm_id,
PCM_IN, &p_pcm_ses->ec_ref_config);
if (!p_pcm_ses->ec_ref_pcm) {
ALOGE("%s: ERROR. EC ref pcm_open failed", __func__);
status = -ENODEV;
goto sm_error;
}
if (!pcm_is_ready(p_pcm_ses->ec_ref_pcm)) {
ALOGE("%s: ERROR. EC ref pcm_is_ready failed err=%s", __func__,
pcm_get_error(p_pcm_ses->ec_ref_pcm));
status = -ENODEV;
goto sm_error;
}
}
num_ec_ref_ch = p_pcm_ses->ec_ref_config.channels;
num_tx_in_ch = p_ses->vendor_uuid_info->split_ec_ref_data ?
(p_pcm_ses->capture_config.channels - num_ec_ref_ch) :
p_pcm_ses->capture_config.channels;
num_out_ch = p_pcm_ses->out_config.channels;
frame_len = p_pcm_ses->capture_config.period_size;
sample_rate = p_pcm_ses->capture_config.rate;
ALOGD("%s: ec_ref_ch %d, tx_in_ch %d, out_ch %d, frame_len %d, sample_rate %d",
__func__, num_ec_ref_ch, num_tx_in_ch, num_out_ch, frame_len, sample_rate);
ALOGD("%s: config file path %s", __func__, config_file_path);
status = platform_stdev_get_license_by_product(p_ses->stdev->platform,
PRODUCT_FFV, &product_id, product_license);
ALOGD("%s: LICENSE[%s] key[%d]", __func__, product_license, product_id);
status_type = ffv_init_fn(&p_pcm_ses->handle, num_tx_in_ch, num_out_ch, num_ec_ref_ch,
frame_len, sample_rate, config_file_path, (char *)sm_data, sm_size,
&total_mem_size, product_id, product_license);
if (status_type) {
ALOGE("%s: ERROR. ffv_init returned %d", __func__, status_type);
status = -EINVAL;
goto sm_error;
}
ALOGD("%s: ffv_init success %p", __func__, p_pcm_ses->handle);
status_type = ffv_register_event_callback_fn(p_pcm_ses->handle,
st_event_callback,
p_pcm_ses);
if (status_type) {
ALOGE("%s: ERROR. ffv_reg_event_callback returned %d", __func__, status_type);
status = -EINVAL;
goto sm_error;
}
if (pcm_data.esp_lib_handle) {
esp_status_type = esp_init_fn(&p_pcm_ses->esp_handle, frame_len_ms,
sample_rate, &total_mem_size);
if (esp_status_type) {
ALOGE("%s: ERROR. esp_init returned %d", __func__, esp_status_type);
p_pcm_ses->esp_handle = NULL;
} else {
ALOGD("%s: esp_init success %p", __func__, p_pcm_ses->esp_handle);
}
}
p_pcm_ses->session_restart_done = false;
/* Callback thread to wait on event detection */
p_pcm_ses->exit_callback_thread = false;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&p_pcm_ses->callback_thread, &attr,
callback_thread_loop, p_pcm_ses);
ALOGD("%s:[%d] Exit, status=%d", __func__, p_pcm_ses->common.sm_handle,
status);
return 0;
sm_error:
if (!p_ses->vendor_uuid_info->split_ec_ref_data) {
if (p_pcm_ses->ec_ref_pcm) {
pcm_close(p_pcm_ses->ec_ref_pcm);
p_pcm_ses->ec_ref_pcm = NULL;
}
platform_arm_free_pcm_device_id(p_ses->stdev->platform,
p_pcm_ses->ec_ref_pcm_id,
true);
}
sm_error_1:
if (p_pcm_ses->pcm) {
pcm_close(p_pcm_ses->pcm);
p_pcm_ses->pcm = NULL;
}
platform_arm_free_pcm_device_id(p_ses->stdev->platform,
p_pcm_ses->pcm_id,
false);
ALOGD("%s:[%d] Exit, status=%d", __func__,
p_pcm_ses->common.sm_handle, status);
return status;
}
static int dereg_sm(st_hw_session_t *p_ses, uint32_t model_id __unused)
{
int status = 0;
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
if (!p_pcm_ses->pcm) {
ALOGV("%s: pcm NULL", __func__);
return status;
}
/* Exit the callback thread waiting on event detection */
pthread_mutex_lock(&p_pcm_ses->callback_thread_lock);
p_pcm_ses->exit_callback_thread = true;
pthread_cond_signal(&p_pcm_ses->ev_cond);
pthread_mutex_unlock(&p_pcm_ses->callback_thread_lock);
pthread_join(p_pcm_ses->callback_thread, (void **) NULL);
ffv_deinit_fn(p_pcm_ses->handle);
if (p_pcm_ses->esp_handle)
esp_deinit_fn(p_pcm_ses->esp_handle);
if (!p_ses->vendor_uuid_info->split_ec_ref_data) {
if (p_pcm_ses->ec_ref_pcm) {
pcm_close(p_pcm_ses->ec_ref_pcm);
p_pcm_ses->ec_ref_pcm = NULL;
}
platform_arm_free_pcm_device_id(p_ses->stdev->platform,
p_pcm_ses->ec_ref_pcm_id,
true);
}
if (p_pcm_ses->pcm) {
pcm_close(p_pcm_ses->pcm);
p_pcm_ses->pcm = NULL;
}
platform_arm_free_pcm_device_id(p_ses->stdev->platform,
p_pcm_ses->pcm_id,
false);
/* Deallocate buffers allocated during start_recognition */
if (p_ses->lab_enabled) {
if (p_pcm_ses->lab_buffers_allocated) {
deallocate_lab_buffers(p_pcm_ses);
}
}
if (p_pcm_ses->buffers_allocated)
deallocate_buffers(p_pcm_ses);
ALOGD("%s:[%d] Exit, status=%d", __func__, p_pcm_ses->common.sm_handle,
status);
return status;
}
static int reg_sm_params(st_hw_session_t* p_ses, unsigned int recognition_mode __unused,
bool capture_requested, struct sound_trigger_recognition_config *rc_config __unused)
{
int status = 0;
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t*)p_ses;
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
if (!p_pcm_ses->buffers_allocated) {
status = allocate_buffers(p_pcm_ses);
if (status) {
ALOGE("%s: buffer allocation failed %d", __func__, status);
goto error;
}
}
if (capture_requested) {
if (!p_pcm_ses->lab_buffers_allocated)
status = allocate_lab_buffers(p_pcm_ses);
}
p_ses->lab_enabled = capture_requested;
error:
ALOGD("%s:[%d] Exit, status=%d", __func__,
p_pcm_ses->common.sm_handle, status);
return status;
}
static int dereg_sm_params(st_hw_session_t* p_ses)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
return 0;
}
static int start(st_hw_session_t* p_ses)
{
int status = 0;
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
int retry_num = 0;
pthread_attr_t attr, attr_2;
char *params_buffer_ptr = NULL;
int param_id = FFV_RESET_AEC_PARAM;
int param_size = 0;
FfvStatusType status_type;
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
if (!p_pcm_ses->pcm) {
ALOGW("%s: pcm NULL", __func__);
status = -ENODEV;
return status;
}
status = enable_use_case(true, p_ses);
if (status) {
ALOGE("%s: enable use case returned %d", __func__, status);
return status;
}
status = check_and_enable_ec_ref_use_case(true, p_ses);
if (status) {
ALOGE("%s: enable ec ref use case returned %d", __func__, status);
return status;
}
/* notify library to reset AEC during each start */
status_type = ffv_set_param_fn(p_pcm_ses->handle, params_buffer_ptr,
param_id, param_size);
if (status_type) {
ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
status = -EINVAL;
return status;
}
status = pcm_start(p_pcm_ses->pcm);
while (status && (retry_num < SOUND_TRIGGER_PCM_MAX_RETRY)) {
usleep(SOUND_TRIGGER_PCM_SLEEP_WAIT);
retry_num++;
ALOGI("%s: pcm_start retrying..status %d errno %d, retry cnt %d",
__func__, status, errno, retry_num);
status = pcm_start(p_pcm_ses->pcm);
}
if (status) {
ALOGE("%s: ERROR. pcm_start failed, returned status %d, err=%s",
__func__, status, pcm_get_error(p_pcm_ses->pcm));
return status;
}
ALOGD("%s: capture session started", __func__);
status = check_and_start_ec_ref_ses(p_ses, true /* start */);
if (status) {
ALOGE("%s: ERROR. ec ref pcm_start failed, returned status %d",
__func__, status);
return status;
}
if (p_pcm_ses->session_restart_done == false) {
p_pcm_ses->session_restart_done = true;
restart_session(p_ses);
}
if (p_pcm_ses->st_ffv_process_thread_started == false) {
/* init process threadloop */
pthread_attr_init(&attr_2);
pthread_attr_setdetachstate(&attr_2, PTHREAD_CREATE_JOINABLE);
p_pcm_ses->exit_st_ffv_process_thread = false;
status = pthread_create(&p_pcm_ses->st_ffv_process_thread, &attr_2,
st_ffv_process_thread_loop, p_pcm_ses);
if (status) {
ALOGE("%s: ERROR. failed to start process thread status %d",
__func__, status);
return status;
}
p_pcm_ses->st_ffv_process_thread_started = true;
}
if (p_pcm_ses->capture_thread_started == false) {
/* init capture threadloop */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
p_pcm_ses->exit_capture_thread = false;
status = pthread_create(&p_pcm_ses->capture_thread, &attr,
capture_thread_loop, p_pcm_ses);
if (status) {
ALOGE("%s: ERROR. failed to start capture thread, status %d",
__func__, status);
return status;
}
p_pcm_ses->capture_thread_started = true;
}
ALOGD("%s:[%d] Exit, status=%d", __func__, p_pcm_ses->common.sm_handle,
status);
return status;
}
static int stop(st_hw_session_t* p_ses)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
int param_id = FFV_STOP_VOICE_RECOGNITION_PARAM;
char *params_buffer_ptr = NULL;
int param_size = 0;
FfvStatusType status_type;
int status = 0;
ALOGD("%s:[%d] Enter", __func__, p_pcm_ses->common.sm_handle);
if (!p_pcm_ses->pcm) {
ALOGW("%s: pcm NULL", __func__);
status = -ENODEV;
return status;
}
pthread_mutex_lock(&p_pcm_ses->capture_thread_lock);
p_pcm_ses->exit_capture_thread = true;
pthread_cond_signal(&p_pcm_ses->st_ffv_capture_cond);
pthread_mutex_unlock(&p_pcm_ses->capture_thread_lock);
if (p_pcm_ses->capture_thread)
pthread_join(p_pcm_ses->capture_thread, (void **) NULL);
p_pcm_ses->capture_thread_started = false;
pthread_mutex_lock(&p_pcm_ses->st_ffv_process_lock);
p_pcm_ses->exit_st_ffv_process_thread = true;
pthread_cond_signal(&p_pcm_ses->st_ffv_process_cond);
pthread_mutex_unlock(&p_pcm_ses->st_ffv_process_lock);
if (p_pcm_ses->st_ffv_process_thread)
pthread_join(p_pcm_ses->st_ffv_process_thread, (void **) NULL);
p_pcm_ses->st_ffv_process_thread_started = false;
/* notify library on voice recognition stop */
status_type = ffv_set_param_fn(p_pcm_ses->handle, params_buffer_ptr, param_id,
param_size);
if (status_type)
ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
check_and_start_ec_ref_ses(p_ses, false /* stop */);
check_and_enable_ec_ref_use_case(false, p_ses);
pcm_stop(p_pcm_ses->pcm);
enable_use_case(false, p_ses);
ALOGD("%s:[%d] Exit, status=%d", __func__, p_pcm_ses->common.sm_handle,
status);
return status;
}
static int pcm_stop_buffering(st_hw_session_t* p_ses)
{
int status = 0;
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
ALOGD("%s:[%d] Enter pcm %p", __func__, p_pcm_ses->common.sm_handle,
p_pcm_ses->pcm);
if (p_ses->lab_enabled) {
pthread_mutex_lock(&p_pcm_ses->lab_out_buf_lock);
pthread_cond_broadcast(&p_pcm_ses->lab_out_buf_cond);
pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
}
ALOGD("%s:[%d] Exit, status=%d", __func__, p_ses->sm_handle, status);
return status;
}
static void check_and_exit_lab(st_hw_session_t *p_ses)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
if ((p_pcm_ses->lab_buffers_allocated && !p_pcm_ses->exit_lab_processing) &&
CHECK_STATE(p_pcm_ses->common.state, SES_BUFFERING)) {
pthread_mutex_lock(&p_pcm_ses->lab_out_buf_lock);
p_pcm_ses->exit_lab_processing = true;
pthread_cond_signal(&p_pcm_ses->lab_out_buf_cond);
pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
}
}
static int stop_buffering(st_hw_session_t* p_ses)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
int status = 0;
ALOGD("%s:[%d] Enter pcm %p", __func__, p_pcm_ses->common.sm_handle,
p_pcm_ses->pcm);
if (!CHECK_STATE(p_ses->state, SES_BUFFERING)) {
return status;
}
check_and_exit_lab(p_ses);
status = pcm_stop_buffering(p_ses);
CLEAR_STATE(p_ses->state, SES_BUFFERING);
return status;
}
static int restart(st_hw_session_t* p_ses, unsigned int recognition_mode __unused,
struct sound_trigger_recognition_config *rc_config __unused)
{
st_hw_session_pcm_t *p_pcm_ses =
(st_hw_session_pcm_t *)p_ses;
char *params_buffer_ptr = NULL;
int param_id;
int param_size = 0;
FfvStatusType status_type;
/* notify library on voice recognition stop to restart detections*/
param_id = FFV_STOP_VOICE_RECOGNITION_PARAM;
status_type = ffv_set_param_fn(p_pcm_ses->handle, params_buffer_ptr, param_id,
param_size);
if (status_type) {
ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
return -EINVAL;
}
/* notify library to reset AEC */
param_id = FFV_RESET_AEC_PARAM;
status_type = ffv_set_param_fn(p_pcm_ses->handle, params_buffer_ptr, param_id,
param_size);
if (status_type) {
ALOGE("%s: ERROR. ffv_set_param_fn ret %d", __func__, status_type);
return -EINVAL;
}
return 0;
}
static int read_pcm_data(st_hw_session_pcm_t *p_ses,
unsigned char *buf,
unsigned int bytes)
{
unsigned int copy_bytes, read_bytes;
unsigned char *updated_rd_ptr;
struct timespec tspec;
int ret = 0;
pthread_mutex_lock(&p_ses->lab_out_buf_lock);
ALOGVV("%s: bytes=%d, unread_bytes=%d size=%d", __func__,
bytes, p_ses->lab_out_buf_unread_bytes, p_ses->lab_out_buf_size);
while (!p_ses->exit_lab_processing && (bytes > 0)) {
if (!p_ses->lab_out_buf_unread_bytes) {
ALOGVV("%s: waiting on cond, bytes=%d", __func__, 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(&p_ses->lab_out_buf_cond,
&p_ses->lab_out_buf_lock, &tspec);
ALOGVV("%s: done waiting on cond", __func__);
if (ret) {
ALOGE("%s: ERROR. read wait timed out, ret %d", __func__, ret);
p_ses->exit_lab_processing = true;
ret = -EIO;
goto exit;
}
if (p_ses->exit_lab_processing) {
ALOGVV("%s: buffering stopped while waiting on cond, exiting",
__func__);
ret = -EIO;
goto exit;
}
}
read_bytes = MIN(bytes, p_ses->lab_out_buf_unread_bytes);
if (p_ses->lab_out_buf_end_ptr < (p_ses->lab_out_buf_rd_ptr + read_bytes)) {
copy_bytes = p_ses->lab_out_buf_end_ptr - p_ses->lab_out_buf_rd_ptr;
memcpy(buf, p_ses->lab_out_buf_rd_ptr, copy_bytes);
memcpy(buf + copy_bytes, p_ses->lab_out_buf_start_ptr, read_bytes - copy_bytes);
updated_rd_ptr = p_ses->lab_out_buf_start_ptr + (read_bytes - copy_bytes);
p_ses->lab_out_buf_rd_ptr = updated_rd_ptr;
p_ses->lab_out_buf_unread_bytes -= read_bytes;
} else {
memcpy(buf, p_ses->lab_out_buf_rd_ptr, read_bytes);
p_ses->lab_out_buf_rd_ptr += read_bytes;
p_ses->lab_out_buf_unread_bytes -= read_bytes;
}
pthread_cond_signal(&p_ses->lab_out_buf_cond);
bytes -= read_bytes;
buf += read_bytes;
}
exit:
pthread_mutex_unlock(&p_ses->lab_out_buf_lock);
return 0;
}
static int write_pcm_data(st_hw_session_pcm_t *p_pcm_ses,
unsigned char *buf,
unsigned int bytes)
{
unsigned int free_bytes, write_bytes;
unsigned int copy_bytes;
unsigned char *updated_wr_ptr;
pthread_mutex_lock(&p_pcm_ses->lab_out_buf_lock);
ALOGVV("%s: bytes=%d, unread_bytes=%d size=%d", __func__,
bytes, p_pcm_ses->lab_out_buf_unread_bytes, p_pcm_ses->lab_out_buf_size);
while (!p_pcm_ses->exit_lab_processing && (bytes > 0)) {
free_bytes = p_pcm_ses->lab_out_buf_size - p_pcm_ses->lab_out_buf_unread_bytes;
if (!free_bytes) {
ALOGVV("%s: waiting on cond, bytes=%d", __func__, bytes);
/* wait till space is avaialable to write */
pthread_cond_wait(&p_pcm_ses->lab_out_buf_cond, &p_pcm_ses->lab_out_buf_lock);
if (p_pcm_ses->exit_lab_processing) {
ALOGVV("%s: buffering stopped while waiting on cond, exiting", __func__);
pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
return -EIO;
}
}
write_bytes = MIN(bytes, free_bytes);
if (p_pcm_ses->lab_out_buf_end_ptr < (p_pcm_ses->lab_out_buf_wr_ptr + write_bytes)) {
copy_bytes = p_pcm_ses->lab_out_buf_end_ptr - p_pcm_ses->lab_out_buf_wr_ptr;
memcpy(p_pcm_ses->lab_out_buf_wr_ptr, buf, copy_bytes);
memcpy(p_pcm_ses->lab_out_buf_start_ptr,
buf + copy_bytes, write_bytes - copy_bytes);
updated_wr_ptr = p_pcm_ses->lab_out_buf_start_ptr + (write_bytes - copy_bytes);
p_pcm_ses->lab_out_buf_wr_ptr = updated_wr_ptr;
p_pcm_ses->lab_out_buf_unread_bytes += write_bytes;
} else {
memcpy(p_pcm_ses->lab_out_buf_wr_ptr, buf, write_bytes);
p_pcm_ses->lab_out_buf_wr_ptr += write_bytes;
p_pcm_ses->lab_out_buf_unread_bytes += write_bytes;
}
ALOGVV("%s: about to signal condition", __func__);
pthread_cond_signal(&p_pcm_ses->lab_out_buf_cond);
bytes -= write_bytes;
buf += write_bytes;
}
pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
return 0;
}
static void process_lab_capture(st_hw_session_t *p_ses)
{
int bytes = 0, status = 0;
SET_STATE(p_ses->state, SES_BUFFERING);
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
pthread_mutex_lock(&p_pcm_ses->lab_out_buf_lock);
p_pcm_ses->exit_lab_processing = false;
pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
if (p_ses->stdev->enable_debug_dumps) {
ST_DBG_DECLARE(FILE *fptr_lab = NULL; static int file_cnt = 0);
ST_DBG_FILE_OPEN_WR(fptr_lab, ST_DEBUG_DUMP_LOCATION,
"st_lab_capture_data", "pcm", file_cnt++);
}
/* Initialize pcm output buffer pointers */
p_pcm_ses->lab_out_buf_start_ptr = p_pcm_ses->lab_out_buf;
p_pcm_ses->lab_out_buf_end_ptr = p_pcm_ses->lab_out_buf + p_pcm_ses->lab_out_buf_size;
p_pcm_ses->lab_out_buf_wr_ptr = p_pcm_ses->lab_out_buf;
p_pcm_ses->lab_out_buf_rd_ptr = p_pcm_ses->lab_out_buf;
p_pcm_ses->lab_out_buf_unread_bytes = 0;
while (!p_pcm_ses->exit_lab_processing) {
ALOGVV("%s: ffv_read reading bytes=%d", __func__, p_pcm_ses->lab_cap_buf_size);
bytes = ffv_read_fn(p_pcm_ses->handle, (int16_t *)p_pcm_ses->lab_cap_buf,
p_pcm_ses->lab_cap_buf_size);
ALOGVV("%s: ffv_read done", __func__);
if (!bytes || (bytes == -1)) {
ALOGE("%s: ERROR. ffv_read returned %d", __func__, bytes);
p_pcm_ses->exit_lab_processing = true;
p_pcm_ses->common.fptrs->stop_buffering(&p_pcm_ses->common, true);
break;
}
if (p_ses->stdev->enable_debug_dumps)
ST_DBG_FILE_WRITE(fptr_lab, p_pcm_ses->lab_cap_buf, bytes);
status = write_pcm_data(p_pcm_ses, p_pcm_ses->lab_cap_buf, p_pcm_ses->lab_cap_buf_size);
if (status)
break;
}
if (p_ses->stdev->enable_debug_dumps)
ST_DBG_FILE_CLOSE(fptr_lab);
ALOGVV("%s: Exit status=%d", __func__, status);
return;
}
static int read_pcm(st_hw_session_t *p_ses,
unsigned char *buf,
unsigned int bytes)
{
int status = 0;
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
pthread_mutex_lock(&p_pcm_ses->lab_out_buf_lock);
if (p_pcm_ses->exit_lab_processing) {
ALOGE("%s: No active buffering", __func__);
pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
return -EIO;
}
pthread_mutex_unlock(&p_pcm_ses->lab_out_buf_lock);
status = read_pcm_data(p_pcm_ses, buf, bytes);
return status;
}
static int set_device(st_hw_session_t *p_ses,
bool enable)
{
return sound_trigger_set_device(p_ses, enable);
}
static int disable_device(st_hw_session_t *p_ses __unused,
bool setting_device __unused)
{
ALOGV("%s: Unsupported", __func__);
return 0;
}
static int enable_device(st_hw_session_t *p_ses __unused,
bool setting_device __unused)
{
ALOGV("%s: Unsupported", __func__);
return 0;
}
static int get_param_data(st_hw_session_t *p_ses, const char *param,
void *payload, size_t payload_size,
size_t *param_data_size)
{
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
int ret = 0;
st_get_param_payload_t *params_buffer_ptr;
int param_id, buffer_size;
int param_size = 0;
FfvStatusType status_type;
EspStatusType esp_status_type;
if (!param || !payload || !param_data_size) {
ALOGE("%s: Invalid params", __func__);
return -EINVAL;
}
buffer_size = sizeof(st_get_param_payload_t);
params_buffer_ptr = (st_get_param_payload_t *)calloc(1, buffer_size);
if (!params_buffer_ptr) {
ALOGE("%s: ERROR. Can not allocate buffer size %d", __func__, buffer_size);
goto exit;
}
ALOGD("%s:[%d] Enter param %s", __func__, p_pcm_ses->common.sm_handle, param);
if (!strncmp(param, QSTHW_PARAMETER_DIRECTION_OF_ARRIVAL,
sizeof(QSTHW_PARAMETER_DIRECTION_OF_ARRIVAL))) {
param_id = FFV_SRC_TRACKING_PARAM;
status_type = ffv_get_param_fn(p_pcm_ses->handle, (char *)params_buffer_ptr,
param_id, buffer_size, &param_size);
if (status_type) {
ALOGE("%s: ERROR. ffv_get_param_fn ret %d", __func__, status_type);
ret = -EINVAL;
goto exit;
}
if (param_size != sizeof(ffv_src_tracking_param_t)) {
ALOGE("%s: ERROR. Invalid param size returned %d", __func__, param_size);
ret = -EINVAL;
goto exit;
}
if (payload_size < (size_t) param_size) {
ALOGE("%s: ERROR. Invalid payload size %zu", __func__, payload_size);
ret = -EINVAL;
goto exit;
}
} else if (!strncmp(param, QSTHW_PARAMETER_CHANNEL_INDEX,
sizeof(QSTHW_PARAMETER_CHANNEL_INDEX))) {
param_id = FFV_TARGET_CHANNEL_INDEX_PARAM;
status_type = ffv_get_param_fn(p_pcm_ses->handle, (char *)params_buffer_ptr,
param_id, buffer_size, &param_size);
if (status_type) {
ALOGE("%s: ERROR. ffv_get_param_fn ret %d", __func__, status_type);
goto exit;
}
if (param_size != sizeof(ffv_target_channel_index_param_t)) {
ALOGE("%s: ERROR. Invalid param size returned %d", __func__, param_size);
ret = -EINVAL;
goto exit;
}
if (payload_size < (size_t) param_size) {
ALOGE("%s: ERROR. Invalid payload size %zu", __func__, payload_size);
ret = -EINVAL;
goto exit;
}
} else if (!strncmp(param, QSTHW_PARAMETER_ESP_ENERGY_LEVELS,
sizeof(QSTHW_PARAMETER_ESP_ENERGY_LEVELS))) {
if (!p_pcm_ses->esp_handle) {
ALOGE("%s: ERROR. ESP not enabled for session", __func__);
ret = -EINVAL;
goto exit;
}
param_id = ESP_ENERGY_LEVELS_PARAM;
esp_status_type = esp_get_param_fn(p_pcm_ses->esp_handle,
(char *)params_buffer_ptr, param_id, buffer_size, &param_size);
if (esp_status_type) {
ALOGE("%s: ERROR. esp_get_param_fn ret %d", __func__, esp_status_type);
goto exit;
}
if (param_size != sizeof(esp_energy_levels_t)) {
ALOGE("%s: ERROR. Invalid param size returned %d", __func__, param_size);
ret = -EINVAL;
goto exit;
}
if (payload_size < (size_t) param_size) {
ALOGE("%s: ERROR. Invalid payload size %zu", __func__, payload_size);
ret = -EINVAL;
goto exit;
}
} else {
ALOGE("%s: ERROR. Unsupported param %s", __func__, param);
ret = -EINVAL;
goto exit;
}
/* copy params retrieved to payload */
memcpy(payload, params_buffer_ptr, param_size);
*param_data_size = param_size;
exit:
return ret;
}
static int send_detection_request(st_hw_session_t *p_ses __unused)
{
ALOGV("%s: Unsupported", __func__);
return -ENOSYS;
}
int st_hw_sess_pcm_init(st_hw_session_t *const p_ses,
hw_ses_event_callback_t cb, void* cookie, st_exec_mode_t exec_mode,
struct st_vendor_info* v_info, sound_model_handle_t sm_handle,
sound_trigger_device_t *stdev)
{
int status = 0;
pthread_condattr_t attr;
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
p_ses->exec_mode = exec_mode;
p_ses->fptrs = &pcm_fptrs;
p_ses->callback_to_st_session = cb;
p_ses->cookie = cookie;
p_ses->vendor_uuid_info = v_info;
p_ses->sm_handle = sm_handle;
p_ses->stdev = stdev;
list_init(&p_pcm_ses->ev_list);
pthread_cond_init(&p_pcm_ses->ev_cond, (const pthread_condattr_t *) NULL);
pthread_cond_init(&p_pcm_ses->st_ffv_process_cond, (const pthread_condattr_t *) NULL);
pthread_cond_init(&p_pcm_ses->st_ffv_capture_cond, (const pthread_condattr_t *) NULL);
pthread_mutex_init(&p_pcm_ses->callback_thread_lock, NULL);
pthread_mutex_init(&p_pcm_ses->capture_thread_lock, NULL);
pthread_mutex_init(&p_pcm_ses->st_ffv_process_lock, NULL);
pthread_mutex_init(&p_pcm_ses->lab_out_buf_lock,
(const pthread_mutexattr_t *) NULL);
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&p_pcm_ses->lab_out_buf_cond, &attr);
return status;
}
void st_hw_sess_pcm_deinit(st_hw_session_t *const p_ses __unused)
{
st_hw_session_pcm_t *p_pcm_ses = (st_hw_session_pcm_t *)p_ses;
pthread_cond_destroy(&p_pcm_ses->ev_cond);
pthread_cond_destroy(&p_pcm_ses->st_ffv_process_cond);
pthread_cond_destroy(&p_pcm_ses->st_ffv_capture_cond);
pthread_mutex_destroy(&p_pcm_ses->callback_thread_lock);
pthread_mutex_destroy(&p_pcm_ses->capture_thread_lock);
pthread_mutex_destroy(&p_pcm_ses->st_ffv_process_lock);
pthread_cond_destroy(&p_pcm_ses->lab_out_buf_cond);
pthread_mutex_destroy(&p_pcm_ses->lab_out_buf_lock);
}
int st_hw_pcm_init()
{
int status = 0;
char lib_path[MIXER_PATH_MAX_LENGTH];
char ffv_lib_file[MIXER_PATH_MAX_LENGTH];
char esp_lib_file[MIXER_PATH_MAX_LENGTH];
/* Get path for libs */
get_lib_path(lib_path, sizeof(lib_path));
/* Get path for ffv_lib_file */
snprintf(ffv_lib_file, sizeof(ffv_lib_file), "%s/%s", lib_path, FFV_LIB_NAME);
/* load FFV + SVA library */
pcm_data.ffv_lib_handle = dlopen(ffv_lib_file, RTLD_NOW);
if (!pcm_data.ffv_lib_handle) {
ALOGE("%s: Unable to open %s, error %s", __func__, ffv_lib_file,
dlerror());
status = -ENOENT;
goto exit;
}
DLSYM(pcm_data.ffv_lib_handle, ffv_init_fn, ffv_init, status);
if (status)
goto exit;
DLSYM(pcm_data.ffv_lib_handle, ffv_deinit_fn, ffv_deinit, status);
if (status)
goto exit;
DLSYM(pcm_data.ffv_lib_handle, ffv_process_v2_fn, ffv_process_v2, status);
if (status) {
DLSYM(pcm_data.ffv_lib_handle, ffv_process_fn, ffv_process, status);
if (status)
goto exit;
}
DLSYM(pcm_data.ffv_lib_handle, ffv_read_fn, ffv_read, status);
if (status)
goto exit;
DLSYM(pcm_data.ffv_lib_handle, ffv_get_param_fn, ffv_get_param, status);
if (status)
goto exit;
DLSYM(pcm_data.ffv_lib_handle, ffv_set_param_fn, ffv_set_param, status);
if (status)
goto exit;
DLSYM(pcm_data.ffv_lib_handle, ffv_register_event_callback_fn,
ffv_register_event_callback, status);
if (status)
goto exit;
/* Get path for esp_lib_file */
snprintf(esp_lib_file, sizeof(esp_lib_file), "%s/%s", lib_path, ESP_LIB_NAME);
/* load ESP library */
pcm_data.esp_lib_handle = dlopen(esp_lib_file, RTLD_NOW);
if (!pcm_data.esp_lib_handle) {
ALOGE("%s: Unable to open %s, error %s", __func__, esp_lib_file,
dlerror());
status = -ENOENT;
goto exit_1;
}
dlerror(); /* clear errors */
DLSYM(pcm_data.esp_lib_handle, esp_init_fn, esp_init, status);
if (status)
goto exit_1;
DLSYM(pcm_data.esp_lib_handle, esp_deinit_fn, esp_deinit, status);
if (status)
goto exit_1;
DLSYM(pcm_data.esp_lib_handle, esp_process_fn, esp_process, status);
if (status)
goto exit_1;
DLSYM(pcm_data.esp_lib_handle, esp_get_param_fn, esp_get_param, status);
if (status)
goto exit_1;
return 0;
exit_1:
/*
* Do not treat esp failures as init error.
* Instead dlclose esp lib handle and return success.
*/
if (pcm_data.esp_lib_handle) {
dlclose(pcm_data.esp_lib_handle);
pcm_data.esp_lib_handle = NULL;
}
return 0;
exit:
if (pcm_data.ffv_lib_handle)
dlclose(pcm_data.ffv_lib_handle);
pcm_data.ffv_lib_handle = NULL;
return status;
}
void st_hw_pcm_deinit()
{
if (pcm_data.ffv_lib_handle)
dlclose(pcm_data.ffv_lib_handle);
pcm_data.ffv_lib_handle = NULL;
if (pcm_data.esp_lib_handle)
dlclose(pcm_data.esp_lib_handle);
pcm_data.esp_lib_handle = NULL;
}