| /* |
| * Copyright (c) 2017, 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 "audio_adsp_hdlr_event" |
| |
| /*#define LOG_NDEBUG 0*/ |
| /*#define VERY_VERY_VERBOSE_LOGGING*/ |
| #ifdef VERY_VERY_VERBOSE_LOGGING |
| #define ALOGVV ALOGV |
| #else |
| #define ALOGVV(a...) do { } while(0) |
| #endif |
| |
| #include <pthread.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <sys/resource.h> |
| #include <sys/prctl.h> |
| #include <cutils/log.h> |
| #include <cutils/sched_policy.h> |
| #include <system/thread_defs.h> |
| #include <sound/asound.h> |
| #include <linux/msm_audio.h> |
| |
| #include "audio_hw.h" |
| #include "audio_defs.h" |
| #include "platform.h" |
| #include "platform_api.h" |
| #include "adsp_hdlr.h" |
| |
| #define MAX_EVENT_PAYLOAD 512 |
| #define WAIT_EVENT_POLL_TIMEOUT 50 |
| |
| #define MIXER_MAX_BYTE_LENGTH 512 |
| |
| struct adsp_hdlr_stream_data { |
| struct adsp_hdlr_stream_cfg config; |
| stream_callback_t client_callback; |
| void *client_cookie; |
| }; |
| |
| struct adsp_hdlr_event_info { |
| struct listnode list; |
| void *stream_handle; |
| char mixer_ctl_name[MIXER_PATH_MAX_LENGTH]; |
| char cb_mixer_ctl_name[MIXER_PATH_MAX_LENGTH]; |
| adsp_event_callback_t cb; |
| void *cookie; |
| int event_type; |
| }; |
| |
| struct adsp_hdlr_inst { |
| struct listnode event_list; |
| bool binit; |
| struct mixer *mixer; |
| pthread_mutex_t event_list_lock; |
| |
| struct listnode list; |
| pthread_cond_t event_wait_cond; |
| pthread_t event_wait_thread; |
| struct listnode event_wait_cmd_list; |
| pthread_mutex_t event_wait_lock; |
| bool event_wait_thread_active; |
| |
| pthread_cond_t event_callback_cond; |
| pthread_t event_callback_thread; |
| struct listnode event_callback_cmd_list; |
| pthread_mutex_t event_callback_lock; |
| bool event_callback_thread_active; |
| }; |
| |
| enum { |
| EVENT_CMD_EXIT, /* event thread exit command loop*/ |
| EVENT_CMD_WAIT, /* event thread wait on mixer control */ |
| EVENT_CMD_GET /* event thread get param data from mixer */ |
| }; |
| |
| struct event_cmd { |
| struct listnode list; |
| int opcode; |
| char cb_mixer_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; |
| }; |
| |
| static struct adsp_hdlr_inst *adsp_hdlr_inst = NULL; |
| |
| static void *event_wait_thread_loop(void *context); |
| static void *event_callback_thread_loop(void *context); |
| |
| static int send_cmd_event_wait_thread(struct adsp_hdlr_inst *adsp_hdlr_inst, int opcode) |
| { |
| struct event_cmd *cmd = calloc(1, sizeof(*cmd)); |
| |
| if (!cmd) { |
| ALOGE("Failed to allocate mem for command 0x%x", opcode); |
| return -ENOMEM; |
| } |
| |
| ALOGVV("%s %d", __func__, opcode); |
| |
| cmd->opcode = opcode; |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_wait_lock); |
| list_add_tail(&adsp_hdlr_inst->event_wait_cmd_list, &cmd->list); |
| pthread_cond_signal(&adsp_hdlr_inst->event_wait_cond); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_wait_lock); |
| |
| return 0; |
| } |
| |
| static int send_cmd_event_callback_thread(struct adsp_hdlr_inst *adsp_hdlr_inst, |
| int opcode, char *mixer_ctl_name) |
| { |
| struct event_cmd *cmd = calloc(1, sizeof(*cmd)); |
| |
| if (!cmd) { |
| ALOGE("Failed to allocate mem for command 0x%x", opcode); |
| return -ENOMEM; |
| } |
| |
| ALOGVV("%s opcode %d, name = %s", __func__, opcode, mixer_ctl_name); |
| |
| cmd->opcode = opcode; |
| if (mixer_ctl_name) |
| strlcpy(cmd->cb_mixer_ctl_name, mixer_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_callback_lock); |
| list_add_tail(&adsp_hdlr_inst->event_callback_cmd_list, &cmd->list); |
| pthread_cond_signal(&adsp_hdlr_inst->event_callback_cond); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_callback_lock); |
| |
| return 0; |
| } |
| |
| static void create_event_wait_thread(struct adsp_hdlr_inst *adsp_hdlr_inst) |
| { |
| pthread_cond_init(&adsp_hdlr_inst->event_wait_cond, |
| (const pthread_condattr_t *) NULL); |
| list_init(&adsp_hdlr_inst->event_wait_cmd_list); |
| pthread_create(&adsp_hdlr_inst->event_wait_thread, (const pthread_attr_t *) NULL, |
| event_wait_thread_loop, adsp_hdlr_inst); |
| adsp_hdlr_inst->event_wait_thread_active = true; |
| } |
| |
| static void create_event_callback_thread(struct adsp_hdlr_inst *adsp_hdlr_inst) |
| { |
| pthread_cond_init(&adsp_hdlr_inst->event_callback_cond, |
| (const pthread_condattr_t *) NULL); |
| list_init(&adsp_hdlr_inst->event_callback_cmd_list); |
| pthread_create(&adsp_hdlr_inst->event_callback_thread, (const pthread_attr_t *) NULL, |
| event_callback_thread_loop, adsp_hdlr_inst); |
| adsp_hdlr_inst->event_callback_thread_active = true; |
| } |
| |
| static void destroy_event_wait_thread(struct adsp_hdlr_inst *adsp_hdlr_inst) |
| { |
| send_cmd_event_wait_thread(adsp_hdlr_inst, EVENT_CMD_EXIT); |
| pthread_join(adsp_hdlr_inst->event_wait_thread, (void **) NULL); |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_wait_lock); |
| pthread_cond_destroy(&adsp_hdlr_inst->event_wait_cond); |
| adsp_hdlr_inst->event_wait_thread_active = false; |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_wait_lock); |
| } |
| |
| static void destroy_event_callback_thread(struct adsp_hdlr_inst *adsp_hdlr_inst) |
| { |
| send_cmd_event_callback_thread(adsp_hdlr_inst, EVENT_CMD_EXIT, NULL); |
| pthread_join(adsp_hdlr_inst->event_callback_thread, (void **) NULL); |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_callback_lock); |
| pthread_cond_destroy(&adsp_hdlr_inst->event_callback_cond); |
| adsp_hdlr_inst->event_callback_thread_active = false; |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_callback_lock); |
| } |
| |
| static void destroy_event_threads(struct adsp_hdlr_inst *adsp_hdlr_inst) |
| { |
| if (adsp_hdlr_inst->event_wait_thread_active) |
| destroy_event_wait_thread(adsp_hdlr_inst); |
| if (adsp_hdlr_inst->event_callback_thread_active) |
| destroy_event_callback_thread(adsp_hdlr_inst); |
| } |
| |
| static void *event_wait_thread_loop(void *context) |
| { |
| int ret = 0; |
| int opcode = 0; |
| bool wait = false; |
| struct adsp_hdlr_inst *adsp_hdlr_inst = |
| (struct adsp_hdlr_inst *) context; |
| struct event_cmd *cmd; |
| struct listnode *node, *tempnode; |
| struct snd_ctl_event mixer_event = {0}; |
| |
| setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); |
| set_sched_policy(0, SP_BACKGROUND); |
| prctl(PR_SET_NAME, (unsigned long)"Event Wait", 0, 0, 0); |
| |
| ret = mixer_subscribe_events(adsp_hdlr_inst->mixer, 1); |
| if (ret < 0) { |
| ALOGE("%s: Could not subscribe for mixer events, ret %d", |
| __func__, ret); |
| goto done; |
| } |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_wait_lock); |
| while (1) { |
| if (list_empty(&adsp_hdlr_inst->event_wait_cmd_list) && !wait) { |
| ALOGVV("%s SLEEPING", __func__); |
| pthread_cond_wait(&adsp_hdlr_inst->event_wait_cond, &adsp_hdlr_inst->event_wait_lock); |
| ALOGVV("%s RUNNING", __func__); |
| } |
| /* execute command if available */ |
| if (!list_empty(&adsp_hdlr_inst->event_wait_cmd_list)) { |
| node = list_head(&adsp_hdlr_inst->event_wait_cmd_list); |
| list_remove(node); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_wait_lock); |
| cmd = node_to_item(node, struct event_cmd, list); |
| opcode = cmd->opcode; |
| /* wait if no command avialable */ |
| } else if (wait) |
| opcode = EVENT_CMD_WAIT; |
| /* check que again and sleep if needed */ |
| else |
| continue; |
| |
| ALOGVV("%s command received: %d", __func__, opcode); |
| switch(opcode) { |
| case EVENT_CMD_EXIT: |
| free(cmd); |
| goto thread_exit; |
| case EVENT_CMD_WAIT: |
| ret = mixer_wait_event(adsp_hdlr_inst->mixer, WAIT_EVENT_POLL_TIMEOUT); |
| ALOGVV("%s: mixer_wait_event unblocked!, ret = %d", __func__, ret); |
| if (ret < 0) { |
| ALOGE("%s: mixer_wait_event err!, ret = %d", __func__, ret); |
| } else if (ret > 0) { |
| ret = mixer_read(adsp_hdlr_inst->mixer, &mixer_event); |
| if (ret >= 0) |
| send_cmd_event_callback_thread(adsp_hdlr_inst, EVENT_CMD_GET, mixer_event.data.elem.id.name); |
| else |
| ALOGE("%s: mixer_read failed, ret = %d", __func__, ret); |
| } |
| /* Once wait command has been sent continue to wait for |
| events unless something else is in the command que */ |
| wait = true; |
| break; |
| default: |
| ALOGE("%s unknown command received: %d", __func__, opcode); |
| break; |
| } |
| |
| if (cmd != NULL) { |
| free(cmd); |
| cmd = NULL; |
| } |
| } |
| thread_exit: |
| ret = mixer_subscribe_events(adsp_hdlr_inst->mixer, 0); |
| if (ret < 0) { |
| ALOGE("%s: Could not un-subscribe for mixer events, ret %d", |
| __func__, ret); |
| goto done; |
| } |
| pthread_mutex_lock(&adsp_hdlr_inst->event_wait_lock); |
| list_for_each_safe(node, tempnode, &adsp_hdlr_inst->event_wait_cmd_list) { |
| list_remove(node); |
| free(node); |
| } |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_wait_lock); |
| done: |
| return NULL; |
| } |
| |
| static void *event_callback_thread_loop(void *context) |
| { |
| int ret = 0; |
| size_t count = 0; |
| struct adsp_hdlr_inst *adsp_hdlr_inst = |
| (struct adsp_hdlr_inst *)context; |
| struct mixer_ctl *ctl = NULL; |
| uint8_t param[MAX_EVENT_PAYLOAD] = {0}; |
| struct event_cmd *cmd; |
| struct listnode *node, *tempnode; |
| struct adsp_hdlr_event_info *event_info; |
| bool param_avail = false; |
| struct msm_adsp_event_data *received_evt; |
| |
| setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); |
| set_sched_policy(0, SP_BACKGROUND); |
| prctl(PR_SET_NAME, (unsigned long)"Event Callback", 0, 0, 0); |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_callback_lock); |
| while (1) { |
| if (list_empty(&adsp_hdlr_inst->event_callback_cmd_list)) { |
| ALOGVV("%s SLEEPING", __func__); |
| pthread_cond_wait(&adsp_hdlr_inst->event_callback_cond, |
| &adsp_hdlr_inst->event_callback_lock); |
| ALOGVV("%s RUNNING", __func__); |
| continue; |
| } |
| node = list_head(&adsp_hdlr_inst->event_callback_cmd_list); |
| list_remove(node); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_callback_lock); |
| cmd = node_to_item(node, struct event_cmd, list); |
| |
| ALOGVV("%s command received: %d", __func__, cmd->opcode); |
| switch(cmd->opcode) { |
| case EVENT_CMD_EXIT: |
| free(cmd); |
| goto thread_exit; |
| case EVENT_CMD_GET: |
| param_avail = false; |
| pthread_mutex_lock(&adsp_hdlr_inst->event_list_lock); |
| /* Find the mixer control for which event is triggered */ |
| list_for_each(node, &adsp_hdlr_inst->event_list) { |
| event_info = node_to_item(node, struct adsp_hdlr_event_info, list); |
| ALOGVV("%s: cmd mixer name: %s, event list mixer name: %s", __func__, |
| cmd->cb_mixer_ctl_name, event_info->cb_mixer_ctl_name); |
| if (!strcmp(cmd->cb_mixer_ctl_name, event_info->cb_mixer_ctl_name)) { |
| if (!param_avail) { |
| ctl = mixer_get_ctl_by_name(adsp_hdlr_inst->mixer, cmd->cb_mixer_ctl_name); |
| if (!ctl) { |
| ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__, |
| cmd->cb_mixer_ctl_name); |
| break; |
| } |
| mixer_ctl_update(ctl); |
| count = mixer_ctl_get_num_values(ctl); |
| if ((count > MAX_EVENT_PAYLOAD) || (count <= 0)) { |
| ALOGE("%s: count is %d greater than allowed for %s mixer cmd", |
| __func__, count, cmd->cb_mixer_ctl_name); |
| break; |
| } |
| ret = mixer_ctl_get_array(ctl, param, count); |
| if (ret < 0) { |
| ALOGE("%s: mixer_ctl_get_array failed! mixer - %s, ret = %d", |
| __func__, cmd->cb_mixer_ctl_name, ret); |
| break; |
| } |
| param_avail = true; |
| received_evt = (struct msm_adsp_event_data *)param; |
| ALOGD("%s: event type = %d", __func__, received_evt->event_type); |
| } |
| /* Call appropriate event type client callback */ |
| if (param_avail && event_info->event_type == received_evt->event_type) { |
| struct adsp_hdlr_stream_data *stream_data = event_info->stream_handle; |
| if (event_info->cb != NULL) { |
| ALOGVV("%s: calling event callback function", __func__); |
| event_info->cb(event_info->stream_handle, |
| received_evt->payload, |
| event_info->cookie); |
| } else if (stream_data->client_callback != NULL) { |
| ALOGVV("%s: sending client callback event %d", __func__, |
| AUDIO_EXTN_STREAM_CBK_EVENT_ADSP); |
| stream_data->client_callback((stream_callback_event_t) |
| AUDIO_EXTN_STREAM_CBK_EVENT_ADSP, |
| received_evt, |
| stream_data->client_cookie); |
| } |
| break; |
| } |
| } |
| } |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_list_lock); |
| |
| break; |
| default: |
| ALOGE("%s unknown command received: %d", __func__, cmd->opcode); |
| break; |
| } |
| free(cmd); |
| } |
| thread_exit: |
| pthread_mutex_lock(&adsp_hdlr_inst->event_callback_lock); |
| list_for_each_safe(node, tempnode, &adsp_hdlr_inst->event_callback_cmd_list) { |
| list_remove(node); |
| free(node); |
| } |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_callback_lock); |
| done: |
| return NULL; |
| } |
| |
| int audio_extn_adsp_hdlr_stream_deregister_event(void *handle, void *data) |
| { |
| struct listnode *node, *tempnode; |
| struct adsp_hdlr_stream_data *stream_data = (struct adsp_hdlr_stream_data *)handle; |
| struct adsp_hdlr_event_info *event_info; |
| struct audio_adsp_event *param = (struct audio_adsp_event *)data; |
| |
| if (!handle) { |
| ALOGE("%s: Invalid handle", __func__); |
| return -EINVAL; |
| } |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_list_lock); |
| if (list_empty(&adsp_hdlr_inst->event_list)) { |
| ALOGD("%s: event list is empty", __func__); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_list_lock); |
| return 0; |
| } |
| list_for_each_safe(node, tempnode, &adsp_hdlr_inst->event_list) { |
| event_info = node_to_item(node, struct adsp_hdlr_event_info, list); |
| if (param && event_info->stream_handle == stream_data) { |
| /* if the type of event is avaliable to dereg then dereg only that event */ |
| if (event_info->event_type == param->event_type) { |
| ALOGD("%s: Deregister event type = %d", __func__, event_info->event_type); |
| list_remove(node); |
| free(event_info); |
| } |
| } else if (event_info->stream_handle == stream_data) { |
| /* Dereg all the events related to that stream */ |
| ALOGD("%s: Deregister all stream events", __func__); |
| list_remove(node); |
| free(event_info); |
| } |
| } |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_list_lock); |
| |
| if (list_empty(&adsp_hdlr_inst->event_list)) { |
| ALOGD("%s: Closing event threads", __func__); |
| destroy_event_threads(adsp_hdlr_inst); |
| pthread_mutex_destroy(&adsp_hdlr_inst->event_wait_lock); |
| pthread_mutex_destroy(&adsp_hdlr_inst->event_callback_lock); |
| pthread_mutex_destroy(&adsp_hdlr_inst->event_list_lock); |
| } |
| |
| return 0; |
| } |
| |
| int audio_extn_adsp_hdlr_stream_register_event(void *handle, void *data, |
| adsp_event_callback_t cb, |
| void *cookie) |
| { |
| int ret = 0; |
| char mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0}; |
| char cb_mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0}; |
| struct mixer_ctl *ctl = NULL; |
| uint8_t payload[AUDIO_MAX_ADSP_STREAM_CMD_PAYLOAD_LEN] = {0}; |
| struct adsp_hdlr_stream_data *stream_data = (struct adsp_hdlr_stream_data *)handle; |
| struct adsp_hdlr_stream_cfg *config = &stream_data->config; |
| struct adsp_hdlr_event_info *event_info; |
| struct audio_adsp_event *param = (struct audio_adsp_event *)data; |
| |
| if (!param || !handle) { |
| ret = -EINVAL; |
| ALOGE("%s: Invalid input arguments", __func__); |
| goto done; |
| } |
| |
| /* check if param size exceeds max size supported by mixer */ |
| if (param->payload_length > AUDIO_MAX_ADSP_STREAM_CMD_PAYLOAD_LEN) { |
| ALOGE("%s: Invalid payload_length %d",__func__, param->payload_length); |
| return -EINVAL; |
| } |
| ret = snprintf(cb_mixer_ctl_name, sizeof(cb_mixer_ctl_name), |
| "ADSP Stream Callback Event %d", config->pcm_device_id); |
| if (ret < 0) { |
| ALOGE("%s: snprintf failed",__func__); |
| ret = -EINVAL; |
| goto done; |
| } |
| ctl = mixer_get_ctl_by_name(adsp_hdlr_inst->mixer, cb_mixer_ctl_name); |
| if (!ctl) { |
| ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__, |
| cb_mixer_ctl_name); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| ret = snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), |
| "ADSP Stream Cmd %d", config->pcm_device_id); |
| if (ret < 0) { |
| ALOGE("%s: snprintf failed",__func__); |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| ctl = mixer_get_ctl_by_name(adsp_hdlr_inst->mixer, mixer_ctl_name); |
| if (!ctl) { |
| ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__, |
| mixer_ctl_name); |
| ret = -EINVAL; |
| goto done; |
| } |
| ALOGD("%s: event = %d, payload_length %d", __func__, param->event_type, param->payload_length); |
| |
| /* copy event_type, payload size and payload */ |
| memcpy(payload, ¶m->event_type, |
| sizeof(param->event_type)); |
| memcpy(payload + sizeof(param->event_type), ¶m->payload_length, |
| sizeof(param->payload_length)); |
| memcpy(payload + sizeof(param->event_type) + sizeof(param->payload_length), |
| param->payload, param->payload_length); |
| ret = mixer_ctl_set_array(ctl, payload, (sizeof(param->event_type) + |
| sizeof(param->payload_length) + param->payload_length)); |
| if (ret < 0) { |
| ALOGE("%s: Could not set ctl for mixer cmd - %s, ret %d", __func__, |
| mixer_ctl_name, ret); |
| goto done; |
| } |
| |
| if (list_empty(&adsp_hdlr_inst->event_list)) { |
| pthread_mutex_init(&adsp_hdlr_inst->event_wait_lock, |
| (const pthread_mutexattr_t *) NULL); |
| pthread_mutex_init(&adsp_hdlr_inst->event_callback_lock, |
| (const pthread_mutexattr_t *) NULL); |
| pthread_mutex_init(&adsp_hdlr_inst->event_list_lock, |
| (const pthread_mutexattr_t *) NULL); |
| |
| /* create event threads during first event registration */ |
| pthread_mutex_lock(&adsp_hdlr_inst->event_wait_lock); |
| if (!adsp_hdlr_inst->event_wait_thread_active) |
| create_event_wait_thread(adsp_hdlr_inst); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_wait_lock); |
| |
| pthread_mutex_lock(&adsp_hdlr_inst->event_callback_lock); |
| if (!adsp_hdlr_inst->event_callback_thread_active) |
| create_event_callback_thread(adsp_hdlr_inst); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_callback_lock); |
| |
| send_cmd_event_wait_thread(adsp_hdlr_inst, EVENT_CMD_WAIT); |
| } |
| event_info = (struct adsp_hdlr_event_info *) calloc(1, |
| sizeof(struct adsp_hdlr_event_info)); |
| if (event_info == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| event_info->event_type = param->event_type; |
| event_info->cb = cb; |
| event_info->cookie = cookie; |
| event_info->stream_handle = stream_data; |
| strlcpy(event_info->mixer_ctl_name, mixer_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); |
| strlcpy(event_info->cb_mixer_ctl_name, cb_mixer_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); |
| pthread_mutex_lock(&adsp_hdlr_inst->event_list_lock); |
| list_add_tail(&adsp_hdlr_inst->event_list, &event_info->list); |
| ALOGD("%s: event_info type %d added from the list", __func__, event_info->event_type); |
| pthread_mutex_unlock(&adsp_hdlr_inst->event_list_lock); |
| |
| done: |
| return ret; |
| } |
| |
| int audio_extn_adsp_hdlr_stream_set_param(void *handle, |
| adsp_hdlr_cmd_t cmd, |
| void *param) |
| { |
| int ret = 0; |
| |
| if (handle == NULL) { |
| ALOGE("%s: Invalid handle",__func__); |
| return -EINVAL; |
| } |
| |
| switch (cmd) { |
| case ADSP_HDLR_STREAM_CMD_REGISTER_EVENT : |
| ret = audio_extn_adsp_hdlr_stream_register_event(handle, param, NULL, NULL); |
| if (ret) |
| ALOGE("%s:adsp_hdlr_stream_register_event failed error %d", |
| __func__, ret); |
| break; |
| case ADSP_HDLR_STREAM_CMD_DEREGISTER_EVENT: |
| ret = audio_extn_adsp_hdlr_stream_deregister_event(handle, param); |
| if (ret) |
| ALOGE("%s:adsp_hdlr_stream_deregister_event failed error %d", |
| __func__, ret); |
| break; |
| default: |
| ret = -EINVAL; |
| ALOGE("%s: Unsupported command %d",__func__, cmd); |
| } |
| return ret; |
| } |
| |
| int audio_extn_adsp_hdlr_stream_set_callback(void *handle, |
| stream_callback_t callback, |
| void *cookie) |
| { |
| int ret = 0; |
| struct adsp_hdlr_stream_data *stream_data; |
| |
| ALOGV("%s:: handle %p", __func__, handle); |
| |
| if (!handle) { |
| ALOGE("%s:Invalid handle", __func__); |
| ret = -EINVAL; |
| } else { |
| stream_data = (struct adsp_hdlr_stream_data *)handle; |
| stream_data->client_callback = callback; |
| stream_data->client_cookie = cookie; |
| } |
| return ret; |
| } |
| |
| int audio_extn_adsp_hdlr_stream_close(void *handle) |
| { |
| int ret = 0; |
| struct adsp_hdlr_stream_data *stream_data; |
| |
| ALOGV("%s:: handle %p", __func__, handle); |
| |
| if (!handle) { |
| ALOGE("%s:Invalid handle", __func__); |
| ret = -EINVAL; |
| } else { |
| stream_data = (struct adsp_hdlr_stream_data *)handle; |
| ret = audio_extn_adsp_hdlr_stream_deregister_event(stream_data, NULL); |
| if (ret) |
| ALOGE("%s:adsp_hdlr_stream_deregister_event failed error %d", |
| __func__, ret); |
| free(stream_data); |
| stream_data = NULL; |
| } |
| return ret; |
| } |
| |
| int audio_extn_adsp_hdlr_stream_open(void **handle, |
| struct adsp_hdlr_stream_cfg *config) |
| { |
| |
| int ret = 0; |
| struct adsp_hdlr_stream_data *stream_data; |
| |
| if (!adsp_hdlr_inst) { |
| ALOGE("%s: Not Inited", __func__); |
| return -ENODEV;; |
| } |
| |
| if ((!config) || (config->type != PCM_PLAYBACK)) { |
| ALOGE("%s: Invalid config param", __func__); |
| return -EINVAL; |
| } |
| |
| ALOGV("%s::pcm_device_id %d, flags %x type %d ", __func__, |
| config->pcm_device_id, config->flags, config->type); |
| |
| *handle = NULL; |
| |
| stream_data = (struct adsp_hdlr_stream_data *) calloc(1, |
| sizeof(struct adsp_hdlr_stream_data)); |
| if (stream_data == NULL) { |
| ret = -ENOMEM; |
| } |
| stream_data->config = *config; |
| *handle = (void **)stream_data; |
| |
| return ret; |
| } |
| |
| int audio_extn_adsp_hdlr_init(struct mixer *mixer) |
| { |
| ALOGV("%s", __func__); |
| |
| if (!mixer) { |
| ALOGE("%s: invalid mixer", __func__); |
| return -EINVAL; |
| } |
| |
| if (adsp_hdlr_inst) { |
| ALOGD("%s: Already initialized", __func__); |
| return 0; |
| } |
| adsp_hdlr_inst = (struct adsp_hdlr_inst *)calloc(1, |
| sizeof(struct adsp_hdlr_inst)); |
| if (!adsp_hdlr_inst) { |
| ALOGE("%s: calloc failed for adsp_hdlr_inst", __func__); |
| return -EINVAL; |
| } |
| adsp_hdlr_inst->mixer = mixer; |
| list_init(&adsp_hdlr_inst->event_list); |
| |
| return 0; |
| } |
| |
| int audio_extn_adsp_hdlr_deinit(void) |
| { |
| if (adsp_hdlr_inst) { |
| free(adsp_hdlr_inst); |
| adsp_hdlr_inst = NULL; |
| } else { |
| ALOGD("%s: Already Deinitialized", __func__); |
| } |
| return 0; |
| } |
| |