Home | History | Annotate | Download | only in audio_extn
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #define LOG_TAG "soundtrigger"
     17 /* #define LOG_NDEBUG 0 */
     18 #define LOG_NDDEBUG 0
     19 
     20 #include <errno.h>
     21 #include <stdbool.h>
     22 #include <stdlib.h>
     23 #include <dlfcn.h>
     24 #include <cutils/log.h>
     25 #include "audio_hw.h"
     26 #include "audio_extn.h"
     27 #include "platform.h"
     28 #include "platform_api.h"
     29 #include "sound_trigger_prop_intf.h"
     30 
     31 #define XSTR(x) STR(x)
     32 #define STR(x) #x
     33 
     34 struct sound_trigger_info  {
     35     struct sound_trigger_session_info st_ses;
     36     bool lab_stopped;
     37     struct listnode list;
     38 };
     39 
     40 struct sound_trigger_audio_device {
     41     void *lib_handle;
     42     struct audio_device *adev;
     43     sound_trigger_hw_call_back_t st_callback;
     44     struct listnode st_ses_list;
     45     pthread_mutex_t lock;
     46 };
     47 
     48 static struct sound_trigger_audio_device *st_dev;
     49 
     50 static struct sound_trigger_info *
     51 get_sound_trigger_info(int capture_handle)
     52 {
     53     struct sound_trigger_info  *st_ses_info = NULL;
     54     struct listnode *node;
     55     ALOGV("%s: list %d capture_handle %d", __func__,
     56            list_empty(&st_dev->st_ses_list), capture_handle);
     57     list_for_each(node, &st_dev->st_ses_list) {
     58         st_ses_info = node_to_item(node, struct sound_trigger_info , list);
     59         if (st_ses_info->st_ses.capture_handle == capture_handle)
     60             return st_ses_info;
     61     }
     62     return NULL;
     63 }
     64 
     65 int audio_hw_call_back(sound_trigger_event_type_t event,
     66                        sound_trigger_event_info_t* config)
     67 {
     68     int status = 0;
     69     struct sound_trigger_info  *st_ses_info;
     70 
     71     if (!st_dev)
     72        return -EINVAL;
     73 
     74     pthread_mutex_lock(&st_dev->lock);
     75     switch (event) {
     76     case ST_EVENT_SESSION_REGISTER:
     77         if (!config) {
     78             ALOGE("%s: NULL config", __func__);
     79             status = -EINVAL;
     80             break;
     81         }
     82         st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
     83         if (!st_ses_info) {
     84             ALOGE("%s: st_ses_info alloc failed", __func__);
     85             status = -ENOMEM;
     86             break;
     87         }
     88         memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
     89         ALOGV("%s: add capture_handle %d pcm %p", __func__,
     90               st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
     91         list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
     92         break;
     93 
     94     case ST_EVENT_SESSION_DEREGISTER:
     95         if (!config) {
     96             ALOGE("%s: NULL config", __func__);
     97             status = -EINVAL;
     98             break;
     99         }
    100         st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
    101         if (!st_ses_info) {
    102             ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
    103             status = -EINVAL;
    104             break;
    105         }
    106         ALOGV("%s: remove capture_handle %d pcm %p", __func__,
    107               st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
    108         list_remove(&st_ses_info->list);
    109         free(st_ses_info);
    110         break;
    111     default:
    112         ALOGW("%s: Unknown event %d", __func__, event);
    113         break;
    114     }
    115     pthread_mutex_unlock(&st_dev->lock);
    116     return status;
    117 }
    118 
    119 int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
    120                        size_t bytes)
    121 {
    122     int ret = -1;
    123     struct sound_trigger_info  *st_info = NULL;
    124     audio_event_info_t event;
    125 
    126     if (!st_dev)
    127        return ret;
    128 
    129     if (!in->is_st_session_active) {
    130         ALOGE(" %s: Sound trigger is not active", __func__);
    131         goto exit;
    132     }
    133     if (in->standby)
    134         in->standby = false;
    135 
    136     pthread_mutex_lock(&st_dev->lock);
    137     st_info = get_sound_trigger_info(in->capture_handle);
    138     pthread_mutex_unlock(&st_dev->lock);
    139     if (st_info) {
    140         event.u.aud_info.ses_info = &st_info->st_ses;
    141         event.u.aud_info.buf = buffer;
    142         event.u.aud_info.num_bytes = bytes;
    143         ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event);
    144     }
    145 
    146 exit:
    147     if (ret) {
    148         if (-ENETRESET == ret)
    149             in->is_st_session_active = false;
    150         memset(buffer, 0, bytes);
    151         ALOGV("%s: read failed status %d - sleep", __func__, ret);
    152         usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) *
    153                                    in->config.rate));
    154     }
    155     return ret;
    156 }
    157 
    158 void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
    159 {
    160     int status = 0;
    161     struct sound_trigger_info  *st_ses_info = NULL;
    162     audio_event_info_t event;
    163 
    164     if (!st_dev || !in)
    165        return;
    166 
    167     pthread_mutex_lock(&st_dev->lock);
    168     st_ses_info = get_sound_trigger_info(in->capture_handle);
    169     pthread_mutex_unlock(&st_dev->lock);
    170     if (st_ses_info) {
    171         event.u.ses_info = st_ses_info->st_ses;
    172         ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
    173         st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
    174     }
    175 }
    176 void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
    177 {
    178     struct sound_trigger_info  *st_ses_info = NULL;
    179     struct listnode *node;
    180 
    181     if (!st_dev || !in)
    182        return;
    183 
    184     pthread_mutex_lock(&st_dev->lock);
    185     in->is_st_session = false;
    186     ALOGV("%s: list %d capture_handle %d", __func__,
    187           list_empty(&st_dev->st_ses_list), in->capture_handle);
    188     list_for_each(node, &st_dev->st_ses_list) {
    189         st_ses_info = node_to_item(node, struct sound_trigger_info , list);
    190         if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
    191             in->pcm = st_ses_info->st_ses.pcm;
    192             in->config = st_ses_info->st_ses.config;
    193             in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
    194             in->is_st_session = true;
    195             in->is_st_session_active = true;
    196             ALOGD("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
    197             break;
    198         }
    199     }
    200     pthread_mutex_unlock(&st_dev->lock);
    201 }
    202 
    203 void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
    204                                      st_event_type_t event)
    205 {
    206     int device_type = -1;
    207 
    208     if (!st_dev)
    209        return;
    210 
    211     if (snd_device >= SND_DEVICE_OUT_BEGIN &&
    212         snd_device < SND_DEVICE_OUT_END)
    213         device_type = PCM_PLAYBACK;
    214     else if (snd_device >= SND_DEVICE_IN_BEGIN &&
    215         snd_device < SND_DEVICE_IN_END)
    216         device_type = PCM_CAPTURE;
    217     else {
    218         ALOGE("%s: invalid device 0x%x, for event %d",
    219                            __func__, snd_device, event);
    220         return;
    221     }
    222 
    223     ALOGI("%s: device 0x%x of type %d for Event %d",
    224         __func__, snd_device, device_type, event);
    225     if (device_type == PCM_CAPTURE) {
    226         switch(event) {
    227         case ST_EVENT_SND_DEVICE_FREE:
    228             st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
    229             break;
    230         case ST_EVENT_SND_DEVICE_BUSY:
    231             st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
    232             break;
    233         default:
    234             ALOGW("%s:invalid event %d for device 0x%x",
    235                                   __func__, event, snd_device);
    236         }
    237     }/*Events for output device, if required can be placed here in else*/
    238 }
    239 
    240 void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
    241                                struct str_parms *params)
    242 {
    243     audio_event_info_t event;
    244     char value[32];
    245     int ret, val;
    246 
    247     if(!st_dev || !params) {
    248         ALOGE("%s: str_params NULL", __func__);
    249         return;
    250     }
    251 
    252     ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
    253                             sizeof(value));
    254     if (ret > 0) {
    255         if (strstr(value, "OFFLINE")) {
    256             event.u.status = SND_CARD_STATUS_OFFLINE;
    257             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    258         }
    259         else if (strstr(value, "ONLINE")) {
    260             event.u.status = SND_CARD_STATUS_ONLINE;
    261             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    262         }
    263         else
    264             ALOGE("%s: unknown snd_card_status", __func__);
    265     }
    266 
    267     ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
    268     if (ret > 0) {
    269         if (strstr(value, "OFFLINE")) {
    270             event.u.status = CPE_STATUS_OFFLINE;
    271             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    272         }
    273         else if (strstr(value, "ONLINE")) {
    274             event.u.status = CPE_STATUS_ONLINE;
    275             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
    276         }
    277         else
    278             ALOGE("%s: unknown CPE status", __func__);
    279     }
    280 
    281     ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
    282     if (ret >= 0) {
    283         event.u.value = val;
    284         st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
    285     }
    286 }
    287 
    288 int audio_extn_sound_trigger_init(struct audio_device *adev)
    289 {
    290     int status = 0;
    291     char sound_trigger_lib[100];
    292     void *lib_handle;
    293 
    294     ALOGI("%s: Enter", __func__);
    295 
    296     st_dev = (struct sound_trigger_audio_device*)
    297                         calloc(1, sizeof(struct sound_trigger_audio_device));
    298     if (!st_dev) {
    299         ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
    300         return -ENOMEM;
    301     }
    302 
    303     snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
    304              "/system/vendor/lib/hw/sound_trigger.primary.%s.so",
    305               XSTR(SOUND_TRIGGER_PLATFORM_NAME));
    306 
    307     st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
    308 
    309     if (st_dev->lib_handle == NULL) {
    310         ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
    311                 dlerror());
    312         status = -EINVAL;
    313         goto cleanup;
    314     }
    315     ALOGI("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
    316 
    317     st_dev->st_callback = (sound_trigger_hw_call_back_t)
    318               dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
    319 
    320     if (st_dev->st_callback == NULL) {
    321        ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
    322                dlerror());
    323        goto cleanup;
    324     }
    325 
    326     st_dev->adev = adev;
    327     list_init(&st_dev->st_ses_list);
    328 
    329     return 0;
    330 
    331 cleanup:
    332     if (st_dev->lib_handle)
    333         dlclose(st_dev->lib_handle);
    334     free(st_dev);
    335     st_dev = NULL;
    336     return status;
    337 
    338 }
    339 
    340 void audio_extn_sound_trigger_deinit(struct audio_device *adev)
    341 {
    342     ALOGI("%s: Enter", __func__);
    343     if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
    344         dlclose(st_dev->lib_handle);
    345         free(st_dev);
    346         st_dev = NULL;
    347     }
    348 }
    349