soundtrigger.c revision 188b6223c8baaba6a44c779783a878b4459a0642
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
34struct sound_trigger_info  {
35    struct sound_trigger_session_info st_ses;
36    bool lab_stopped;
37    struct listnode list;
38};
39
40struct 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
48static struct sound_trigger_audio_device *st_dev;
49
50static struct sound_trigger_info *
51get_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
65int 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
119int 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
146exit:
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
158void 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}
176void 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            ALOGV("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
197            break;
198        }
199    }
200    pthread_mutex_unlock(&st_dev->lock);
201}
202
203void 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        if (snd_device == SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)
217            return;
218        device_type = PCM_CAPTURE;
219    } else {
220        ALOGE("%s: invalid device 0x%x, for event %d",
221                           __func__, snd_device, event);
222        return;
223    }
224
225    ALOGV("%s: device 0x%x of type %d for Event %d",
226        __func__, snd_device, device_type, event);
227    if (device_type == PCM_CAPTURE) {
228        switch(event) {
229        case ST_EVENT_SND_DEVICE_FREE:
230            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
231            break;
232        case ST_EVENT_SND_DEVICE_BUSY:
233            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
234            break;
235        default:
236            ALOGW("%s:invalid event %d for device 0x%x",
237                                  __func__, event, snd_device);
238        }
239    }/*Events for output device, if required can be placed here in else*/
240}
241
242void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
243                               struct str_parms *params)
244{
245    audio_event_info_t event;
246    char value[32];
247    int ret, val;
248
249    if(!st_dev || !params) {
250        ALOGE("%s: str_params NULL", __func__);
251        return;
252    }
253
254    ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
255                            sizeof(value));
256    if (ret > 0) {
257        if (strstr(value, "OFFLINE")) {
258            event.u.status = SND_CARD_STATUS_OFFLINE;
259            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
260        }
261        else if (strstr(value, "ONLINE")) {
262            event.u.status = SND_CARD_STATUS_ONLINE;
263            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
264        }
265        else
266            ALOGE("%s: unknown snd_card_status", __func__);
267    }
268
269    ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
270    if (ret > 0) {
271        if (strstr(value, "OFFLINE")) {
272            event.u.status = CPE_STATUS_OFFLINE;
273            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
274        }
275        else if (strstr(value, "ONLINE")) {
276            event.u.status = CPE_STATUS_ONLINE;
277            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
278        }
279        else
280            ALOGE("%s: unknown CPE status", __func__);
281    }
282
283    ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
284    if (ret >= 0) {
285        event.u.value = val;
286        st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
287    }
288}
289
290int audio_extn_sound_trigger_init(struct audio_device *adev)
291{
292    int status = 0;
293    char sound_trigger_lib[100];
294    void *lib_handle;
295
296    ALOGV("%s: Enter", __func__);
297
298    st_dev = (struct sound_trigger_audio_device*)
299                        calloc(1, sizeof(struct sound_trigger_audio_device));
300    if (!st_dev) {
301        ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
302        return -ENOMEM;
303    }
304
305    snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
306             "/system/vendor/lib/hw/sound_trigger.primary.%s.so",
307              XSTR(SOUND_TRIGGER_PLATFORM_NAME));
308
309    st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
310
311    if (st_dev->lib_handle == NULL) {
312        ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
313                dlerror());
314        status = -EINVAL;
315        goto cleanup;
316    }
317    ALOGV("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
318
319    st_dev->st_callback = (sound_trigger_hw_call_back_t)
320              dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
321
322    if (st_dev->st_callback == NULL) {
323       ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
324               dlerror());
325       goto cleanup;
326    }
327
328    st_dev->adev = adev;
329    list_init(&st_dev->st_ses_list);
330
331    return 0;
332
333cleanup:
334    if (st_dev->lib_handle)
335        dlclose(st_dev->lib_handle);
336    free(st_dev);
337    st_dev = NULL;
338    return status;
339
340}
341
342void audio_extn_sound_trigger_deinit(struct audio_device *adev)
343{
344    ALOGV("%s: Enter", __func__);
345    if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
346        dlclose(st_dev->lib_handle);
347        free(st_dev);
348        st_dev = NULL;
349    }
350}
351