blob: d6351ca7707d4a66a405d99cab74cd30222a46aa [file] [log] [blame]
/* st_hw_extn.c
* This file contains extended functionality of
* sound_trigger_hw.
*
* Copyright (c) 2016-2019, 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_extn"
/* #define LOG_NDEBUG 0 */
#define LOG_NDDEBUG 0
/* #define VERY_VERBOSE_LOGGING */
#ifdef VERY_VERBOSE_LOGGING
#define ALOGVV ALOGV
#else
#define ALOGVV(a...) do { } while(0)
#endif
#include <errno.h>
#include <stdlib.h>
#include <cutils/log.h>
#include <inttypes.h>
#include "st_session.h"
#include "st_hw_defs.h"
static int generate_sound_trigger_phrase_recognition_event_v3
(
const struct sound_trigger_phrase_sound_model *phrase_sm,
const struct sound_trigger_recognition_config *rc_config,
const void *payload,
unsigned int payload_size,
qsthw_recognition_event_type_t event_type,
void **out_rc_event
)
{
unsigned int i = 0, j = 0, user_id = 0;
int rc = 0;
ALOGD("%s: Enter payload_size %d", __func__, payload_size);
if (!payload || !phrase_sm || !rc_config || !out_rc_event) {
ALOGE("%s: Null params", __func__);
return -EINVAL;
}
*out_rc_event = NULL;
switch (event_type) {
case QSTHW_RC_EVENT_TYPE_TIMESTAMP: {
struct qsthw_phrase_recognition_event *event;
struct sound_trigger_phrase_recognition_event *phrase_event;
event = calloc(1, sizeof(*event) + payload_size);
if (!event) {
ALOGE("%s: event allocation failed size %d",
__func__, payload_size);
rc = -ENODEV;
break;
}
phrase_event = &event->phrase_event;
phrase_event->num_phrases = rc_config->num_phrases;
phrase_event->common.data_offset = sizeof(*event);
phrase_event->common.data_size = payload_size;
/* fill confidence levels */
for (i = 0; i < rc_config->num_phrases; i++) {
phrase_event->phrase_extras[i].id = rc_config->phrases[i].id;
phrase_event->phrase_extras[i].recognition_modes =
phrase_sm->phrases[0].recognition_mode;
phrase_event->phrase_extras[i].confidence_level =
((char *)payload)[i];
phrase_event->phrase_extras[i].num_levels =
rc_config->phrases[i].num_levels;
for (j = 0; j < rc_config->phrases[i].num_levels; j++) {
user_id = rc_config->phrases[i].levels[j].user_id;
phrase_event->phrase_extras[i].levels[j].user_id = user_id;
phrase_event->phrase_extras[i].levels[j].level =
((char *)payload)[user_id];
}
}
/* Copy payload to event using offset generated above */
memcpy((char *)event + phrase_event->common.data_offset,
payload, payload_size);
*out_rc_event = (struct qsthw_phrase_recognition_event *)event;
break;
}
default:
ALOGE("%s: Invalid event type passed %d", __func__, event_type);
rc = -EINVAL;
break;
}
return rc;
}
bool sthw_extn_check_process_det_ev_support()
{
return true;
}
/* recognition event with timestamp */
int sthw_extn_process_detection_event_keyphrase(
st_proxy_session_t *st_ses, uint64_t timestamp, int detect_status,
void *payload, size_t payload_size,
struct sound_trigger_phrase_recognition_event **event)
{
st_hw_session_t *st_hw_ses = st_ses->hw_ses_current;
st_session_t *stc_ses = st_ses->det_stc_ses;
struct qsthw_phrase_recognition_event *local_event = NULL;
struct st_vendor_info *v_info = st_ses->vendor_uuid_info;
struct sound_trigger_phrase_sound_model *phrase_sm = stc_ses->phrase_sm;
int status = 0;
unsigned int i, j;
qsthw_recognition_event_type_t event_type = QSTHW_RC_EVENT_TYPE_TIMESTAMP;
if (st_hw_ses->is_generic_event) {
/*
* For generic event, reusing the st_session v2 event generation.
* Note: free memory allocated from v2 event generation after forming local event.
*/
struct sound_trigger_phrase_recognition_event *generic_phrase_event = NULL;
status = process_detection_event_keyphrase_v2(st_ses, detect_status, payload,
payload_size, &generic_phrase_event);
if (status) {
ALOGE("%s: process_detection_event_keyphrase_v2 failed, size %zd", __func__,
payload_size);
goto exit;
}
local_event = calloc(1, sizeof(*local_event) + generic_phrase_event->common.data_size);
if (!local_event) {
ALOGE("%s: event allocation failed, size %u", __func__,
generic_phrase_event->common.data_size);
status = -ENOMEM;
free(generic_phrase_event);
goto exit;
}
memcpy(&local_event->phrase_event, generic_phrase_event,
sizeof(*generic_phrase_event));
local_event->phrase_event.common.data_offset = sizeof(*local_event);
memcpy((char *)local_event + local_event->phrase_event.common.data_offset,
(char *)generic_phrase_event + generic_phrase_event->common.data_offset,
generic_phrase_event->common.data_size);
free(generic_phrase_event);
*event = &local_event->phrase_event;
goto exit;
} else if (v_info->is_qcva_uuid) {
/* if smlib is present, get the event from it, else send the
* DSP received payload as it is to App
*/
status = generate_sound_trigger_phrase_recognition_event_v3(
phrase_sm, stc_ses->rc_config, payload, payload_size,
event_type, (void *)&local_event);
if (status || !local_event) {
ALOGW("%s: smlib fill recognition event failed, status %d",
__func__, status);
goto exit;
}
} else {
local_event = calloc(1, sizeof(*local_event) + payload_size);
if (!local_event) {
ALOGE("%s: event allocation failed, size %zd", __func__,
payload_size);
status = -ENOMEM;
goto exit;
}
memcpy(local_event->phrase_event.phrase_extras,
stc_ses->rc_config->phrases,
stc_ses->rc_config->num_phrases *
sizeof(struct sound_trigger_phrase_recognition_extra));
local_event->phrase_event.num_phrases = stc_ses->rc_config->num_phrases;
local_event->phrase_event.common.data_offset = sizeof(*local_event);
local_event->phrase_event.common.data_size = payload_size;
memcpy((char *)local_event + local_event->phrase_event.common.data_offset,
payload, payload_size);
}
/* fill the remaining recognition event parameters not specific
* to soundmodel lib
*/
local_event->timestamp = timestamp;
local_event->phrase_event.common.status = detect_status;
local_event->phrase_event.common.type = phrase_sm->common.type;;
local_event->phrase_event.common.model = stc_ses->sm_handle;
local_event->phrase_event.common.capture_available = stc_ses->capture_requested;
local_event->phrase_event.common.capture_delay_ms = 0;
local_event->phrase_event.common.capture_preamble_ms = 0;
local_event->phrase_event.common.audio_config.sample_rate =
SOUND_TRIGGER_SAMPLING_RATE_16000;
local_event->phrase_event.common.audio_config.channel_mask =
AUDIO_CHANNEL_IN_MONO;
local_event->phrase_event.common.audio_config.format =
AUDIO_FORMAT_PCM_16_BIT;
for (i = 0; i < local_event->phrase_event.num_phrases; i++) {
ALOGVV("%s: [%d] kw_id %d level %d", __func__,i,
local_event->phrase_event.phrase_extras[i].id,
local_event->phrase_event.phrase_extras[i].confidence_level);
for (j = 0; j < local_event->phrase_event.phrase_extras[i].num_levels; j++) {
ALOGVV("%s: [%d] user_id %d level %d ", __func__, i,
local_event->phrase_event.phrase_extras[i].levels[j].user_id,
local_event->phrase_event.phrase_extras[i].levels[j].level);
}
}
ALOGI("%s:[c%d] send keyphrase recognition event %d", __func__,
stc_ses->sm_handle, detect_status);
ALOGV("%s:[c%d] status=%d, type=%d, model=%d, capture_avaiable=%d, "
"num_phrases=%d, id=%d, timestamp = %" PRIu64,
__func__, stc_ses->sm_handle, local_event->phrase_event.common.status,
local_event->phrase_event.common.type, local_event->phrase_event.common.model,
local_event->phrase_event.common.capture_available, local_event->phrase_event.num_phrases,
local_event->phrase_event.phrase_extras[0].id, local_event->timestamp);
*event = &local_event->phrase_event;
exit:
return status;
}