Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fa6b191 authored by Ravi Kumar Alamanda's avatar Ravi Kumar Alamanda
Browse files

hal: support for sound trigger LAB and concurrency

Sound trigger lookahead buffering data is read through audio hal.
Also the audio hal has to inform sound trigger hal about concurrencies
and subsytsem restart events. Audio Hal uses proprietary interface
defined by sound trigger for communication with sound trigger hal.

Change-Id: Id344077b40b481a953769518f8a6ac0adc13b709
parent 6d121278
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -171,6 +171,13 @@ ifeq ($(strip $(AUDIO_FEATURE_ENABLED_LISTEN)),true)
    LOCAL_SRC_FILES += audio_extn/listen.c
endif

ifeq ($(strip $(BOARD_SUPPORTS_SOUND_TRIGGER)),true)
    LOCAL_CFLAGS += -DSOUND_TRIGGER_ENABLED
    LOCAL_CFLAGS += -DSOUND_TRIGGER_PLATFORM_NAME=$(TARGET_BOARD_PLATFORM)
    LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/sound_trigger
    LOCAL_SRC_FILES += audio_extn/soundtrigger.c
endif

ifeq ($(strip $(AUDIO_FEATURE_ENABLED_AUXPCM_BT)),true)
    LOCAL_CFLAGS += -DAUXPCM_BT_ENABLED
endif
+1 −0
Original line number Diff line number Diff line
@@ -457,6 +457,7 @@ void audio_extn_set_parameters(struct audio_device *adev,
   audio_extn_set_fluence_parameters(adev, parms);
   audio_extn_set_afe_proxy_parameters(adev, parms);
   audio_extn_fm_set_parameters(adev, parms);
   audio_extn_sound_trigger_set_parameters(adev, parms);
   audio_extn_listen_set_parameters(adev, parms);
   audio_extn_hfp_set_parameters(adev, parms);
   audio_extn_ddp_set_parameters(adev, parms);
+30 −0
Original line number Diff line number Diff line
@@ -177,6 +177,36 @@ void audio_extn_listen_set_parameters(struct audio_device *adev,
                                      struct str_parms *parms);
#endif /* AUDIO_LISTEN_ENABLED */

#ifndef SOUND_TRIGGER_ENABLED
#define audio_extn_sound_trigger_init(adev)                            (0)
#define audio_extn_sound_trigger_deinit(adev)                          (0)
#define audio_extn_sound_trigger_update_device_status(snd_dev, event)  (0)
#define audio_extn_sound_trigger_update_stream_status(uc_info, event)  (0)
#define audio_extn_sound_trigger_set_parameters(adev, parms)           (0)
#define audio_extn_sound_trigger_check_and_get_session(in)             (0)
#define audio_extn_sound_trigger_stop_lab(in)                          (0)
#else

enum st_event_type {
    ST_EVENT_SND_DEVICE_FREE,
    ST_EVENT_SND_DEVICE_BUSY,
    ST_EVENT_STREAM_FREE,
    ST_EVENT_STREAM_BUSY
};
typedef enum st_event_type st_event_type_t;

int audio_extn_sound_trigger_init(struct audio_device *adev);
void audio_extn_sound_trigger_deinit(struct audio_device *adev);
void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
                                     st_event_type_t event);
void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
                                     st_event_type_t event);
void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
                                             struct str_parms *parms);
void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
void audio_extn_sound_trigger_stop_lab(struct stream_in *in);
#endif

#ifndef AUXPCM_BT_ENABLED
#define audio_extn_read_xml(adev, mixer_card, MIXER_XML_PATH, \
                            MIXER_XML_PATH_AUXPCM)               (-ENOSYS)
+352 −0
Original line number Diff line number Diff line
/* Copyright (c) 2013-2014, 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 "soundtrigger"
/* #define LOG_NDEBUG 0 */
#define LOG_NDDEBUG 0

#include <stdbool.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <cutils/log.h>
#include "audio_hw.h"
#include "audio_extn.h"
#include "platform.h"
#include "platform_api.h"
#include "sound_trigger_prop_intf.h"

#define XSTR(x) STR(x)
#define STR(x) #x

struct sound_trigger_info  {
    struct sound_trigger_session_info st_ses;
    bool lab_stopped;
    struct listnode list;
};

struct sound_trigger_audio_device {
    void *lib_handle;
    struct audio_device *adev;
    sound_trigger_hw_call_back_t st_callback;
    struct listnode st_ses_list;
    pthread_mutex_t lock;
};

static struct sound_trigger_audio_device *st_dev;

static struct sound_trigger_info *
get_sound_trigger_info(int capture_handle)
{
    struct sound_trigger_info  *st_ses_info = NULL;
    struct listnode *node;
    ALOGD("%s: list %d capture_handle %d", __func__,
           list_empty(&st_dev->st_ses_list), capture_handle);
    list_for_each(node, &st_dev->st_ses_list) {
        st_ses_info = node_to_item(node, struct sound_trigger_info , list);
        if (st_ses_info->st_ses.capture_handle == capture_handle)
            return st_ses_info;
    }
    return NULL;
}

int audio_hw_call_back(sound_trigger_event_type_t event,
                       sound_trigger_event_info_t* config)
{
    int status = 0;
    struct sound_trigger_info  *st_ses_info;

    if (!st_dev)
       return -EINVAL;

    pthread_mutex_lock(&st_dev->lock);
    switch (event) {
    case ST_EVENT_SESSION_REGISTER:
        if (!config) {
            ALOGE("%s: NULL config", __func__);
            status = -EINVAL;
            break;
        }
        st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
        if (!st_ses_info) {
            ALOGE("%s: st_ses_info alloc failed", __func__);
            status = -ENOMEM;
            break;
        }
        memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
        ALOGV("%s: add capture_handle %d pcm %p", __func__,
              st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
        list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
        break;

    case ST_EVENT_SESSION_DEREGISTER:
        if (!config) {
            ALOGE("%s: NULL config", __func__);
            status = -EINVAL;
            break;
        }
        st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
        if (!st_ses_info) {
            ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
            status = -EINVAL;
            break;
        }
        ALOGV("%s: remove capture_handle %d pcm %p", __func__,
              st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
        list_remove(&st_ses_info->list);
        free(st_ses_info);
        break;
    default:
        ALOGW("%s: Unknown event %d", __func__, event);
        break;
    }
    pthread_mutex_unlock(&st_dev->lock);
    return status;
}

void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
{
    int status = 0;
    struct sound_trigger_info  *st_ses_info = NULL;
    audio_event_info_t event;

    if (!st_dev || !in)
       return;

    pthread_mutex_lock(&st_dev->lock);
    st_ses_info = get_sound_trigger_info(in->capture_handle);
    if (st_ses_info) {
        event.u.ses_info = st_ses_info->st_ses;
        ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
        st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
    }
    pthread_mutex_unlock(&st_dev->lock);
}
void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
{
    struct sound_trigger_info  *st_ses_info = NULL;
    struct listnode *node;

    if (!st_dev || !in)
       return;

    pthread_mutex_lock(&st_dev->lock);
    in->is_st_session = false;
    ALOGV("%s: list %d capture_handle %d", __func__,
          list_empty(&st_dev->st_ses_list), in->capture_handle);
    list_for_each(node, &st_dev->st_ses_list) {
        st_ses_info = node_to_item(node, struct sound_trigger_info , list);
        if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
            in->pcm = st_ses_info->st_ses.pcm;
            in->config = st_ses_info->st_ses.config;
            in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
            in->is_st_session = true;
            ALOGD("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
            break;
        }
    }
    pthread_mutex_unlock(&st_dev->lock);
}

void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
                                     st_event_type_t event)
{
    bool raise_event = false;
    int device_type = -1;

    if (!st_dev)
       return;

    if (snd_device >= SND_DEVICE_OUT_BEGIN &&
        snd_device < SND_DEVICE_OUT_END)
        device_type = PCM_PLAYBACK;
    else if (snd_device >= SND_DEVICE_IN_BEGIN &&
        snd_device < SND_DEVICE_IN_END)
        device_type = PCM_CAPTURE;
    else {
        ALOGE("%s: invalid device 0x%x, for event %d",
                           __func__, snd_device, event);
        return;
    }

    raise_event = platform_sound_trigger_device_needs_event(snd_device);
    ALOGI("%s: device 0x%x of type %d for Event %d, with Raise=%d",
        __func__, snd_device, device_type, event, raise_event);
    if (raise_event && (device_type == PCM_CAPTURE)) {
        switch(event) {
        case ST_EVENT_SND_DEVICE_FREE:
            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
            break;
        case ST_EVENT_SND_DEVICE_BUSY:
            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
            break;
        default:
            ALOGW("%s:invalid event %d for device 0x%x",
                                  __func__, event, snd_device);
        }
    }/*Events for output device, if required can be placed here in else*/
}

void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
                                     st_event_type_t event)
{
    bool raise_event = false;
    audio_usecase_t uc_id;
    int usecase_type = -1;

    if (!st_dev) {
        return;
    }

    if (uc_info == NULL) {
        ALOGE("%s: usecase is NULL!!!", __func__);
        return;
    }
    uc_id = uc_info->id;
    usecase_type = uc_info->type;

    raise_event = platform_sound_trigger_usecase_needs_event(uc_id);
    ALOGD("%s: uc_id %d of type %d for Event %d, with Raise=%d",
        __func__, uc_id, usecase_type, event, raise_event);
    if (raise_event && (usecase_type == PCM_PLAYBACK)) {
        switch(event) {
        case ST_EVENT_STREAM_FREE:
            st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE, NULL);
            break;
        case ST_EVENT_STREAM_BUSY:
            st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE, NULL);
            break;
        default:
            ALOGW("%s:invalid event %d, for usecase %d",
                                  __func__, event, uc_id);
        }
    }/*Events for capture usecase, if required can be placed here in else*/
}

void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
                               struct str_parms *params)
{
    audio_event_info_t event;
    char value[32];
    int ret;

    if(!st_dev || !params) {
        ALOGE("%s: str_params NULL", __func__);
        return;
    }

    ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
                            sizeof(value));
    if (ret > 0) {
        if (strstr(value, "OFFLINE")) {
            event.u.status = SND_CARD_STATUS_OFFLINE;
            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
        }
        else if (strstr(value, "ONLINE")) {
            event.u.status = SND_CARD_STATUS_ONLINE;
            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
        }
        else
            ALOGE("%s: unknown snd_card_status", __func__);
    }

    ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
    if (ret > 0) {
        if (strstr(value, "OFFLINE")) {
            event.u.status = CPE_STATUS_OFFLINE;
            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
        }
        else if (strstr(value, "ONLINE")) {
            event.u.status = CPE_STATUS_ONLINE;
            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
        }
        else
            ALOGE("%s: unknown CPE status", __func__);
    }
}

int audio_extn_sound_trigger_init(struct audio_device *adev)
{
    int status = 0;
    char sound_trigger_lib[100];
    void *lib_handle;

    ALOGI("%s: Enter", __func__);

    st_dev = (struct sound_trigger_audio_device*)
                        calloc(1, sizeof(struct sound_trigger_audio_device));
    if (!st_dev) {
        ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
        return -ENOMEM;
    }

    snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
             "/system/vendor/lib/hw/sound_trigger.primary.%s.so",
              XSTR(SOUND_TRIGGER_PLATFORM_NAME));

    st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);

    if (st_dev->lib_handle == NULL) {
        ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
                dlerror());
        status = -EINVAL;
        goto cleanup;
    }
    ALOGI("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);

    st_dev->st_callback = (sound_trigger_hw_call_back_t)
              dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");

    if (st_dev->st_callback == NULL) {
       ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
               dlerror());
       goto cleanup;
    }

    st_dev->adev = adev;
    list_init(&st_dev->st_ses_list);

    return 0;

cleanup:
    if (st_dev->lib_handle)
        dlclose(st_dev->lib_handle);
    free(st_dev);
    st_dev = NULL;
    return status;

}

void audio_extn_sound_trigger_deinit(struct audio_device *adev)
{
    ALOGI("%s: Enter", __func__);
    if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
        dlclose(st_dev->lib_handle);
        free(st_dev);
        st_dev = NULL;
    }
}
+38 −14
Original line number Diff line number Diff line
@@ -288,6 +288,7 @@ int enable_audio_route(struct audio_device *adev,
    audio_extn_dolby_set_dmid(adev);
    audio_extn_dolby_set_endpoint(adev);
#endif
    audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_BUSY);
    audio_extn_listen_update_stream_status(usecase, LISTEN_EVENT_STREAM_BUSY);
    audio_extn_utils_send_audio_calibration(adev, usecase);
    audio_extn_utils_send_app_type_cfg(usecase);
@@ -317,6 +318,7 @@ int disable_audio_route(struct audio_device *adev,
    platform_add_backend_name(mixer_path, snd_device);
    ALOGV("%s: reset and update mixer path: %s", __func__, mixer_path);
    audio_route_reset_and_update_path(adev->audio_route, mixer_path);
    audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_FREE);
    audio_extn_listen_update_stream_status(usecase, LISTEN_EVENT_STREAM_FREE);
    ALOGV("%s: exit", __func__);
    return 0;
@@ -372,10 +374,14 @@ int enable_snd_device(struct audio_device *adev,
        snd_device, device_name);
        /* due to the possibility of calibration overwrite between listen
            and audio, notify listen hal before audio calibration is sent */
        audio_extn_sound_trigger_update_device_status(snd_device,
                                        ST_EVENT_SND_DEVICE_BUSY);
        audio_extn_listen_update_device_status(snd_device,
                                        LISTEN_EVENT_SND_DEVICE_BUSY);
        if (platform_get_snd_device_acdb_id(snd_device) < 0) {
            adev->snd_dev_ref_cnt[snd_device]--;
            audio_extn_sound_trigger_update_device_status(snd_device,
                                            ST_EVENT_SND_DEVICE_FREE);
            audio_extn_listen_update_device_status(snd_device,
                                        LISTEN_EVENT_SND_DEVICE_FREE);
            return -EINVAL;
@@ -428,6 +434,9 @@ int disable_snd_device(struct audio_device *adev,
            audio_route_reset_and_update_path(adev->audio_route, device_name);
            audio_extn_dev_arbi_release(snd_device);
        }

        audio_extn_sound_trigger_update_device_status(snd_device,
                                        ST_EVENT_SND_DEVICE_FREE);
        audio_extn_listen_update_device_status(snd_device,
                                        LISTEN_EVENT_SND_DEVICE_FREE);
    }
@@ -2101,7 +2110,6 @@ static int in_standby(struct audio_stream *stream)
    ALOGD("%s: enter: stream (%p) usecase(%d: %s)", __func__,
          stream, in->usecase, use_case_table[in->usecase]);


    if (in->usecase == USECASE_COMPRESS_VOIP_CALL) {
        /* Ignore standby in case of voip call because the voip input
         * stream is closed in adev_close_input_stream()
@@ -2111,6 +2119,12 @@ static int in_standby(struct audio_stream *stream)
    }

    pthread_mutex_lock(&in->lock);
    if (!in->standby && in->is_st_session) {
        ALOGD("%s: sound trigger pcm stop lab", __func__);
        audio_extn_sound_trigger_stop_lab(in);
        in->standby = 1;
    }

    if (!in->standby) {
        pthread_mutex_lock(&adev->lock);
        in->standby = true;
@@ -2172,7 +2186,7 @@ static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
        if ((in->device != val) && (val != 0)) {
            in->device = val;
            /* If recording is in progress, change the tx device to new device */
            if (!in->standby)
            if (!in->standby && !in->is_st_session)
                ret = select_devices(adev, in->usecase);
        }
    }
@@ -2236,6 +2250,7 @@ static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
    }

    if (in->standby) {
        if (!in->is_st_session) {
            pthread_mutex_lock(&adev->lock);
            if (in->usecase == USECASE_COMPRESS_VOIP_CALL)
                ret = voice_extn_compress_voip_start_input_stream(in);
@@ -2245,6 +2260,7 @@ static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
            if (ret != 0) {
                goto exit;
            }
        }
        in->standby = 0;
    }

@@ -2283,7 +2299,7 @@ exit:
        }
        memset(buffer, 0, bytes);
        in_standby(&in->stream.common);
        ALOGV("%s: read failed - sleeping for buffer duration", __func__);
        ALOGV("%s: read failed status %d- sleeping for buffer duration", __func__, ret);
        usleep(bytes * 1000000 / audio_stream_frame_size(&in->stream.common) /
                                   in_get_sample_rate(&in->stream.common));
    }
@@ -2896,8 +2912,8 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
    }

    ALOGD("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x)\
        stream_handle(%p)",__func__, config->sample_rate, config->channel_mask,
        devices, &in->stream);
        stream_handle(%p) io_handle(%d)",__func__, config->sample_rate, config->channel_mask,
        devices, &in->stream, handle);

    pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);

@@ -2922,6 +2938,7 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
    in->dev = adev;
    in->standby = 1;
    in->channel_mask = config->channel_mask;
    in->capture_handle = handle;

    /* Update config params with the requested sample rate and channels */
    in->usecase = USECASE_AUDIO_RECORD;
@@ -2953,6 +2970,10 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
        in->config.period_size = buffer_size / frame_size;
    }

    /* This stream could be for sound trigger lab,
       get sound trigger pcm if present */
    audio_extn_sound_trigger_check_and_get_session(in);

    *stream_in = &in->stream;
    ALOGV("%s: exit", __func__);
    return ret;
@@ -2985,11 +3006,12 @@ static void adev_close_input_stream(struct audio_hw_device *dev,
    if (audio_extn_ssr_get_enabled() && (popcount(in->channel_mask) == 6)) {
        audio_extn_ssr_deinit();
    }
    free(stream);

    if(audio_extn_compr_cap_enabled() &&
            audio_extn_compr_cap_format_supported(in->config.format))
        audio_extn_compr_cap_deinit();

    free(stream);
    return;
}

@@ -3008,6 +3030,7 @@ static int adev_close(hw_device_t *device)
    pthread_mutex_lock(&adev_init_lock);

    if ((--audio_device_ref_count) == 0) {
        audio_extn_sound_trigger_deinit(adev);
        audio_extn_listen_deinit(adev);
        audio_extn_utils_release_streams_output_cfg_list(&adev->streams_output_cfg_list);
        audio_route_free(adev->audio_route);
@@ -3116,6 +3139,7 @@ static int adev_open(const hw_module_t *module, const char *name,
        }
    }
    audio_extn_listen_init(adev, adev->snd_card);
    audio_extn_sound_trigger_init(adev);

    if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) {
        adev->offload_effects_lib = dlopen(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, RTLD_NOW);
Loading