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
17 #define LOG_TAG "sound_trigger_hw_dragon"
18 /*#define LOG_NDEBUG 0*/
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <malloc.h>
23 #include <poll.h>
24 #include <pthread.h>
25 #include <sys/ioctl.h>
26 #include <sys/prctl.h>
27 #include <cutils/log.h>
28 #include <cutils/uevent.h>
29
30 #include <hardware/hardware.h>
31 #include <system/sound_trigger.h>
32 #include <hardware/sound_trigger.h>
33 #include <tinyalsa/asoundlib.h>
34
35 #define DRAGON_MIXER_VAD 0
36 #define DRAGON_MIC_CTRL "Int Mic Switch"
37 #define DRAGON_HOTWORD_MODEL_CTRL "Hotword Model"
38 #define UEVENT_MSG_LEN 1024
39
40 #define DRAGON_ST_CARD_NUM 0
41 #define DRAGON_ST_DEV_NUM 87
42 #define DRAGON_VAD_DEV "/dev/snd/pcmC0D87c"
43
44 static const struct sound_trigger_properties hw_properties = {
45 "The Android Open Source Project", // implementor
46 "Dragon OK Google ", // description
47 1, // version
48 { 0xe780f240, 0xf034, 0x11e3, 0xb79a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // uuid
49 1, // max_sound_models
50 1, // max_key_phrases
51 1, // max_users
52 RECOGNITION_MODE_VOICE_TRIGGER, // recognition_modes
53 true, // capture_transition
54 0, // max_capture_ms
55 false, // concurrent_capture
56 false, // trigger_in_event
57 0 // power_consumption_mw
58 };
59
60 struct dragon_sound_trigger_device {
61 struct sound_trigger_hw_device device;
62 sound_model_handle_t model_handle;
63 recognition_callback_t recognition_callback;
64 void *recognition_cookie;
65 sound_model_callback_t sound_model_callback;
66 void *sound_model_cookie;
67 pthread_t callback_thread;
68 pthread_mutex_t lock;
69 int send_sock;
70 int term_sock;
71 struct mixer *mixer;
72 struct mixer_ctl *int_mic_sw;
73 struct mixer_ctl *hotword_model;
74 struct sound_trigger_recognition_config *config;
75 struct pcm *pcm;
76 int is_streaming;
77 int opened;
78 };
79
80 struct rt_codec_cmd {
81 size_t number;
82 int *buf;
83 };
84
85 enum {
86 RT_READ_CODEC_DSP_IOCTL = _IOR('R', 0x04, struct rt_codec_cmd),
87 RT_WRITE_CODEC_DSP_IOCTL = _IOW('R', 0x04, struct rt_codec_cmd),
88 };
89
90 // Since there's only ever one sound_trigger_device, keep it as a global so that other people can
91 // dlopen this lib to get at the streaming audio.
92 static struct dragon_sound_trigger_device g_stdev = { .lock = PTHREAD_MUTEX_INITIALIZER };
93
stdev_dsp_set_power(struct dragon_sound_trigger_device * stdev,int val)94 static void stdev_dsp_set_power(struct dragon_sound_trigger_device *stdev,
95 int val)
96 {
97 stdev->is_streaming = 0;
98 mixer_ctl_set_value(stdev->int_mic_sw, 0, val);
99 }
100
stdev_init_mixer(struct dragon_sound_trigger_device * stdev)101 static int stdev_init_mixer(struct dragon_sound_trigger_device *stdev)
102 {
103 int ret = -1;
104
105
106 stdev->mixer = mixer_open(DRAGON_MIXER_VAD);
107 if (!stdev->mixer)
108 goto err;
109
110 stdev->int_mic_sw = mixer_get_ctl_by_name(stdev->mixer, DRAGON_MIC_CTRL);
111 if (!stdev->int_mic_sw)
112 goto err;
113
114 stdev->hotword_model = mixer_get_ctl_by_name(stdev->mixer,
115 DRAGON_HOTWORD_MODEL_CTRL);
116 if (!stdev->hotword_model)
117 ALOGE("No hotword model mixer control\n");
118
119 stdev_dsp_set_power(stdev, 0); // Disable DSP at the beginning
120
121 return 0;
122
123 err:
124 if (stdev->mixer)
125 mixer_close(stdev->mixer);
126 return ret;
127 }
128
stdev_close_term_sock(struct dragon_sound_trigger_device * stdev)129 static void stdev_close_term_sock(struct dragon_sound_trigger_device *stdev)
130 {
131 if (stdev->send_sock >=0) {
132 close(stdev->send_sock);
133 stdev->send_sock = -1;
134 }
135 if (stdev->term_sock >=0) {
136 close(stdev->term_sock);
137 stdev->term_sock = -1;
138 }
139 }
140
stdev_close_mixer(struct dragon_sound_trigger_device * stdev)141 static void stdev_close_mixer(struct dragon_sound_trigger_device *stdev)
142 {
143 if (stdev) {
144 stdev_dsp_set_power(stdev, 0);
145 mixer_close(stdev->mixer);
146 stdev_close_term_sock(stdev);
147 }
148 }
149
vad_load_sound_model(struct dragon_sound_trigger_device * stdev,char * buf,size_t len)150 static int vad_load_sound_model(struct dragon_sound_trigger_device *stdev,
151 char *buf, size_t len)
152 {
153 int ret;
154 ret = mixer_ctl_set_array(stdev->hotword_model, buf, len);
155 if (ret)
156 ALOGE("Failed hotword model write %d\n", ret);
157 return ret;
158 }
159
sound_trigger_event_alloc(struct dragon_sound_trigger_device * stdev)160 static char *sound_trigger_event_alloc(struct dragon_sound_trigger_device *
161 stdev)
162 {
163 char *data;
164 struct sound_trigger_phrase_recognition_event *event;
165
166 data = (char *)calloc(1,
167 sizeof(struct sound_trigger_phrase_recognition_event));
168 if (!data)
169 return NULL;
170
171 event = (struct sound_trigger_phrase_recognition_event *)data;
172 event->common.status = RECOGNITION_STATUS_SUCCESS;
173 event->common.type = SOUND_MODEL_TYPE_KEYPHRASE;
174 event->common.model = stdev->model_handle;
175
176 if (stdev->config) {
177 unsigned int i;
178
179 event->num_phrases = stdev->config->num_phrases;
180 if (event->num_phrases > SOUND_TRIGGER_MAX_PHRASES)
181 event->num_phrases = SOUND_TRIGGER_MAX_PHRASES;
182 for (i=0; i < event->num_phrases; i++)
183 memcpy(&event->phrase_extras[i], &stdev->config->phrases[i],
184 sizeof(struct sound_trigger_phrase_recognition_extra));
185 }
186
187 event->num_phrases = 1;
188 event->phrase_extras[0].confidence_level = 100;
189 event->phrase_extras[0].num_levels = 1;
190 event->phrase_extras[0].levels[0].level = 100;
191 event->phrase_extras[0].levels[0].user_id = 0;
192 // Signify that all the data is comming through streaming, not through the
193 // buffer.
194 event->common.capture_available = true;
195
196 event->common.audio_config = AUDIO_CONFIG_INITIALIZER;
197 event->common.audio_config.sample_rate = 16000;
198 event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO;
199 event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
200
201 return data;
202 }
203
callback_thread_loop(void * context)204 static void *callback_thread_loop(void *context)
205 {
206 char msg[UEVENT_MSG_LEN];
207 struct dragon_sound_trigger_device *stdev =
208 (struct dragon_sound_trigger_device *)context;
209 struct pollfd fds[2];
210 struct pcm_config config;
211 int exit_sockets[2];
212 int err = 0;
213 int i, n;
214
215 ALOGI("%s", __func__);
216 prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0);
217
218 pthread_mutex_lock(&stdev->lock);
219 if (stdev->recognition_callback == NULL)
220 goto exit;
221
222 if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1)
223 goto exit;
224
225 stdev_close_term_sock(stdev);
226 stdev->send_sock = exit_sockets[0];
227 stdev->term_sock = exit_sockets[1];
228
229 stdev_dsp_set_power(stdev, 1);
230
231 config.channels = 1;
232 config.rate = 16000;
233 config.period_size = 240;
234 config.period_count = 8;
235 config.format = PCM_FORMAT_S16_LE;
236 config.start_threshold = 0;
237 config.stop_threshold = 0;
238 config.silence_threshold = 0;
239 stdev->pcm = pcm_open(DRAGON_ST_CARD_NUM, DRAGON_ST_DEV_NUM,
240 PCM_IN | PCM_MMAP, &config);
241 if (!stdev->pcm || !pcm_is_ready(stdev->pcm)) {
242 ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(stdev->pcm));
243 err = -1;
244 goto exit;
245 }
246 err = pcm_start(stdev->pcm);
247 if (err) {
248 ALOGE("Unable to start PCM device (%s)\n", pcm_get_error(stdev->pcm));
249 goto exit;
250 }
251
252 memset(fds, 0, 2 * sizeof(struct pollfd));
253 fds[0].events = POLLIN;
254 fds[0].fd = pcm_get_poll_fd(stdev->pcm);;
255 if (fds[0].fd == -1) {
256 ALOGE("Error opening socket for hotplug uevent");
257 goto exit;
258 }
259 fds[1].events = POLLIN;
260 fds[1].fd = stdev->term_sock;
261
262 pthread_mutex_unlock(&stdev->lock);
263
264 while (1) {
265 err = poll(fds, 2, -1);
266 pthread_mutex_lock(&stdev->lock);
267 if ((err < 0) || (stdev->recognition_callback == NULL)) {
268 ALOGE_IF(err < 0, "Error in hotplug CPU poll: %d", errno);
269 break;
270 }
271
272 if (fds[0].revents & POLLIN) {
273 struct sound_trigger_phrase_recognition_event *event;
274 recognition_callback_t callback = stdev->recognition_callback;
275 void *cookie = stdev->recognition_cookie;
276
277 event = (struct sound_trigger_phrase_recognition_event *)
278 sound_trigger_event_alloc(stdev);
279 if (!event) {
280 pcm_close(stdev->pcm);
281 stdev->pcm = NULL;
282 goto exit;
283 }
284 stdev->is_streaming = 1;
285 ALOGI("%s send callback model %d", __func__,
286 stdev->model_handle);
287 pthread_mutex_unlock(&stdev->lock);
288 if (callback != NULL) {
289 callback(&event->common, cookie);
290 }
291 pthread_mutex_lock(&stdev->lock);
292 free(event);
293 /* Leave the device open for streaming. */
294 goto exit;
295 } else if (fds[1].revents & POLLIN) {
296 read(fds[1].fd, &n, sizeof(n)); /* clear the socket */
297 ALOGI("%s: Termination message", __func__);
298 break;
299 } else {
300 ALOGI("%s: Message to ignore", __func__);
301 }
302 pthread_mutex_unlock(&stdev->lock);
303 }
304
305 if (stdev->pcm) {
306 pcm_close(stdev->pcm);
307 stdev->pcm = NULL;
308 }
309
310 exit:
311 stdev->recognition_callback = NULL;
312 stdev_close_term_sock(stdev);
313
314 if (stdev->config && !stdev->config->capture_requested)
315 stdev_dsp_set_power(stdev, 0);
316
317 pthread_mutex_unlock(&stdev->lock);
318
319 return (void *)(long)err;
320 }
321
stdev_get_properties(const struct sound_trigger_hw_device * dev,struct sound_trigger_properties * properties)322 static int stdev_get_properties(const struct sound_trigger_hw_device *dev,
323 struct sound_trigger_properties *properties)
324 {
325 struct dragon_sound_trigger_device *stdev =
326 (struct dragon_sound_trigger_device *)dev;
327
328 ALOGI("%s", __func__);
329 if (properties == NULL)
330 return -EINVAL;
331 memcpy(properties, &hw_properties, sizeof(struct sound_trigger_properties));
332 return 0;
333 }
334
stdev_load_sound_model(const struct sound_trigger_hw_device * dev,struct sound_trigger_sound_model * sound_model,sound_model_callback_t callback,void * cookie,sound_model_handle_t * handle)335 static int stdev_load_sound_model(const struct sound_trigger_hw_device *dev,
336 struct sound_trigger_sound_model *sound_model,
337 sound_model_callback_t callback,
338 void *cookie,
339 sound_model_handle_t *handle)
340 {
341 struct dragon_sound_trigger_device *stdev =
342 (struct dragon_sound_trigger_device *)dev;
343 int ret = 0;
344
345 ALOGI("%s", __func__);
346 pthread_mutex_lock(&stdev->lock);
347 if (handle == NULL || sound_model == NULL) {
348 ret = -EINVAL;
349 goto exit;
350 }
351
352 if (stdev->model_handle == 1) {
353 ret = -ENOSYS;
354 goto exit;
355 }
356
357 ret = vad_load_sound_model(stdev,
358 (char *)sound_model + sound_model->data_offset,
359 sound_model->data_size);
360 if (ret)
361 goto exit;
362
363 stdev->model_handle = 1;
364 stdev->sound_model_callback = callback;
365 stdev->sound_model_cookie = cookie;
366 *handle = stdev->model_handle;
367
368 exit:
369 pthread_mutex_unlock(&stdev->lock);
370 return ret;
371 }
372
stdev_unload_sound_model(const struct sound_trigger_hw_device * dev,sound_model_handle_t handle)373 static int stdev_unload_sound_model(const struct sound_trigger_hw_device *dev,
374 sound_model_handle_t handle)
375 {
376 struct dragon_sound_trigger_device *stdev =
377 (struct dragon_sound_trigger_device *)dev;
378 int status = 0;
379
380 ALOGI("%s handle %d", __func__, handle);
381 pthread_mutex_lock(&stdev->lock);
382 if (handle != 1) {
383 status = -EINVAL;
384 goto exit;
385 }
386 if (stdev->model_handle == 0) {
387 status = -ENOSYS;
388 goto exit;
389 }
390 stdev->model_handle = 0;
391 free(stdev->config);
392 stdev->config = NULL;
393 if (stdev->recognition_callback != NULL) {
394 stdev->recognition_callback = NULL;
395 if (stdev->send_sock >=0)
396 write(stdev->send_sock, "T", 1);
397 pthread_mutex_unlock(&stdev->lock);
398
399 pthread_join(stdev->callback_thread, (void **)NULL);
400
401 pthread_mutex_lock(&stdev->lock);
402 }
403
404 exit:
405 stdev_dsp_set_power(stdev, 0);
406
407 pthread_mutex_unlock(&stdev->lock);
408 return status;
409 }
410
stdev_start_recognition(const struct sound_trigger_hw_device * dev,sound_model_handle_t sound_model_handle,const struct sound_trigger_recognition_config * config,recognition_callback_t callback,void * cookie)411 static int stdev_start_recognition(const struct sound_trigger_hw_device *dev,
412 sound_model_handle_t sound_model_handle,
413 const struct sound_trigger_recognition_config *config,
414 recognition_callback_t callback,
415 void *cookie)
416 {
417 struct dragon_sound_trigger_device *stdev =
418 (struct dragon_sound_trigger_device *)dev;
419 int status = 0;
420
421 ALOGI("%s sound model %d", __func__, sound_model_handle);
422 pthread_mutex_lock(&stdev->lock);
423 if (stdev->model_handle != sound_model_handle) {
424 status = -ENOSYS;
425 goto exit;
426 }
427 if (stdev->recognition_callback != NULL) {
428 status = -ENOSYS;
429 goto exit;
430 }
431
432 free(stdev->config);
433 stdev->config = NULL;
434 if (config) {
435 stdev->config = malloc(sizeof(*config));
436 if (!stdev->config) {
437 status = -ENOMEM;
438 goto exit;
439 }
440 memcpy(stdev->config, config, sizeof(*config));
441 }
442
443 if (stdev->pcm) {
444 pcm_close(stdev->pcm);
445 stdev->pcm = NULL;
446 }
447
448 stdev_dsp_set_power(stdev, 0);
449
450 stdev->recognition_callback = callback;
451 stdev->recognition_cookie = cookie;
452 pthread_create(&stdev->callback_thread, (const pthread_attr_t *) NULL,
453 callback_thread_loop, stdev);
454 exit:
455 pthread_mutex_unlock(&stdev->lock);
456 return status;
457 }
458
stdev_stop_recognition(const struct sound_trigger_hw_device * dev,sound_model_handle_t sound_model_handle)459 static int stdev_stop_recognition(const struct sound_trigger_hw_device *dev,
460 sound_model_handle_t sound_model_handle)
461 {
462 struct dragon_sound_trigger_device *stdev =
463 (struct dragon_sound_trigger_device *)dev;
464 int status = 0;
465
466 ALOGI("%s sound model %d", __func__, sound_model_handle);
467 pthread_mutex_lock(&stdev->lock);
468 if (stdev->model_handle != sound_model_handle) {
469 status = -ENOSYS;
470 goto exit;
471 }
472 if (stdev->recognition_callback == NULL) {
473 status = -ENOSYS;
474 goto exit;
475 }
476 free(stdev->config);
477 stdev->config = NULL;
478 stdev->recognition_callback = NULL;
479 if (stdev->send_sock >=0)
480 write(stdev->send_sock, "T", 1);
481 pthread_mutex_unlock(&stdev->lock);
482
483 pthread_join(stdev->callback_thread, (void **)NULL);
484
485 if (stdev->pcm) {
486 pcm_close(stdev->pcm);
487 stdev->pcm = NULL;
488 }
489
490 pthread_mutex_lock(&stdev->lock);
491
492 exit:
493 stdev_dsp_set_power(stdev, 0);
494
495 pthread_mutex_unlock(&stdev->lock);
496 return status;
497 }
498
499 __attribute__ ((visibility ("default")))
sound_trigger_open_for_streaming()500 int sound_trigger_open_for_streaming()
501 {
502 struct dragon_sound_trigger_device *stdev = &g_stdev;
503 int ret = 0;
504
505 pthread_mutex_lock(&stdev->lock);
506
507 if (!stdev->opened) {
508 ALOGE("%s: stdev has not been opened", __func__);
509 ret = -EFAULT;
510 goto exit;
511 }
512 if (!stdev->is_streaming) {
513 ALOGE("%s: DSP is not currently streaming", __func__);
514 ret = -EBUSY;
515 goto exit;
516 }
517 if (!stdev->pcm) {
518 ALOGE("%s: PCM is not open", __func__);
519 ret = -EINVAL;
520 goto exit;
521 }
522 // TODO: Probably want to get something from whoever called us to bind to it/assert that it's a
523 // valid connection. Perhaps returning a more
524 // meaningful handle would be a good idea as well.
525 ret = 1;
526 exit:
527 pthread_mutex_unlock(&stdev->lock);
528 return ret;
529 }
530
531 __attribute__ ((visibility ("default")))
sound_trigger_read_samples(int audio_handle,void * buffer,size_t buffer_len)532 size_t sound_trigger_read_samples(int audio_handle, void *buffer, size_t buffer_len)
533 {
534 struct dragon_sound_trigger_device *stdev = &g_stdev;
535 int i;
536 int ret = 0;
537 int frames;
538 int orig_frames;
539 unsigned int offset;
540 unsigned int bytes_mmapped;
541 int16_t* snd_buffer;
542 unsigned int retry_attempts = 0;
543
544 if (audio_handle <= 0) {
545 ALOGE("%s: invalid audio handle", __func__);
546 return -EINVAL;
547 }
548
549 pthread_mutex_lock(&stdev->lock);
550
551 if (!stdev->opened) {
552 ALOGE("%s: stdev has not been opened", __func__);
553 ret = -EFAULT;
554 goto exit;
555 }
556 if (!stdev->is_streaming) {
557 ALOGE("%s: DSP is not currently streaming", __func__);
558 ret = -EINVAL;
559 goto exit;
560 }
561 if (!stdev->pcm) {
562 ALOGE("%s: PCM has closed", __func__);
563 ret = -EINVAL;
564 goto exit;
565 }
566
567
568 read_again:
569 frames = pcm_mmap_avail(stdev->pcm);
570 if (frames < 0) {
571 ALOGE("%s: DSP mmap error %d", __func__, frames);
572 ret = frames;
573 goto exit;
574 }
575 if (frames == 0) {
576 // ALOGE("%s: DSP mmap retry %d", __func__, frames);
577 retry_attempts++;
578 if (retry_attempts > 10)
579 goto exit;
580 usleep(35000);
581 goto read_again;
582 }
583
584 if (frames > buffer_len / 2)
585 frames = buffer_len / 2;
586
587 orig_frames = frames;
588
589 ret = pcm_mmap_begin(stdev->pcm, (void**)&snd_buffer, &offset, &frames);
590 if (ret < 0) {
591 ALOGE("Failed to mmap hotword buffer %d", ret);
592 goto exit;
593 }
594 memcpy(buffer, snd_buffer + offset, frames * 2);
595 pcm_mmap_commit(stdev->pcm, offset, frames);
596 ret = frames * 2;
597 ALOGV("%s: Sent %u frames to buffer", __func__, frames);
598
599 exit:
600 pthread_mutex_unlock(&stdev->lock);
601 return ret;
602 }
603
604 __attribute__ ((visibility ("default")))
sound_trigger_close_for_streaming(int audio_handle)605 int sound_trigger_close_for_streaming(int audio_handle)
606 {
607 struct dragon_sound_trigger_device *stdev = &g_stdev;
608
609 if (audio_handle <= 0) {
610 ALOGE("%s: invalid audio handle", __func__);
611 return -EINVAL;
612 }
613
614 if (stdev->pcm) {
615 pcm_close(stdev->pcm);
616 stdev->pcm = NULL;
617 }
618
619 return 0;
620 }
621
stdev_close(hw_device_t * device)622 static int stdev_close(hw_device_t *device)
623 {
624 struct dragon_sound_trigger_device *stdev =
625 (struct dragon_sound_trigger_device *)device;
626 int ret = 0;
627
628 pthread_mutex_lock(&stdev->lock);
629 if (!stdev->opened) {
630 ALOGE("%s: device already closed", __func__);
631 ret = -EFAULT;
632 goto exit;
633 }
634 stdev_close_mixer(stdev);
635 stdev->model_handle = 0;
636 stdev->send_sock = 0;
637 stdev->term_sock = 0;
638 stdev->opened = false;
639
640 exit:
641 pthread_mutex_unlock(&stdev->lock);
642 return ret;
643 }
644
stdev_open(const hw_module_t * module,const char * name,hw_device_t ** device)645 static int stdev_open(const hw_module_t *module, const char *name,
646 hw_device_t **device)
647 {
648 struct dragon_sound_trigger_device *stdev;
649 int ret;
650
651 if (strcmp(name, SOUND_TRIGGER_HARDWARE_INTERFACE) != 0)
652 return -EINVAL;
653
654 stdev = &g_stdev;
655 pthread_mutex_lock(&stdev->lock);
656
657 if (stdev->opened) {
658 ALOGE("%s: Only one sountrigger can be opened at a time", __func__);
659 ret = -EBUSY;
660 goto exit;
661 }
662
663 ret = stdev_init_mixer(stdev);
664 if (ret) {
665 ALOGE("Error mixer init");
666 goto exit;
667 }
668
669 stdev->device.common.tag = HARDWARE_DEVICE_TAG;
670 stdev->device.common.version = SOUND_TRIGGER_DEVICE_API_VERSION_1_0;
671 stdev->device.common.module = (struct hw_module_t *)module;
672 stdev->device.common.close = stdev_close;
673 stdev->device.get_properties = stdev_get_properties;
674 stdev->device.load_sound_model = stdev_load_sound_model;
675 stdev->device.unload_sound_model = stdev_unload_sound_model;
676 stdev->device.start_recognition = stdev_start_recognition;
677 stdev->device.stop_recognition = stdev_stop_recognition;
678 stdev->send_sock = stdev->term_sock = -1;
679 stdev->opened = true;
680
681 *device = &stdev->device.common; /* same address as stdev */
682 exit:
683 pthread_mutex_unlock(&stdev->lock);
684 return ret;
685 }
686
687 static struct hw_module_methods_t hal_module_methods = {
688 .open = stdev_open,
689 };
690
691 struct sound_trigger_module HAL_MODULE_INFO_SYM = {
692 .common = {
693 .tag = HARDWARE_MODULE_TAG,
694 .module_api_version = SOUND_TRIGGER_MODULE_API_VERSION_1_0,
695 .hal_api_version = HARDWARE_HAL_API_VERSION,
696 .id = SOUND_TRIGGER_HARDWARE_MODULE_ID,
697 .name = "Default sound trigger HAL",
698 .author = "The Android Open Source Project",
699 .methods = &hal_module_methods,
700 },
701 };
702