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 *
get_sound_trigger_info(int capture_handle)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
audio_hw_call_back(sound_trigger_event_type_t event,sound_trigger_event_info_t * config)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
audio_extn_sound_trigger_read(struct stream_in * in,void * buffer,size_t bytes)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
audio_extn_sound_trigger_stop_lab(struct stream_in * in)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 }
audio_extn_sound_trigger_check_and_get_session(struct stream_in * in)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
audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,st_event_type_t event)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
audio_extn_sound_trigger_set_parameters(struct audio_device * adev __unused,struct str_parms * params)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
audio_extn_sound_trigger_init(struct audio_device * adev)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
audio_extn_sound_trigger_deinit(struct audio_device * adev)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