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