hal: add audio ip handler support to loopback extension

Add support to call audio ip handler APIs from loopback extension
for secure dolby playback.

Change-Id: I24686aaae84dbaf4f5a304404bc0009e153ded26
diff --git a/hal/audio_extn/hw_loopback.c b/hal/audio_extn/hw_loopback.c
index ddca6b8..8e577c2 100644
--- a/hal/audio_extn/hw_loopback.c
+++ b/hal/audio_extn/hw_loopback.c
@@ -215,6 +215,9 @@
     int32_t ret = 0;
     struct audio_usecase *uc_info;
     struct audio_device *adev = audio_loopback_mod->adev;
+    struct stream_inout *inout =  &active_loopback_patch->patch_stream;
+    struct audio_port_config *source_patch_config = &active_loopback_patch->
+                                                    loopback_source;
 
     /* 1. Close the PCM devices */
     if (active_loopback_patch->source_stream) {
@@ -251,10 +254,41 @@
     list_remove(&uc_info->list);
     free(uc_info);
 
+    if (audio_extn_ip_hdlr_intf_supported(source_patch_config->format) && inout->ip_hdlr_handle) {
+        ret = audio_extn_ip_hdlr_intf_close(inout->ip_hdlr_handle, true, inout);
+        if (ret < 0)
+            ALOGE("%s: audio_extn_ip_hdlr_intf_close failed %d",__func__, ret);
+    }
+
+    /* close adsp hdrl session before standby */
+    if (inout->adsp_hdlr_stream_handle) {
+        ret = audio_extn_adsp_hdlr_stream_close(inout->adsp_hdlr_stream_handle);
+        if (ret)
+            ALOGE("%s: adsp_hdlr_stream_close failed %d",__func__, ret);
+        inout->adsp_hdlr_stream_handle = NULL;
+    }
+
+    if (audio_extn_ip_hdlr_intf_supported(source_patch_config->format) &&
+        inout->ip_hdlr_handle) {
+        audio_extn_ip_hdlr_intf_deinit(inout->ip_hdlr_handle);
+        inout->ip_hdlr_handle = NULL;
+    }
+
     ALOGD("%s: Release loopback session exit: status(%d)", __func__, ret);
     return ret;
 }
 
+/* Callback funtion called in the case of failures */
+int loopback_stream_cb(stream_callback_event_t event, void *param, void *cookie)
+{
+    if (event == AUDIO_EXTN_STREAM_CBK_EVENT_ERROR) {
+        pthread_mutex_lock(&audio_loopback_mod->lock);
+        release_loopback_session(cookie);
+        pthread_mutex_unlock(&audio_loopback_mod->lock);
+    }
+    return 0;
+}
+
 /* Create a loopback session based on active loopback patch selected */
 int create_loopback_session(loopback_patch_t *active_loopback_patch)
 {
@@ -269,6 +303,8 @@
                                                     loopback_source;
     struct audio_port_config *sink_patch_config = &active_loopback_patch->
                                                     loopback_sink;
+    struct stream_inout *inout =  &active_loopback_patch->patch_stream;
+    struct adsp_hdlr_stream_cfg hdlr_stream_cfg;
 
     ALOGD("%s: Create loopback session begin", __func__);
 
@@ -303,6 +339,29 @@
     ALOGD("%s: LOOPBACK PCM devices (rx: %d tx: %d) usecase(%d)",
         __func__, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id, uc_info->id);
 
+    /* setup a channel for client <--> adsp communication for stream events */
+    inout->dev = adev;
+    inout->client_callback = loopback_stream_cb;
+    inout->client_cookie = active_loopback_patch;
+    hdlr_stream_cfg.pcm_device_id = pcm_dev_asm_rx_id;
+    hdlr_stream_cfg.flags = 0;
+    hdlr_stream_cfg.type = PCM_PLAYBACK;
+    ret = audio_extn_adsp_hdlr_stream_open(&inout->adsp_hdlr_stream_handle,
+            &hdlr_stream_cfg);
+    if (ret) {
+        ALOGE("%s: adsp_hdlr_stream_open failed %d", __func__, ret);
+        inout->adsp_hdlr_stream_handle = NULL;
+        goto exit;
+    }
+    if (audio_extn_ip_hdlr_intf_supported(source_patch_config->format)) {
+        ret = audio_extn_ip_hdlr_intf_init(&inout->ip_hdlr_handle, NULL, NULL);
+        if (ret < 0) {
+            ALOGE("%s: audio_extn_ip_hdlr_intf_init failed %d", __func__, ret);
+            inout->ip_hdlr_handle = NULL;
+            goto exit;
+        }
+    }
+
     if (source_patch_config->format == AUDIO_FORMAT_IEC61937) {
         // This is needed to set a known format to DSP and handle
         // any format change via ADSP event
@@ -378,6 +437,14 @@
         ret = -EINVAL;
         goto exit;
     }
+    if (audio_extn_ip_hdlr_intf_supported(source_patch_config->format) && inout->ip_hdlr_handle) {
+        ret = audio_extn_ip_hdlr_intf_open(inout->ip_hdlr_handle, true, inout,
+                                           USECASE_AUDIO_TRANSCODE_LOOPBACK);
+        if (ret < 0) {
+            ALOGE("%s: audio_extn_ip_hdlr_intf_open failed %d",__func__, ret);
+            goto exit;
+        }
+    }
 
     /* Move patch state to running, now that session is set up */
     active_loopback_patch->patch_state = PATCH_RUNNING;
@@ -411,6 +478,7 @@
     int status = 0;
     patch_handle_type_t loopback_patch_type=0x0;
     loopback_patch_t loopback_patch, *active_loopback_patch = NULL;
+
     ALOGV("%s : Create audio patch begin", __func__);
 
     if ((audio_loopback_mod == NULL) || (dev == NULL)) {
diff --git a/hal/audio_extn/ip_hdlr_intf.c b/hal/audio_extn/ip_hdlr_intf.c
index 411b16f..4242d68 100644
--- a/hal/audio_extn/ip_hdlr_intf.c
+++ b/hal/audio_extn/ip_hdlr_intf.c
@@ -42,6 +42,7 @@
 #define LIB_PATH "/system/vendor/lib/libaudio_ip_handler.so"
 #endif
 
+#include <pthread.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <dlfcn.h>
@@ -57,6 +58,12 @@
 #define ADSP_EVENT_ID_RTIC            0x00013239
 #define ADSP_EVENT_ID_RTIC_FAIL       0x0001323A
 
+struct ip_hdlr_stream {
+    struct listnode list;
+    void *stream;
+    audio_usecase_t usecase;
+};
+
 struct ip_hdlr_intf {
     void *lib_hdl;
     int (*init)(void **handle, char *lib_path, void **lib_handle);
@@ -67,6 +74,8 @@
     int (*event)(void *handle, void *payload);
     int (*reg_cb)(void *handle, void *ack_cb, void *fail_cb);
 
+    struct listnode stream_list;
+    pthread_mutex_t stream_list_lock;
     int ref_cnt;
 };
 static struct ip_hdlr_intf *ip_hdlr = NULL;
@@ -109,9 +118,7 @@
 
 bool audio_extn_ip_hdlr_intf_supported(audio_format_t format)
 {
-    if ((format & AUDIO_FORMAT_MAIN_MASK == AUDIO_FORMAT_AC3) ||
-        (format & AUDIO_FORMAT_MAIN_MASK == AUDIO_FORMAT_E_AC3) ||
-        (format & AUDIO_FORMAT_MAIN_MASK == AUDIO_FORMAT_DOLBY_TRUEHD))
+    if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_DOLBY_TRUEHD)
         return true;
     else
         return false;
@@ -132,8 +139,37 @@
     struct mixer_ctl *ctl = NULL;
     struct stream_out *out = (struct stream_out *)aud_sess_handle;
     struct rtic_ack_param param;
+    struct listnode *node, *tempnode;
+    struct ip_hdlr_stream *stream_info;
+    struct audio_device *adev = NULL;
+    audio_usecase_t usecase = 0;
 
-    pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
+    pthread_mutex_lock(&ip_hdlr->stream_list_lock);
+    list_for_each_safe(node, tempnode, &ip_hdlr->stream_list) {
+        stream_info = node_to_item(node, struct ip_hdlr_stream, list);
+        /* send the error if rtic failure notifcation is received */
+        if ((stream_info->stream == aud_sess_handle) &&
+            (stream_info->usecase == USECASE_AUDIO_TRANSCODE_LOOPBACK)) {
+            struct stream_inout *inout = (struct stream_inout *)aud_sess_handle;
+            usecase = stream_info->usecase;
+            adev = inout->dev;
+            break;
+        } else if (stream_info->stream == aud_sess_handle) {
+            struct stream_out *out = (struct stream_out *)aud_sess_handle;
+            usecase = stream_info->usecase;
+            adev = out->dev;
+            break;
+        }
+    }
+    pthread_mutex_unlock(&ip_hdlr->stream_list_lock);
+
+    if (adev == NULL) {
+        ALOGE("%s:[%d] Invalid adev", __func__, ip_hdlr->ref_cnt);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    pcm_device_id = platform_get_pcm_device_id(usecase, PCM_PLAYBACK);
 
     ALOGVV("%s:[%d] token = %d, info->status = %d, pcm_id = %d",__func__,
           ip_hdlr->ref_cnt, info->token, info->status, pcm_device_id);
@@ -146,7 +182,7 @@
         ret = -EINVAL;
         goto done;
     }
-    ctl = mixer_get_ctl_by_name(out->dev->mixer, mixer_ctl_name);
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
     if (!ctl) {
         ALOGE("%s:[%d] Could not get ctl for mixer cmd - %s", __func__,
               ip_hdlr->ref_cnt, mixer_ctl_name);
@@ -167,37 +203,56 @@
     return ret;
 }
 
-/* Acquire Mutex lock on output stream */
-static void lock_output_stream(struct stream_out *out)
-{
-    pthread_mutex_lock(&out->pre_lock);
-    pthread_mutex_lock(&out->lock);
-    pthread_mutex_unlock(&out->pre_lock);
-}
-
 int audio_extn_ip_hdlr_intf_rtic_fail(void *aud_sess_handle)
 {
-    struct stream_out *out = (struct stream_out *)aud_sess_handle;
+    struct listnode *node, *tempnode;
+    struct ip_hdlr_stream *stream_info;
 
     ALOGD("%s:[%d] sess_handle = %p",__func__, ip_hdlr->ref_cnt, aud_sess_handle);
 
-    /* send the error if rtic fail notifcation is received */
-    lock_output_stream(out);
-    if (out && out->client_callback)
-        out->client_callback(AUDIO_EXTN_STREAM_CBK_EVENT_ERROR, NULL, out->client_cookie);
-    pthread_mutex_unlock(&out->lock);
+    pthread_mutex_lock(&ip_hdlr->stream_list_lock);
+    list_for_each_safe(node, tempnode, &ip_hdlr->stream_list) {
+        stream_info = node_to_item(node, struct ip_hdlr_stream, list);
+        /* send the error if rtic failure notifcation is received */
+        if ((stream_info->stream == aud_sess_handle) &&
+            (stream_info->usecase == USECASE_AUDIO_TRANSCODE_LOOPBACK)) {
+            struct stream_inout *inout = (struct stream_inout *)aud_sess_handle;
+            pthread_mutex_lock(&inout->pre_lock);
+            pthread_mutex_lock(&inout->lock);
+            pthread_mutex_unlock(&inout->pre_lock);
+            ALOGVV("%s:[%d] calling client callback", __func__, ip_hdlr->ref_cnt);
+            if (inout && inout->client_callback)
+                inout->client_callback(AUDIO_EXTN_STREAM_CBK_EVENT_ERROR, NULL, inout->client_cookie);
+            pthread_mutex_unlock(&inout->lock);
+            break;
+        } else if (stream_info->stream == aud_sess_handle) {
+            struct stream_out *out = (struct stream_out *)aud_sess_handle;
+            pthread_mutex_lock(&out->pre_lock);
+            pthread_mutex_lock(&out->lock);
+            pthread_mutex_unlock(&out->pre_lock);
+            ALOGVV("%s:[%d] calling client callback", __func__, ip_hdlr->ref_cnt);
+            if (out && out->client_callback)
+                out->client_callback(AUDIO_EXTN_STREAM_CBK_EVENT_ERROR, NULL, out->client_cookie);
+            pthread_mutex_unlock(&out->lock);
+            break;
+        }
+    }
+    pthread_mutex_unlock(&ip_hdlr->stream_list_lock);
 
     return 0;
 }
 
-static int audio_extn_ip_hdlr_intf_open_dsp(void *handle, void *stream_handle)
+static int audio_extn_ip_hdlr_intf_open_dsp(void *handle, void *stream_handle, audio_usecase_t usecase)
 {
     int ret = 0, fd = 0, pcm_device_id = 0;
     struct audio_adsp_event *param;
     struct reg_event *reg_ev;
     size_t shm_size;
     void  *shm_buf;;
-    struct stream_out *out = (struct stream_out *)stream_handle;
+    struct stream_out *out;
+    struct stream_inout *inout;
+    void *adsp_hdlr_stream_handle;
+    struct audio_device *dev = NULL;
     struct mixer_ctl *ctl = NULL;
     char mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0};
 
@@ -220,8 +275,18 @@
     param->payload_length = sizeof(struct reg_event);
     param->payload = reg_ev;
 
+    if (usecase == USECASE_AUDIO_TRANSCODE_LOOPBACK) {
+        inout = (struct stream_inout *)stream_handle;
+        adsp_hdlr_stream_handle = inout->adsp_hdlr_stream_handle;
+        dev = inout->dev;
+    } else {
+        out = (struct stream_out *)stream_handle;
+        adsp_hdlr_stream_handle = out->adsp_hdlr_stream_handle;
+        dev = out->dev;
+    }
+
     /* Register for event and its callback */
-    ret = audio_extn_adsp_hdlr_stream_register_event(out->adsp_hdlr_stream_handle, param,
+    ret = audio_extn_adsp_hdlr_stream_register_event(adsp_hdlr_stream_handle, param,
                                                      audio_extn_ip_hdlr_intf_event,
                                                      handle);
     if (ret < 0) {
@@ -231,16 +296,17 @@
 
     ip_hdlr->reg_cb(handle, &audio_extn_ip_hdlr_intf_rtic_ack, &audio_extn_ip_hdlr_intf_rtic_fail);
     ip_hdlr->shm_info(handle, &fd);
-    ALOGV("%s: fd = %d", __func__, fd);
 
-    pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
+    pcm_device_id = platform_get_pcm_device_id(usecase, PCM_PLAYBACK);
     ret = snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
                    "Playback ION FD %d", pcm_device_id);
     if (ret < 0) {
         ALOGE("%s:[%d] snprintf failed",__func__, ip_hdlr->ref_cnt, ret);
         goto done;
     }
-    ctl = mixer_get_ctl_by_name(out->dev->mixer, mixer_ctl_name);
+    ALOGV("%s: fd = %d  pcm_id = %d", __func__, fd, pcm_device_id);
+
+    ctl = mixer_get_ctl_by_name(dev->mixer, mixer_ctl_name);
     if (!ctl) {
         ALOGE("%s:[%d] Could not get ctl for mixer cmd - %s", __func__,
               ip_hdlr->ref_cnt, mixer_ctl_name);
@@ -260,28 +326,39 @@
     return ret;
 }
 
-int audio_extn_ip_hdlr_intf_open(void *handle, bool is_dsp_decode, void *aud_sess_handle)
+int audio_extn_ip_hdlr_intf_open(void *handle, bool is_dsp_decode,
+                                 void *aud_sess_handle, audio_usecase_t usecase)
 {
     int ret = 0;
+    struct ip_hdlr_stream *stream_info;
 
     if (!handle || !aud_sess_handle) {
         ALOGE("%s:[%d] Invalid arguments, handle %p", __func__, ip_hdlr->ref_cnt, handle);
         return -EINVAL;
     }
 
+    stream_info = (struct ip_hdlr_stream *)calloc(1, sizeof(struct ip_hdlr_stream));
+    if (!stream_info)
+        return -ENOMEM;
+    stream_info->stream = aud_sess_handle;
+    stream_info->usecase = usecase;
+
     ret = ip_hdlr->open(handle, is_dsp_decode, aud_sess_handle);
     if (ret < 0) {
         ALOGE("%s:[%d] open failed", __func__, ip_hdlr->ref_cnt);
         return -EINVAL;
     }
-    ALOGD("%s:[%d] handle = %p, sess_handle = %p, is_dsp_decode = %d",__func__,
-          ip_hdlr->ref_cnt, handle, aud_sess_handle, is_dsp_decode);
+    ALOGD("%s:[%d] handle = %p, sess_handle = %p, is_dsp_decode = %d, usecase = %d",
+          __func__, ip_hdlr->ref_cnt, handle, aud_sess_handle, is_dsp_decode, usecase);
     if (is_dsp_decode) {
-        ret = audio_extn_ip_hdlr_intf_open_dsp(handle, aud_sess_handle);
+        ret = audio_extn_ip_hdlr_intf_open_dsp(handle, aud_sess_handle, usecase);
         if (ret < 0)
             ip_hdlr->close(handle);
     }
 
+    pthread_mutex_lock(&ip_hdlr->stream_list_lock);
+    list_add_tail(&ip_hdlr->stream_list, &stream_info->list);
+    pthread_mutex_unlock(&ip_hdlr->stream_list_lock);
 done:
     return ret;
 }
@@ -289,24 +366,46 @@
 int audio_extn_ip_hdlr_intf_close(void *handle, bool is_dsp_decode, void *aud_sess_handle)
 {
     struct audio_adsp_event param;
+    void *adsp_hdlr_stream_handle;
+    struct listnode *node, *tempnode;
+    struct ip_hdlr_stream *stream_info;
+    audio_usecase_t usecase = 0;
     int ret = 0;
 
     if (!handle) {
         ALOGE("%s:[%d] handle is NULL", __func__, ip_hdlr->ref_cnt);
         return -EINVAL;
     }
-    ALOGD("%s:[%d] handle = %p",__func__, ip_hdlr->ref_cnt, handle);
 
     ret = ip_hdlr->close(handle);
     if (ret < 0)
         ALOGE("%s:[%d] close failed", __func__, ip_hdlr->ref_cnt);
 
+    pthread_mutex_lock(&ip_hdlr->stream_list_lock);
+    list_for_each_safe(node, tempnode, &ip_hdlr->stream_list) {
+        stream_info = node_to_item(node, struct ip_hdlr_stream, list);
+        if (stream_info->stream == aud_sess_handle) {
+            usecase = stream_info->usecase;
+            list_remove(node);
+            free(stream_info);
+            break;
+        }
+    }
+    pthread_mutex_unlock(&ip_hdlr->stream_list_lock);
+    ALOGD("%s:[%d] handle = %p, usecase = %d",__func__, ip_hdlr->ref_cnt, handle, usecase);
+
     if (is_dsp_decode) {
-        struct stream_out *out = (struct stream_out *)aud_sess_handle;
+        if (usecase == USECASE_AUDIO_TRANSCODE_LOOPBACK) {
+            struct stream_inout *inout = (struct stream_inout *)aud_sess_handle;
+            adsp_hdlr_stream_handle = inout->adsp_hdlr_stream_handle;
+        } else {
+            struct stream_out *out = (struct stream_out *)aud_sess_handle;
+            adsp_hdlr_stream_handle = out->adsp_hdlr_stream_handle;
+        }
         param.event_type = AUDIO_STREAM_ENCDEC_EVENT;
         param.payload_length = 0;
         /* Deregister the event */
-        ret = audio_extn_adsp_hdlr_stream_deregister_event(out->adsp_hdlr_stream_handle, &param);
+        ret = audio_extn_adsp_hdlr_stream_deregister_event(adsp_hdlr_stream_handle, &param);
         if (ret < 0)
             ALOGE("%s:[%d] event deregister failed", __func__, ip_hdlr->ref_cnt);
     }
@@ -323,6 +422,9 @@
         if (!ip_hdlr)
             return -ENOMEM;
 
+        list_init(&ip_hdlr->stream_list);
+        pthread_mutex_init(&ip_hdlr->stream_list_lock, (const pthread_mutexattr_t *) NULL);
+
         ip_hdlr->lib_hdl = dlopen(LIB_PATH, RTLD_NOW);
         if (ip_hdlr->lib_hdl == NULL) {
              ALOGE("%s: DLOPEN failed, %s", __func__, dlerror());
@@ -365,6 +467,7 @@
 dlclose:
     dlclose(ip_hdlr->lib_hdl);
 err:
+    pthread_mutex_destroy(&ip_hdlr->stream_list_lock);
     free(ip_hdlr);
     ip_hdlr = NULL;
     return ret;
@@ -387,6 +490,7 @@
         if (ip_hdlr->lib_hdl)
             dlclose(ip_hdlr->lib_hdl);
 
+        pthread_mutex_destroy(&ip_hdlr->stream_list_lock);
         free(ip_hdlr);
         ip_hdlr == NULL;
     }
diff --git a/hal/audio_extn/ip_hdlr_intf.h b/hal/audio_extn/ip_hdlr_intf.h
index 01d0b7b..e8a9166 100644
--- a/hal/audio_extn/ip_hdlr_intf.h
+++ b/hal/audio_extn/ip_hdlr_intf.h
@@ -32,7 +32,8 @@
 
 #ifdef AUDIO_EXTN_IP_HDLR_ENABLED
 
-int audio_extn_ip_hdlr_intf_open(void *handle, bool is_dsp_decode, void *aud_sess_handle);
+int audio_extn_ip_hdlr_intf_open(void *handle, bool is_dsp_decode, void *aud_sess_handle,
+                                 audio_usecase_t usecase);
 int audio_extn_ip_hdlr_intf_close(void *handle, bool is_dsp_decode, void *aud_sess_handle);
 int audio_extn_ip_hdlr_intf_init(void **handle, char *lib_path, void **lib_handle);
 int audio_extn_ip_hdlr_intf_deinit(void *handle);
@@ -40,7 +41,7 @@
 
 #else
 
-#define audio_extn_ip_hdlr_intf_open(handle, is_dsp_decode, aud_sess_handle)  (0)
+#define audio_extn_ip_hdlr_intf_open(handle, is_dsp_decode, aud_sess_handle, usecase)  (0)
 #define audio_extn_ip_hdlr_intf_close(handle, is_dsp_decode, aud_sess_handle) (0)
 #define audio_extn_ip_hdlr_intf_init(handle, lib_path, lib_handle)            (0)
 #define audio_extn_ip_hdlr_intf_deinit(handle)                                (0)
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 6375ec5..2228106 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -2543,7 +2543,7 @@
     ALOGD("%s: exit", __func__);
 
     if (audio_extn_ip_hdlr_intf_supported(out->format) && out->ip_hdlr_handle) {
-        ret = audio_extn_ip_hdlr_intf_open(out->ip_hdlr_handle, true, out);
+        ret = audio_extn_ip_hdlr_intf_open(out->ip_hdlr_handle, true, out, out->usecase);
         if (ret < 0)
             ALOGE("%s: audio_extn_ip_hdlr_intf_open failed %d",__func__, ret);
     }
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 20edc99..80b11b8 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -224,6 +224,11 @@
     pthread_cond_t  cond;
     struct stream_config in_config;
     struct stream_config out_config;
+    struct audio_device *dev;
+    void *adsp_hdlr_stream_handle;
+    void *ip_hdlr_handle;
+    stream_callback_t client_callback;
+    void *client_cookie;
 };
 struct stream_out {
     struct audio_stream_out stream;