• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <pthread.h>
25 #include <unistd.h>
26 #include <log/log.h>
27 #include "audio_hw.h"
28 #include "audio_extn.h"
29 #include "platform.h"
30 #include "platform_api.h"
31 
32 /*-------------------- Begin: AHAL-STHAL Interface ---------------------------*/
33 /*
34  * Maintain the proprietary interface between AHAL and STHAL locally to avoid
35  * the compilation dependency of interface header file from STHAL.
36  */
37 
38 #define MAKE_HAL_VERSION(maj, min) ((((maj) & 0xff) << 8) | ((min) & 0xff))
39 #define MAJOR_VERSION(ver) (((ver) & 0xff00) >> 8)
40 #define MINOR_VERSION(ver) ((ver) & 0x00ff)
41 
42 /* Proprietary interface version used for compatibility with STHAL */
43 #define STHAL_PROP_API_VERSION_1_0 MAKE_HAL_VERSION(1, 0)
44 #define STHAL_PROP_API_CURRENT_VERSION STHAL_PROP_API_VERSION_1_0
45 
46 #define ST_EVENT_CONFIG_MAX_STR_VALUE 32
47 
48 typedef enum {
49     ST_EVENT_SESSION_REGISTER,
50     ST_EVENT_SESSION_DEREGISTER
51 } sound_trigger_event_type_t;
52 
53 typedef enum {
54     AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE,
55     AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE,
56     AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE,
57     AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE,
58     AUDIO_EVENT_STOP_LAB,
59     AUDIO_EVENT_SSR,
60     AUDIO_EVENT_NUM_ST_SESSIONS,
61     AUDIO_EVENT_READ_SAMPLES,
62     AUDIO_EVENT_DEVICE_CONNECT,
63     AUDIO_EVENT_DEVICE_DISCONNECT,
64     AUDIO_EVENT_SVA_EXEC_MODE,
65     AUDIO_EVENT_SVA_EXEC_MODE_STATUS,
66     AUDIO_EVENT_CAPTURE_STREAM_INACTIVE,
67     AUDIO_EVENT_CAPTURE_STREAM_ACTIVE,
68 } audio_event_type_t;
69 
70 typedef enum {
71     USECASE_TYPE_PCM_PLAYBACK,
72     USECASE_TYPE_PCM_CAPTURE,
73     USECASE_TYPE_VOICE_CALL,
74     USECASE_TYPE_VOIP_CALL,
75 } audio_stream_usecase_type_t;
76 
77 typedef enum {
78     SND_CARD_STATUS_OFFLINE,
79     SND_CARD_STATUS_ONLINE,
80     CPE_STATUS_OFFLINE,
81     CPE_STATUS_ONLINE,
82     SLPI_STATUS_OFFLINE,
83     SLPI_STATUS_ONLINE,
84 } ssr_event_status_t;
85 
86 struct sound_trigger_session_info {
87     void* p_ses; /* opaque pointer to st_session obj */
88     int capture_handle;
89     struct pcm *pcm;
90     struct pcm_config config;
91 };
92 
93 struct audio_read_samples_info {
94     struct sound_trigger_session_info *ses_info;
95     void *buf;
96     size_t num_bytes;
97 };
98 
99 struct audio_hal_usecase {
100     audio_stream_usecase_type_t type;
101 };
102 
103 struct sound_trigger_event_info {
104     struct sound_trigger_session_info st_ses;
105 };
106 
107 struct audio_event_info {
108     union {
109         ssr_event_status_t status;
110         int value;
111         struct sound_trigger_session_info ses_info;
112         struct audio_read_samples_info aud_info;
113         char str_value[ST_EVENT_CONFIG_MAX_STR_VALUE];
114         struct audio_hal_usecase usecase;
115     }u;
116 };
117 
118 /* STHAL callback which is called by AHAL */
119 typedef int (*sound_trigger_hw_call_back_t)(audio_event_type_t,
120                                   struct audio_event_info*);
121 
122 /*---------------- End: AHAL-STHAL Interface ----------------------------------*/
123 
124 #define XSTR(x) STR(x)
125 #define STR(x) #x
126 
127 #define DLSYM(handle, ptr, symbol, err)                                   \
128 do {                                                                      \
129     ptr = dlsym(handle, #symbol);                                         \
130     if (ptr == NULL) {                                                    \
131         ALOGE("%s: dlsym %s failed . %s", __func__, #symbol, dlerror());  \
132         err = -ENODEV;                                                    \
133     }                                                                     \
134 } while(0)
135 
136 #ifdef __LP64__
137 #define SOUND_TRIGGER_LIBRARY_PATH "/vendor/lib64/hw/sound_trigger.primary.%s.so"
138 #else
139 #define SOUND_TRIGGER_LIBRARY_PATH "/vendor/lib/hw/sound_trigger.primary.%s.so"
140 #endif
141 
142 /*
143  * Current proprietary API version used by AHAL. Queried by STHAL
144  * for compatibility check with AHAL
145  */
146 const unsigned int sthal_prop_api_version = STHAL_PROP_API_CURRENT_VERSION;
147 
148 struct sound_trigger_info  {
149     struct sound_trigger_session_info st_ses;
150     bool lab_stopped;
151     struct listnode list;
152 };
153 
154 struct sound_trigger_audio_device {
155     void *lib_handle;
156     struct audio_device *adev;
157     sound_trigger_hw_call_back_t st_callback;
158     struct listnode st_ses_list;
159     pthread_mutex_t lock;
160     unsigned int sthal_prop_api_version;
161 };
162 
163 static struct sound_trigger_audio_device *st_dev;
164 
165 static struct sound_trigger_info *
get_sound_trigger_info(int capture_handle)166 get_sound_trigger_info(int capture_handle)
167 {
168     struct sound_trigger_info  *st_ses_info = NULL;
169     struct listnode *node;
170     ALOGV("%s: list empty %d capture_handle %d", __func__,
171            list_empty(&st_dev->st_ses_list), capture_handle);
172     list_for_each(node, &st_dev->st_ses_list) {
173         st_ses_info = node_to_item(node, struct sound_trigger_info , list);
174         if (st_ses_info->st_ses.capture_handle == capture_handle)
175             return st_ses_info;
176     }
177     return NULL;
178 }
179 
populate_usecase(struct audio_hal_usecase * usecase,struct audio_usecase * uc_info)180 static int populate_usecase(struct audio_hal_usecase *usecase,
181                        struct audio_usecase *uc_info)
182 {
183     int status  = 0;
184 
185     switch (uc_info->type) {
186     case PCM_PLAYBACK:
187         if (uc_info->id == USECASE_AUDIO_PLAYBACK_VOIP)
188             usecase->type = USECASE_TYPE_VOIP_CALL;
189         else
190             usecase->type = USECASE_TYPE_PCM_PLAYBACK;
191         break;
192 
193     case PCM_CAPTURE:
194         if (uc_info->id == USECASE_AUDIO_RECORD_VOIP)
195             usecase->type = USECASE_TYPE_VOIP_CALL;
196         else
197             usecase->type = USECASE_TYPE_PCM_CAPTURE;
198         break;
199 
200     case VOICE_CALL:
201         usecase->type = USECASE_TYPE_VOICE_CALL;
202         break;
203 
204     default:
205         ALOGE("%s: unsupported usecase type %d", __func__, uc_info->type);
206         status = -EINVAL;
207     }
208     return status;
209 }
210 
stdev_snd_mon_cb(void * stream __unused,struct str_parms * parms)211 static void stdev_snd_mon_cb(void * stream __unused, struct str_parms * parms)
212 {
213     if (!parms)
214         return;
215 
216     audio_extn_sound_trigger_set_parameters(NULL, parms);
217     return;
218 }
219 
audio_hw_call_back(sound_trigger_event_type_t event,struct sound_trigger_event_info * config)220 int audio_hw_call_back(sound_trigger_event_type_t event,
221                        struct sound_trigger_event_info* config)
222 {
223     int status = 0;
224     struct sound_trigger_info  *st_ses_info;
225 
226     if (!st_dev)
227        return -EINVAL;
228 
229     pthread_mutex_lock(&st_dev->lock);
230     switch (event) {
231     case ST_EVENT_SESSION_REGISTER:
232         if (!config) {
233             ALOGE("%s: NULL config", __func__);
234             status = -EINVAL;
235             break;
236         }
237         st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
238         if (!st_ses_info) {
239             ALOGE("%s: st_ses_info alloc failed", __func__);
240             status = -ENOMEM;
241             break;
242         }
243         memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
244         ALOGV("%s: add capture_handle %d pcm %p", __func__,
245               st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
246         list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
247         break;
248 
249     case ST_EVENT_SESSION_DEREGISTER:
250         if (!config) {
251             ALOGE("%s: NULL config", __func__);
252             status = -EINVAL;
253             break;
254         }
255         st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
256         if (!st_ses_info) {
257             ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
258             status = -EINVAL;
259             break;
260         }
261         ALOGV("%s: remove capture_handle %d pcm %p", __func__,
262               st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
263         list_remove(&st_ses_info->list);
264         free(st_ses_info);
265         break;
266     default:
267         ALOGW("%s: Unknown event %d", __func__, event);
268         break;
269     }
270     pthread_mutex_unlock(&st_dev->lock);
271     return status;
272 }
273 
audio_extn_sound_trigger_read(struct stream_in * in,void * buffer,size_t bytes)274 int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
275                        size_t bytes)
276 {
277     int ret = -1;
278     struct sound_trigger_info  *st_info = NULL;
279     struct audio_event_info event;
280 
281     if (!st_dev)
282        return ret;
283 
284     if (!in->is_st_session_active) {
285         ALOGE(" %s: Sound trigger is not active", __func__);
286         goto exit;
287     }
288     if (in->standby)
289         in->standby = false;
290 
291     pthread_mutex_lock(&st_dev->lock);
292     st_info = get_sound_trigger_info(in->capture_handle);
293     pthread_mutex_unlock(&st_dev->lock);
294     if (st_info) {
295         event.u.aud_info.ses_info = &st_info->st_ses;
296         event.u.aud_info.buf = buffer;
297         event.u.aud_info.num_bytes = bytes;
298         ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event);
299     }
300 
301 exit:
302     if (ret) {
303         if (-ENETRESET == ret)
304             in->is_st_session_active = false;
305         memset(buffer, 0, bytes);
306         ALOGV("%s: read failed status %d - sleep", __func__, ret);
307         usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) *
308                                    in->config.rate));
309     }
310     return ret;
311 }
312 
audio_extn_sound_trigger_stop_lab(struct stream_in * in)313 void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
314 {
315     struct sound_trigger_info  *st_ses_info = NULL;
316     struct audio_event_info event;
317 
318     if (!st_dev || !in || !in->is_st_session_active)
319        return;
320 
321     pthread_mutex_lock(&st_dev->lock);
322     st_ses_info = get_sound_trigger_info(in->capture_handle);
323     pthread_mutex_unlock(&st_dev->lock);
324     if (st_ses_info) {
325         event.u.ses_info = st_ses_info->st_ses;
326         ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
327         st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
328         in->is_st_session_active = false;
329     }
330 }
331 
audio_extn_sound_trigger_check_and_get_session(struct stream_in * in)332 void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
333 {
334     struct sound_trigger_info  *st_ses_info = NULL;
335     struct listnode *node;
336 
337     if (!st_dev || !in)
338        return;
339 
340     pthread_mutex_lock(&st_dev->lock);
341     in->is_st_session = false;
342     ALOGV("%s: list %d capture_handle %d", __func__,
343           list_empty(&st_dev->st_ses_list), in->capture_handle);
344     list_for_each(node, &st_dev->st_ses_list) {
345         st_ses_info = node_to_item(node, struct sound_trigger_info , list);
346         if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
347             in->pcm = st_ses_info->st_ses.pcm;
348             in->config = st_ses_info->st_ses.config;
349             in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
350             in->is_st_session = true;
351             in->is_st_session_active = true;
352             ALOGV("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
353             break;
354         }
355     }
356     pthread_mutex_unlock(&st_dev->lock);
357 }
358 
audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,st_event_type_t event)359 void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
360                                      st_event_type_t event)
361 {
362     int device_type = -1;
363 
364     if (!st_dev)
365        return;
366 
367     if (snd_device >= SND_DEVICE_OUT_BEGIN &&
368         snd_device < SND_DEVICE_OUT_END) {
369         device_type = PCM_PLAYBACK;
370     } else if (snd_device >= SND_DEVICE_IN_BEGIN &&
371         snd_device < SND_DEVICE_IN_END) {
372         if (snd_device == SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)
373             return;
374         device_type = PCM_CAPTURE;
375     } else {
376         ALOGE("%s: invalid device 0x%x, for event %d",
377                            __func__, snd_device, event);
378         return;
379     }
380 
381     ALOGV("%s: device 0x%x of type %d for Event %d",
382         __func__, snd_device, device_type, event);
383     if (device_type == PCM_CAPTURE) {
384         switch(event) {
385         case ST_EVENT_SND_DEVICE_FREE:
386             st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
387             break;
388         case ST_EVENT_SND_DEVICE_BUSY:
389             st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
390             break;
391         default:
392             ALOGW("%s:invalid event %d for device 0x%x",
393                                   __func__, event, snd_device);
394         }
395     }/*Events for output device, if required can be placed here in else*/
396 }
397 
audio_extn_sound_trigger_update_stream_status(struct audio_usecase * uc_info,st_event_type_t event)398 void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
399                                      st_event_type_t event)
400 {
401     bool raise_event = false;
402     struct audio_event_info ev_info;
403     audio_event_type_t ev;
404 
405     if (!st_dev)
406        return;
407 
408     if (st_dev->sthal_prop_api_version < STHAL_PROP_API_VERSION_1_0)
409         return;
410 
411     if (uc_info == NULL) {
412         ALOGE("%s: null usecase", __func__);
413         return;
414     }
415 
416     bool valid_type =
417             (uc_info->type == PCM_PLAYBACK &&
418              platform_snd_device_has_speaker(uc_info->out_snd_device)) ||
419             (uc_info->type == PCM_CAPTURE) ||
420             (uc_info->type == VOICE_CALL);
421 
422     if (valid_type && platform_sound_trigger_usecase_needs_event(uc_info->id)) {
423         ALOGV("%s: uc_id %d of type %d for Event %d, with Raise=%d",
424               __func__, uc_info->id, uc_info->type, event, raise_event);
425         if (uc_info->type == PCM_CAPTURE || uc_info->type == VOICE_CALL) {
426             ev = (event == ST_EVENT_STREAM_BUSY) ? AUDIO_EVENT_CAPTURE_STREAM_ACTIVE :
427                                                    AUDIO_EVENT_CAPTURE_STREAM_INACTIVE;
428         } else {
429             ev = (event == ST_EVENT_STREAM_BUSY) ? AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE :
430                                                    AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE;
431         }
432         if (!populate_usecase(&ev_info.u.usecase, uc_info)) {
433             ALOGI("%s: send event %d: usecase id %d, type %d",
434                   __func__, ev, uc_info->id, uc_info->type);
435             st_dev->st_callback(ev, &ev_info);
436         }
437     }
438 }
439 
audio_extn_sound_trigger_set_parameters(struct audio_device * adev __unused,struct str_parms * params)440 void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
441                                struct str_parms *params)
442 {
443     struct audio_event_info event;
444     char value[32];
445     int ret, val;
446 
447     if(!st_dev || !params) {
448         ALOGE("%s: str_params NULL", __func__);
449         return;
450     }
451 
452     ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
453                             sizeof(value));
454     if (ret > 0) {
455         if (strstr(value, "OFFLINE")) {
456             event.u.status = SND_CARD_STATUS_OFFLINE;
457             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
458         }
459         else if (strstr(value, "ONLINE")) {
460             event.u.status = SND_CARD_STATUS_ONLINE;
461             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
462         }
463         else
464             ALOGE("%s: unknown snd_card_status", __func__);
465     }
466 
467     ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
468     if (ret > 0) {
469         if (strstr(value, "OFFLINE")) {
470             event.u.status = CPE_STATUS_OFFLINE;
471             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
472         }
473         else if (strstr(value, "ONLINE")) {
474             event.u.status = CPE_STATUS_ONLINE;
475             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
476         }
477         else
478             ALOGE("%s: unknown CPE status", __func__);
479     }
480 
481     ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
482     if (ret >= 0) {
483         event.u.value = val;
484         st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
485     }
486 
487     ret = str_parms_get_str(params, "SLPI_STATUS", value, sizeof(value));
488     if (ret > 0) {
489         if (strstr(value, "OFFLINE")) {
490             event.u.status = SLPI_STATUS_OFFLINE;
491             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
492         } else if (strstr(value, "ONLINE")) {
493             event.u.status = SLPI_STATUS_ONLINE;
494             st_dev->st_callback(AUDIO_EVENT_SSR, &event);
495         } else {
496             ALOGE("%s: unknown SLPI status", __func__);
497         }
498     }
499 }
500 
audio_extn_sound_trigger_init(struct audio_device * adev)501 int audio_extn_sound_trigger_init(struct audio_device *adev)
502 {
503     int status = 0;
504     char sound_trigger_lib[100];
505     void *sthal_prop_api_version;
506 
507     ALOGV("%s: Enter", __func__);
508 
509     st_dev = (struct sound_trigger_audio_device*)
510                         calloc(1, sizeof(struct sound_trigger_audio_device));
511     if (!st_dev) {
512         ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
513         return -ENOMEM;
514     }
515 
516     snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
517              SOUND_TRIGGER_LIBRARY_PATH,
518               XSTR(SOUND_TRIGGER_PLATFORM_NAME));
519 
520     st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
521 
522     if (st_dev->lib_handle == NULL) {
523         ALOGE("%s: error %s", __func__, dlerror());
524         status = -ENODEV;
525         goto cleanup;
526     }
527     ALOGV("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
528 
529     DLSYM(st_dev->lib_handle, st_dev->st_callback, sound_trigger_hw_call_back,
530           status);
531     if (status)
532         goto cleanup;
533 
534     DLSYM(st_dev->lib_handle, sthal_prop_api_version,
535           sthal_prop_api_version, status);
536     if (status) {
537         st_dev->sthal_prop_api_version = 0;
538         status  = 0; /* passthru for backward compability */
539     } else {
540         st_dev->sthal_prop_api_version = *(int*)sthal_prop_api_version;
541         if (MAJOR_VERSION(st_dev->sthal_prop_api_version) !=
542             MAJOR_VERSION(STHAL_PROP_API_CURRENT_VERSION)) {
543             ALOGE("%s: Incompatible API versions ahal:0x%x != sthal:0x%x",
544                   __func__, STHAL_PROP_API_CURRENT_VERSION,
545                   st_dev->sthal_prop_api_version);
546             goto cleanup;
547         }
548         ALOGD("%s: sthal is using proprietary API version 0x%04x", __func__,
549               st_dev->sthal_prop_api_version);
550     }
551 
552     st_dev->adev = adev;
553     list_init(&st_dev->st_ses_list);
554     audio_extn_snd_mon_register_listener(st_dev, stdev_snd_mon_cb);
555 
556     return 0;
557 
558 cleanup:
559     if (st_dev->lib_handle)
560         dlclose(st_dev->lib_handle);
561     free(st_dev);
562     st_dev = NULL;
563     return status;
564 }
565 
audio_extn_sound_trigger_deinit(struct audio_device * adev)566 void audio_extn_sound_trigger_deinit(struct audio_device *adev)
567 {
568     ALOGV("%s: Enter", __func__);
569     if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
570         audio_extn_snd_mon_unregister_listener(st_dev);
571         dlclose(st_dev->lib_handle);
572         free(st_dev);
573         st_dev = NULL;
574     }
575 }
576