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 #ifdef __LP64__ 35 #define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib64/hw/sound_trigger.primary.%s.so" 36 #else 37 #define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib/hw/sound_trigger.primary.%s.so" 38 #endif 39 40 struct sound_trigger_info { 41 struct sound_trigger_session_info st_ses; 42 bool lab_stopped; 43 struct listnode list; 44 }; 45 46 struct sound_trigger_audio_device { 47 void *lib_handle; 48 struct audio_device *adev; 49 sound_trigger_hw_call_back_t st_callback; 50 struct listnode st_ses_list; 51 pthread_mutex_t lock; 52 }; 53 54 static struct sound_trigger_audio_device *st_dev; 55 56 static struct sound_trigger_info * 57 get_sound_trigger_info(int capture_handle) 58 { 59 struct sound_trigger_info *st_ses_info = NULL; 60 struct listnode *node; 61 ALOGV("%s: list %d capture_handle %d", __func__, 62 list_empty(&st_dev->st_ses_list), capture_handle); 63 list_for_each(node, &st_dev->st_ses_list) { 64 st_ses_info = node_to_item(node, struct sound_trigger_info , list); 65 if (st_ses_info->st_ses.capture_handle == capture_handle) 66 return st_ses_info; 67 } 68 return NULL; 69 } 70 71 int audio_hw_call_back(sound_trigger_event_type_t event, 72 sound_trigger_event_info_t* config) 73 { 74 int status = 0; 75 struct sound_trigger_info *st_ses_info; 76 77 if (!st_dev) 78 return -EINVAL; 79 80 pthread_mutex_lock(&st_dev->lock); 81 switch (event) { 82 case ST_EVENT_SESSION_REGISTER: 83 if (!config) { 84 ALOGE("%s: NULL config", __func__); 85 status = -EINVAL; 86 break; 87 } 88 st_ses_info= calloc(1, sizeof(struct sound_trigger_info )); 89 if (!st_ses_info) { 90 ALOGE("%s: st_ses_info alloc failed", __func__); 91 status = -ENOMEM; 92 break; 93 } 94 memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses)); 95 ALOGV("%s: add capture_handle %d pcm %p", __func__, 96 st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm); 97 list_add_tail(&st_dev->st_ses_list, &st_ses_info->list); 98 break; 99 100 case ST_EVENT_SESSION_DEREGISTER: 101 if (!config) { 102 ALOGE("%s: NULL config", __func__); 103 status = -EINVAL; 104 break; 105 } 106 st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle); 107 if (!st_ses_info) { 108 ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm); 109 status = -EINVAL; 110 break; 111 } 112 ALOGV("%s: remove capture_handle %d pcm %p", __func__, 113 st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm); 114 list_remove(&st_ses_info->list); 115 free(st_ses_info); 116 break; 117 default: 118 ALOGW("%s: Unknown event %d", __func__, event); 119 break; 120 } 121 pthread_mutex_unlock(&st_dev->lock); 122 return status; 123 } 124 125 int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer, 126 size_t bytes) 127 { 128 int ret = -1; 129 struct sound_trigger_info *st_info = NULL; 130 audio_event_info_t event; 131 132 if (!st_dev) 133 return ret; 134 135 if (!in->is_st_session_active) { 136 ALOGE(" %s: Sound trigger is not active", __func__); 137 goto exit; 138 } 139 if (in->standby) 140 in->standby = false; 141 142 pthread_mutex_lock(&st_dev->lock); 143 st_info = get_sound_trigger_info(in->capture_handle); 144 pthread_mutex_unlock(&st_dev->lock); 145 if (st_info) { 146 event.u.aud_info.ses_info = &st_info->st_ses; 147 event.u.aud_info.buf = buffer; 148 event.u.aud_info.num_bytes = bytes; 149 ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event); 150 } 151 152 exit: 153 if (ret) { 154 if (-ENETRESET == ret) 155 in->is_st_session_active = false; 156 memset(buffer, 0, bytes); 157 ALOGV("%s: read failed status %d - sleep", __func__, ret); 158 usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) * 159 in->config.rate)); 160 } 161 return ret; 162 } 163 164 void audio_extn_sound_trigger_stop_lab(struct stream_in *in) 165 { 166 int status = 0; 167 struct sound_trigger_info *st_ses_info = NULL; 168 audio_event_info_t event; 169 170 if (!st_dev || !in) 171 return; 172 173 pthread_mutex_lock(&st_dev->lock); 174 st_ses_info = get_sound_trigger_info(in->capture_handle); 175 pthread_mutex_unlock(&st_dev->lock); 176 if (st_ses_info) { 177 event.u.ses_info = st_ses_info->st_ses; 178 ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm); 179 st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event); 180 } 181 } 182 void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in) 183 { 184 struct sound_trigger_info *st_ses_info = NULL; 185 struct listnode *node; 186 187 if (!st_dev || !in) 188 return; 189 190 pthread_mutex_lock(&st_dev->lock); 191 in->is_st_session = false; 192 ALOGV("%s: list %d capture_handle %d", __func__, 193 list_empty(&st_dev->st_ses_list), in->capture_handle); 194 list_for_each(node, &st_dev->st_ses_list) { 195 st_ses_info = node_to_item(node, struct sound_trigger_info , list); 196 if (st_ses_info->st_ses.capture_handle == in->capture_handle) { 197 in->pcm = st_ses_info->st_ses.pcm; 198 in->config = st_ses_info->st_ses.config; 199 in->channel_mask = audio_channel_in_mask_from_count(in->config.channels); 200 in->is_st_session = true; 201 in->is_st_session_active = true; 202 ALOGV("%s: capture_handle %d is sound trigger", __func__, in->capture_handle); 203 break; 204 } 205 } 206 pthread_mutex_unlock(&st_dev->lock); 207 } 208 209 void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device, 210 st_event_type_t event) 211 { 212 int device_type = -1; 213 214 if (!st_dev) 215 return; 216 217 if (snd_device >= SND_DEVICE_OUT_BEGIN && 218 snd_device < SND_DEVICE_OUT_END) { 219 device_type = PCM_PLAYBACK; 220 } else if (snd_device >= SND_DEVICE_IN_BEGIN && 221 snd_device < SND_DEVICE_IN_END) { 222 if (snd_device == SND_DEVICE_IN_CAPTURE_VI_FEEDBACK) 223 return; 224 device_type = PCM_CAPTURE; 225 } else { 226 ALOGE("%s: invalid device 0x%x, for event %d", 227 __func__, snd_device, event); 228 return; 229 } 230 231 ALOGV("%s: device 0x%x of type %d for Event %d", 232 __func__, snd_device, device_type, event); 233 if (device_type == PCM_CAPTURE) { 234 switch(event) { 235 case ST_EVENT_SND_DEVICE_FREE: 236 st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL); 237 break; 238 case ST_EVENT_SND_DEVICE_BUSY: 239 st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL); 240 break; 241 default: 242 ALOGW("%s:invalid event %d for device 0x%x", 243 __func__, event, snd_device); 244 } 245 }/*Events for output device, if required can be placed here in else*/ 246 } 247 248 void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused, 249 struct str_parms *params) 250 { 251 audio_event_info_t event; 252 char value[32]; 253 int ret, val; 254 255 if(!st_dev || !params) { 256 ALOGE("%s: str_params NULL", __func__); 257 return; 258 } 259 260 ret = str_parms_get_str(params, "SND_CARD_STATUS", value, 261 sizeof(value)); 262 if (ret > 0) { 263 if (strstr(value, "OFFLINE")) { 264 event.u.status = SND_CARD_STATUS_OFFLINE; 265 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 266 } 267 else if (strstr(value, "ONLINE")) { 268 event.u.status = SND_CARD_STATUS_ONLINE; 269 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 270 } 271 else 272 ALOGE("%s: unknown snd_card_status", __func__); 273 } 274 275 ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value)); 276 if (ret > 0) { 277 if (strstr(value, "OFFLINE")) { 278 event.u.status = CPE_STATUS_OFFLINE; 279 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 280 } 281 else if (strstr(value, "ONLINE")) { 282 event.u.status = CPE_STATUS_ONLINE; 283 st_dev->st_callback(AUDIO_EVENT_SSR, &event); 284 } 285 else 286 ALOGE("%s: unknown CPE status", __func__); 287 } 288 289 ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val); 290 if (ret >= 0) { 291 event.u.value = val; 292 st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event); 293 } 294 } 295 296 int audio_extn_sound_trigger_init(struct audio_device *adev) 297 { 298 int status = 0; 299 char sound_trigger_lib[100]; 300 void *lib_handle; 301 302 ALOGV("%s: Enter", __func__); 303 304 st_dev = (struct sound_trigger_audio_device*) 305 calloc(1, sizeof(struct sound_trigger_audio_device)); 306 if (!st_dev) { 307 ALOGE("%s: ERROR. sound trigger alloc failed", __func__); 308 return -ENOMEM; 309 } 310 311 snprintf(sound_trigger_lib, sizeof(sound_trigger_lib), 312 SOUND_TRIGGER_LIBRARY_PATH, 313 XSTR(SOUND_TRIGGER_PLATFORM_NAME)); 314 315 st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW); 316 317 if (st_dev->lib_handle == NULL) { 318 ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib, 319 dlerror()); 320 status = -EINVAL; 321 goto cleanup; 322 } 323 ALOGV("%s: DLOPEN successful for %s", __func__, sound_trigger_lib); 324 325 st_dev->st_callback = (sound_trigger_hw_call_back_t) 326 dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back"); 327 328 if (st_dev->st_callback == NULL) { 329 ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__, 330 dlerror()); 331 goto cleanup; 332 } 333 334 st_dev->adev = adev; 335 list_init(&st_dev->st_ses_list); 336 337 return 0; 338 339 cleanup: 340 if (st_dev->lib_handle) 341 dlclose(st_dev->lib_handle); 342 free(st_dev); 343 st_dev = NULL; 344 return status; 345 346 } 347 348 void audio_extn_sound_trigger_deinit(struct audio_device *adev) 349 { 350 ALOGV("%s: Enter", __func__); 351 if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) { 352 dlclose(st_dev->lib_handle); 353 free(st_dev); 354 st_dev = NULL; 355 } 356 } 357