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