Home | History | Annotate | Download | only in audio_extn
      1 /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
      2  *
      3  * Redistribution and use in source and binary forms, with or without
      4  * modification, are permitted provided that the following conditions are
      5  * met:
      6  *     * Redistributions of source code must retain the above copyright
      7  *       notice, this list of conditions and the following disclaimer.
      8  *     * Redistributions in binary form must reproduce the above
      9  *       copyright notice, this list of conditions and the following
     10  *       disclaimer in the documentation and/or other materials provided
     11  *       with the distribution.
     12  *     * Neither the name of The Linux Foundation nor the names of its
     13  *       contributors may be used to endorse or promote products derived
     14  *       from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  *
     28  */
     29 #define LOG_TAG "soundtrigger"
     30 /* #define LOG_NDEBUG 0 */
     31 #define LOG_NDDEBUG 0
     32 
     33 #include <stdbool.h>
     34 #include <stdlib.h>
     35 #include <dlfcn.h>
     36 #include <cutils/log.h>
     37 #include "audio_hw.h"
     38 #include "audio_extn.h"
     39 #include "platform.h"
     40 #include "platform_api.h"
     41 #include "sound_trigger_prop_intf.h"
     42 
     43 #define XSTR(x) STR(x)
     44 #define STR(x) #x
     45 
     46 struct sound_trigger_info  {
     47     struct sound_trigger_session_info st_ses;
     48     bool lab_stopped;
     49     struct listnode list;
     50 };
     51 
     52 struct sound_trigger_audio_device {
     53     void *lib_handle;
     54     struct audio_device *adev;
     55     sound_trigger_hw_call_back_t st_callback;
     56     struct listnode st_ses_list;
     57     pthread_mutex_t lock;
     58 };
     59 
     60 static struct sound_trigger_audio_device *st_dev;
     61 
     62 static struct sound_trigger_info *
     63 get_sound_trigger_info(int capture_handle)
     64 {
     65     struct sound_trigger_info  *st_ses_info = NULL;
     66     struct listnode *node;
     67     ALOGD("%s: list %d capture_handle %d", __func__,
     68            list_empty(&st_dev->st_ses_list), capture_handle);
     69     list_for_each(node, &st_dev->st_ses_list) {
     70         st_ses_info = node_to_item(node, struct sound_trigger_info , list);
     71         if (st_ses_info->st_ses.capture_handle == capture_handle)
     72             return st_ses_info;
     73     }
     74     return NULL;
     75 }
     76 
     77 int audio_hw_call_back(sound_trigger_event_type_t event,
     78                        sound_trigger_event_info_t* config)
     79 {
     80     int status = 0;
     81     struct sound_trigger_info  *st_ses_info;
     82 
     83     if (!st_dev)
     84        return -EINVAL;
     85 
     86     pthread_mutex_lock(&st_dev->lock);
     87     switch (event) {
     88     case ST_EVENT_SESSION_REGISTER:
     89         if (!config) {
     90             ALOGE("%s: NULL config", __func__);
     91             status = -EINVAL;
     92             break;
     93         }
     94         st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
     95         if (!st_ses_info) {
     96             ALOGE("%s: st_ses_info alloc failed", __func__);
     97             status = -ENOMEM;
     98             break;
     99         }
    100         memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
    101         ALOGV("%s: add capture_handle %d pcm %p", __func__,
    102               st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
    103         list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
    104         break;
    105 
    106     case ST_EVENT_SESSION_DEREGISTER:
    107         if (!config) {
    108             ALOGE("%s: NULL config", __func__);
    109             status = -EINVAL;
    110             break;
    111         }
    112         st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
    113         if (!st_ses_info) {
    114             ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
    115             status = -EINVAL;
    116             break;
    117         }
    118         ALOGV("%s: remove capture_handle %d pcm %p", __func__,
    119               st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
    120         list_remove(&st_ses_info->list);
    121         free(st_ses_info);
    122         break;
    123     default:
    124         ALOGW("%s: Unknown event %d", __func__, event);
    125         break;
    126     }
    127     pthread_mutex_unlock(&st_dev->lock);
    128     return status;
    129 }
    130 
    131 void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
    132 {
    133     int status = 0;
    134     struct sound_trigger_info  *st_ses_info = NULL;
    135     audio_event_info_t event;
    136 
    137     if (!st_dev || !in)
    138        return;
    139 
    140     pthread_mutex_lock(&st_dev->lock);
    141     st_ses_info = get_sound_trigger_info(in->capture_handle);
    142     pthread_mutex_unlock(&st_dev->lock);
    143     if (st_ses_info) {
    144         event.u.ses_info = st_ses_info->st_ses;
    145         ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
    146         st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
    147     }
    148 }
    149 void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
    150 {
    151     struct sound_trigger_info  *st_ses_info = NULL;
    152     struct listnode *node;
    153 
    154     if (!st_dev || !in)
    155        return;
    156 
    157     pthread_mutex_lock(&st_dev->lock);
    158     in->is_st_session = false;
    159     ALOGV("%s: list %d capture_handle %d", __func__,
    160           list_empty(&st_dev->st_ses_list), in->capture_handle);
    161     list_for_each(node, &st_dev->st_ses_list) {
    162         st_ses_info = node_to_item(node, struct sound_trigger_info , list);
    163         if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
    164             in->pcm = st_ses_info->st_ses.pcm;
    165             in->config = st_ses_info->st_ses.config;
    166             in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
    167             in->is_st_session = true;
    168             ALOGD("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
    169             break;
    170         }
    171     }
    172     pthread_mutex_unlock(&st_dev->lock);
    173 }
    174 
    175 void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
    176                                      st_event_type_t event)
    177 {
    178     bool raise_event = false;
    179     int device_type = -1;
    180 
    181     if (!st_dev)
    182        return;
    183 
    184     if (snd_device >= SND_DEVICE_OUT_BEGIN &&
    185         snd_device < SND_DEVICE_OUT_END)
    186         device_type = PCM_PLAYBACK;
    187     else if (snd_device >= SND_DEVICE_IN_BEGIN &&
    188         snd_device < SND_DEVICE_IN_END)
    189         device_type = PCM_CAPTURE;
    190     else {
    191         ALOGE("%s: invalid device 0x%x, for event %d",
    192                            __func__, snd_device, event);
    193         return;
    194     }
    195 
    196     raise_event = platform_sound_trigger_device_needs_event(snd_device);
    197     ALOGI("%s: device 0x%x of type %d for Event %d, with Raise=%d",
    198         __func__, snd_device, device_type, event, raise_event);
    199     if (raise_event && (device_type == PCM_CAPTURE)) {
    200         switch(event) {
    201         case ST_EVENT_SND_DEVICE_FREE:
    202             st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
    203             break;
    204         case ST_EVENT_SND_DEVICE_BUSY:
    205             st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
    206             break;
    207         default:
    208             ALOGW("%s:invalid event %d for device 0x%x",
    209                                   __func__, event, snd_device);
    210         }
    211     }/*Events for output device, if required can be placed here in else*/
    212 }
    213 
    214 void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
    215                                      st_event_type_t event)
    216 {
    217     bool raise_event = false;
    218     audio_usecase_t uc_id;
    219     int usecase_type = -1;
    220 
    221     if (!st_dev) {
    222         return;
    223     }
    224 
    225     if (uc_info == NULL) {
    226         ALOGE("%s: usecase is NULL!!!", __func__);
    227         return;
    228     }
    229     uc_id = uc_info->id;
    230     usecase_type = uc_info->type;
    231 
    232     raise_event = platform_sound_trigger_usecase_needs_event(uc_id);
    233     ALOGD("%s: uc_id %d of type %d for Event %d, with Raise=%d",
    234         __func__, uc_id, usecase_type, event, raise_event);
    235     if (raise_event && (usecase_type == PCM_PLAYBACK)) {
    236         switch(event) {
    237         case ST_EVENT_STREAM_FREE:
    238             st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE, NULL);
    239             break;
    240         case ST_EVENT_STREAM_BUSY:
    241             st_dev->st_callback(AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE, NULL);
    242             break;
    243         default:
    244             ALOGW("%s:invalid event %d, for usecase %d",
    245                                   __func__, event, uc_id);
    246         }
    247     }/*Events for capture usecase, if required can be placed here in else*/
    248 }
    249 
    250 void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
    251                                struct str_parms *params)
    252 {
    253     audio_event_info_t event;
    254     char value[32];
    255     int ret, val;
    256 
    257     if(!st_dev || !params) {
    258         ALOGE("%s: str_params NULL", __func__);
    259         return;
    260     }
    261 
    262     ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
    263                             sizeof(value));
    264     if (ret > 0) {
    265         if (strstr(value, "OFFLINE")) {
    266             event.u.status = SND_CARD_STATUS_OFFLINE;
    267             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    268         }
    269         else if (strstr(value, "ONLINE")) {
    270             event.u.status = SND_CARD_STATUS_ONLINE;
    271             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    272         }
    273         else
    274             ALOGE("%s: unknown snd_card_status", __func__);
    275     }
    276 
    277     ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
    278     if (ret > 0) {
    279         if (strstr(value, "OFFLINE")) {
    280             event.u.status = CPE_STATUS_OFFLINE;
    281             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    282         }
    283         else if (strstr(value, "ONLINE")) {
    284             event.u.status = CPE_STATUS_ONLINE;
    285             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    286         }
    287         else
    288             ALOGE("%s: unknown CPE status", __func__);
    289     }
    290 
    291     ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
    292     if (ret >= 0) {
    293         event.u.value = val;
    294         st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
    295     }
    296 }
    297 
    298 int audio_extn_sound_trigger_init(struct audio_device *adev)
    299 {
    300     int status = 0;
    301     char sound_trigger_lib[100];
    302     void *lib_handle;
    303 
    304     ALOGI("%s: Enter", __func__);
    305 
    306     st_dev = (struct sound_trigger_audio_device*)
    307                         calloc(1, sizeof(struct sound_trigger_audio_device));
    308     if (!st_dev) {
    309         ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
    310         return -ENOMEM;
    311     }
    312 
    313     snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
    314              "/system/vendor/lib/hw/sound_trigger.primary.%s.so",
    315               XSTR(SOUND_TRIGGER_PLATFORM_NAME));
    316 
    317     st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
    318 
    319     if (st_dev->lib_handle == NULL) {
    320         ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
    321                 dlerror());
    322         status = -EINVAL;
    323         goto cleanup;
    324     }
    325     ALOGI("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
    326 
    327     st_dev->st_callback = (sound_trigger_hw_call_back_t)
    328               dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
    329 
    330     if (st_dev->st_callback == NULL) {
    331        ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
    332                dlerror());
    333        goto cleanup;
    334     }
    335 
    336     st_dev->adev = adev;
    337     list_init(&st_dev->st_ses_list);
    338 
    339     return 0;
    340 
    341 cleanup:
    342     if (st_dev->lib_handle)
    343         dlclose(st_dev->lib_handle);
    344     free(st_dev);
    345     st_dev = NULL;
    346     return status;
    347 
    348 }
    349 
    350 void audio_extn_sound_trigger_deinit(struct audio_device *adev)
    351 {
    352     ALOGI("%s: Enter", __func__);
    353     if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
    354         dlclose(st_dev->lib_handle);
    355         free(st_dev);
    356         st_dev = NULL;
    357     }
    358 }
    359