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     memset(&config, 0, sizeof(config));
232     config.channels = 1;
233     config.rate = 16000;
234     config.period_size = 240;
235     config.period_count = 8;
236     config.format = PCM_FORMAT_S16_LE;
237     config.start_threshold = 0;
238     config.stop_threshold = 0;
239     config.silence_threshold = 0;
240     stdev->pcm = pcm_open(DRAGON_ST_CARD_NUM, DRAGON_ST_DEV_NUM,
241                           PCM_IN | PCM_MMAP, &config);
242     if (!stdev->pcm || !pcm_is_ready(stdev->pcm)) {
243         ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(stdev->pcm));
244         err = -1;
245         goto exit;
246     }
247     err = pcm_start(stdev->pcm);
248     if (err) {
249         ALOGE("Unable to start PCM device (%s)\n", pcm_get_error(stdev->pcm));
250         goto exit;
251     }
252 
253     memset(fds, 0, 2 * sizeof(struct pollfd));
254     fds[0].events = POLLIN;
255     fds[0].fd = pcm_get_poll_fd(stdev->pcm);;
256     if (fds[0].fd == -1) {
257         ALOGE("Error opening socket for hotplug uevent");
258         goto exit;
259     }
260     fds[1].events = POLLIN;
261     fds[1].fd = stdev->term_sock;
262 
263     pthread_mutex_unlock(&stdev->lock);
264 
265     while (1) {
266         err = poll(fds, 2, -1);
267         pthread_mutex_lock(&stdev->lock);
268         if ((err < 0) || (stdev->recognition_callback == NULL)) {
269             ALOGE_IF(err < 0, "Error in hotplug CPU poll: %d", errno);
270             break;
271         }
272 
273         if (fds[0].revents & POLLIN) {
274             struct sound_trigger_phrase_recognition_event *event;
275             recognition_callback_t callback = stdev->recognition_callback;
276             void *cookie = stdev->recognition_cookie;
277 
278             event = (struct sound_trigger_phrase_recognition_event *)
279                         sound_trigger_event_alloc(stdev);
280             if (!event) {
281                 pcm_close(stdev->pcm);
282                 stdev->pcm = NULL;
283                 goto exit;
284             }
285             stdev->is_streaming = 1;
286             ALOGI("%s send callback model %d", __func__,
287                     stdev->model_handle);
288             pthread_mutex_unlock(&stdev->lock);
289             if (callback != NULL) {
290                 callback(&event->common, cookie);
291             }
292             pthread_mutex_lock(&stdev->lock);
293             free(event);
294             /* Leave the device open for streaming. */
295             goto exit;
296         } else if (fds[1].revents & POLLIN) {
297             read(fds[1].fd, &n, sizeof(n)); /* clear the socket */
298             ALOGI("%s: Termination message", __func__);
299             break;
300         } else {
301             ALOGI("%s: Message to ignore", __func__);
302         }
303         pthread_mutex_unlock(&stdev->lock);
304     }
305 
306     if (stdev->pcm) {
307         pcm_close(stdev->pcm);
308         stdev->pcm = NULL;
309     }
310 
311 exit:
312     stdev->recognition_callback = NULL;
313     stdev_close_term_sock(stdev);
314 
315     if (stdev->config && !stdev->config->capture_requested)
316         stdev_dsp_set_power(stdev, 0);
317 
318     pthread_mutex_unlock(&stdev->lock);
319 
320     return (void *)(long)err;
321 }
322 
stdev_get_properties(const struct sound_trigger_hw_device * dev,struct sound_trigger_properties * properties)323 static int stdev_get_properties(const struct sound_trigger_hw_device *dev,
324                                 struct sound_trigger_properties *properties)
325 {
326     struct dragon_sound_trigger_device *stdev =
327                                (struct dragon_sound_trigger_device *)dev;
328 
329     ALOGI("%s", __func__);
330     if (properties == NULL)
331         return -EINVAL;
332     memcpy(properties, &hw_properties, sizeof(struct sound_trigger_properties));
333     return 0;
334 }
335 
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)336 static int stdev_load_sound_model(const struct sound_trigger_hw_device *dev,
337                                   struct sound_trigger_sound_model *sound_model,
338                                   sound_model_callback_t callback,
339                                   void *cookie,
340                                   sound_model_handle_t *handle)
341 {
342     struct dragon_sound_trigger_device *stdev =
343                                  (struct dragon_sound_trigger_device *)dev;
344     int ret = 0;
345 
346     ALOGI("%s", __func__);
347     pthread_mutex_lock(&stdev->lock);
348     if (handle == NULL || sound_model == NULL) {
349         ret = -EINVAL;
350         goto exit;
351     }
352 
353     if (stdev->model_handle == 1) {
354         ret = -ENOSYS;
355         goto exit;
356     }
357 
358     ret = vad_load_sound_model(stdev,
359                                (char *)sound_model + sound_model->data_offset,
360                                sound_model->data_size);
361     if (ret)
362         goto exit;
363 
364     stdev->model_handle = 1;
365     stdev->sound_model_callback = callback;
366     stdev->sound_model_cookie = cookie;
367     *handle = stdev->model_handle;
368 
369 exit:
370     pthread_mutex_unlock(&stdev->lock);
371     return ret;
372 }
373 
stdev_unload_sound_model(const struct sound_trigger_hw_device * dev,sound_model_handle_t handle)374 static int stdev_unload_sound_model(const struct sound_trigger_hw_device *dev,
375                                     sound_model_handle_t handle)
376 {
377     struct dragon_sound_trigger_device *stdev =
378                                    (struct dragon_sound_trigger_device *)dev;
379     int status = 0;
380 
381     ALOGI("%s handle %d", __func__, handle);
382     pthread_mutex_lock(&stdev->lock);
383     if (handle != 1) {
384         status = -EINVAL;
385         goto exit;
386     }
387     if (stdev->model_handle == 0) {
388         status = -ENOSYS;
389         goto exit;
390     }
391     stdev->model_handle = 0;
392     free(stdev->config);
393     stdev->config = NULL;
394     if (stdev->recognition_callback != NULL) {
395         stdev->recognition_callback = NULL;
396         if (stdev->send_sock >=0)
397             write(stdev->send_sock, "T", 1);
398         pthread_mutex_unlock(&stdev->lock);
399 
400         pthread_join(stdev->callback_thread, (void **)NULL);
401 
402         pthread_mutex_lock(&stdev->lock);
403     }
404 
405 exit:
406     stdev_dsp_set_power(stdev, 0);
407 
408     pthread_mutex_unlock(&stdev->lock);
409     return status;
410 }
411 
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)412 static int stdev_start_recognition(const struct sound_trigger_hw_device *dev,
413                                    sound_model_handle_t sound_model_handle,
414                                    const struct sound_trigger_recognition_config *config,
415                                    recognition_callback_t callback,
416                                    void *cookie)
417 {
418     struct dragon_sound_trigger_device *stdev =
419                                   (struct dragon_sound_trigger_device *)dev;
420     int status = 0;
421 
422     ALOGI("%s sound model %d", __func__, sound_model_handle);
423     pthread_mutex_lock(&stdev->lock);
424     if (stdev->model_handle != sound_model_handle) {
425         status = -ENOSYS;
426         goto exit;
427     }
428     if (stdev->recognition_callback != NULL) {
429         status = -ENOSYS;
430         goto exit;
431     }
432 
433     free(stdev->config);
434     stdev->config = NULL;
435     if (config) {
436         stdev->config = malloc(sizeof(*config));
437         if (!stdev->config) {
438             status = -ENOMEM;
439             goto exit;
440         }
441         memcpy(stdev->config, config, sizeof(*config));
442     }
443 
444     if (stdev->pcm) {
445         pcm_close(stdev->pcm);
446         stdev->pcm = NULL;
447     }
448 
449     stdev_dsp_set_power(stdev, 0);
450 
451     stdev->recognition_callback = callback;
452     stdev->recognition_cookie = cookie;
453     pthread_create(&stdev->callback_thread, (const pthread_attr_t *) NULL,
454                         callback_thread_loop, stdev);
455 exit:
456     pthread_mutex_unlock(&stdev->lock);
457     return status;
458 }
459 
stdev_stop_recognition(const struct sound_trigger_hw_device * dev,sound_model_handle_t sound_model_handle)460 static int stdev_stop_recognition(const struct sound_trigger_hw_device *dev,
461                                   sound_model_handle_t sound_model_handle)
462 {
463     struct dragon_sound_trigger_device *stdev =
464                                  (struct dragon_sound_trigger_device *)dev;
465     int status = 0;
466 
467     ALOGI("%s sound model %d", __func__, sound_model_handle);
468     pthread_mutex_lock(&stdev->lock);
469     if (stdev->model_handle != sound_model_handle) {
470         status = -ENOSYS;
471         goto exit;
472     }
473     if (stdev->recognition_callback == NULL) {
474         status = -ENOSYS;
475         goto exit;
476     }
477     free(stdev->config);
478     stdev->config = NULL;
479     stdev->recognition_callback = NULL;
480     if (stdev->send_sock >=0)
481         write(stdev->send_sock, "T", 1);
482     pthread_mutex_unlock(&stdev->lock);
483 
484     pthread_join(stdev->callback_thread, (void **)NULL);
485 
486     if (stdev->pcm) {
487         pcm_close(stdev->pcm);
488         stdev->pcm = NULL;
489     }
490 
491     pthread_mutex_lock(&stdev->lock);
492 
493 exit:
494     stdev_dsp_set_power(stdev, 0);
495 
496     pthread_mutex_unlock(&stdev->lock);
497     return status;
498 }
499 
500 __attribute__ ((visibility ("default")))
sound_trigger_open_for_streaming()501 int sound_trigger_open_for_streaming()
502 {
503     struct dragon_sound_trigger_device *stdev = &g_stdev;
504     int ret = 0;
505 
506     pthread_mutex_lock(&stdev->lock);
507 
508     if (!stdev->opened) {
509         ALOGE("%s: stdev has not been opened", __func__);
510         ret = -EFAULT;
511         goto exit;
512     }
513     if (!stdev->is_streaming) {
514         ALOGE("%s: DSP is not currently streaming", __func__);
515         ret = -EBUSY;
516         goto exit;
517     }
518     if (!stdev->pcm) {
519         ALOGE("%s: PCM is not open", __func__);
520         ret = -EINVAL;
521         goto exit;
522     }
523     // TODO: Probably want to get something from whoever called us to bind to it/assert that it's a
524     // valid connection. Perhaps returning a more
525     // meaningful handle would be a good idea as well.
526     ret = 1;
527 exit:
528     pthread_mutex_unlock(&stdev->lock);
529     return ret;
530 }
531 
532 __attribute__ ((visibility ("default")))
sound_trigger_read_samples(int audio_handle,void * buffer,size_t buffer_len)533 size_t sound_trigger_read_samples(int audio_handle, void *buffer, size_t  buffer_len)
534 {
535     struct dragon_sound_trigger_device *stdev = &g_stdev;
536     int i;
537     int ret = 0;
538     int frames;
539     int orig_frames;
540     unsigned int offset;
541     unsigned int bytes_mmapped;
542     int16_t* snd_buffer;
543     unsigned int retry_attempts = 0;
544 
545     if (audio_handle <= 0) {
546         ALOGE("%s: invalid audio handle", __func__);
547         return -EINVAL;
548     }
549 
550     pthread_mutex_lock(&stdev->lock);
551 
552     if (!stdev->opened) {
553         ALOGE("%s: stdev has not been opened", __func__);
554         ret = -EFAULT;
555         goto exit;
556     }
557     if (!stdev->is_streaming) {
558         ALOGE("%s: DSP is not currently streaming", __func__);
559         ret = -EINVAL;
560         goto exit;
561     }
562     if (!stdev->pcm) {
563         ALOGE("%s: PCM has closed", __func__);
564         ret = -EINVAL;
565         goto exit;
566     }
567 
568 
569 read_again:
570     frames = pcm_mmap_avail(stdev->pcm);
571     if (frames < 0) {
572         ALOGE("%s: DSP mmap error %d", __func__, frames);
573         ret = frames;
574         goto exit;
575     }
576     if (frames == 0) {
577 //        ALOGE("%s: DSP mmap retry %d", __func__, frames);
578         retry_attempts++;
579         if (retry_attempts > 10)
580             goto exit;
581         usleep(35000);
582         goto read_again;
583     }
584 
585     if (frames > buffer_len / 2)
586         frames = buffer_len / 2;
587 
588     orig_frames = frames;
589 
590     ret = pcm_mmap_begin(stdev->pcm, (void**)&snd_buffer, &offset, &frames);
591     if (ret < 0) {
592         ALOGE("Failed to mmap hotword buffer %d", ret);
593         goto exit;
594     }
595     memcpy(buffer, snd_buffer + offset, frames * 2);
596     pcm_mmap_commit(stdev->pcm, offset, frames);
597     ret = frames * 2;
598     ALOGV("%s: Sent %u frames to buffer", __func__, frames);
599 
600 exit:
601     pthread_mutex_unlock(&stdev->lock);
602     return ret;
603 }
604 
605 __attribute__ ((visibility ("default")))
sound_trigger_close_for_streaming(int audio_handle)606 int sound_trigger_close_for_streaming(int audio_handle)
607 {
608     struct dragon_sound_trigger_device *stdev = &g_stdev;
609 
610     if (audio_handle <= 0) {
611         ALOGE("%s: invalid audio handle", __func__);
612         return -EINVAL;
613     }
614 
615     if (stdev->pcm) {
616         pcm_close(stdev->pcm);
617         stdev->pcm = NULL;
618     }
619 
620     return 0;
621 }
622 
stdev_close(hw_device_t * device)623 static int stdev_close(hw_device_t *device)
624 {
625     struct dragon_sound_trigger_device *stdev =
626                                 (struct dragon_sound_trigger_device *)device;
627     int ret = 0;
628 
629     pthread_mutex_lock(&stdev->lock);
630     if (!stdev->opened) {
631         ALOGE("%s: device already closed", __func__);
632         ret = -EFAULT;
633         goto exit;
634     }
635     stdev_close_mixer(stdev);
636     stdev->model_handle = 0;
637     stdev->send_sock = 0;
638     stdev->term_sock = 0;
639     stdev->opened = false;
640 
641 exit:
642     pthread_mutex_unlock(&stdev->lock);
643     return ret;
644 }
645 
stdev_open(const hw_module_t * module,const char * name,hw_device_t ** device)646 static int stdev_open(const hw_module_t *module, const char *name,
647                       hw_device_t **device)
648 {
649     struct dragon_sound_trigger_device *stdev;
650     int ret;
651 
652     if (strcmp(name, SOUND_TRIGGER_HARDWARE_INTERFACE) != 0)
653         return -EINVAL;
654 
655     stdev = &g_stdev;
656     pthread_mutex_lock(&stdev->lock);
657 
658     if (stdev->opened) {
659         ALOGE("%s: Only one sountrigger can be opened at a time", __func__);
660         ret = -EBUSY;
661         goto exit;
662     }
663 
664     ret = stdev_init_mixer(stdev);
665     if (ret) {
666         ALOGE("Error mixer init");
667         goto exit;
668     }
669 
670     stdev->device.common.tag = HARDWARE_DEVICE_TAG;
671     stdev->device.common.version = SOUND_TRIGGER_DEVICE_API_VERSION_1_0;
672     stdev->device.common.module = (struct hw_module_t *)module;
673     stdev->device.common.close = stdev_close;
674     stdev->device.get_properties = stdev_get_properties;
675     stdev->device.load_sound_model = stdev_load_sound_model;
676     stdev->device.unload_sound_model = stdev_unload_sound_model;
677     stdev->device.start_recognition = stdev_start_recognition;
678     stdev->device.stop_recognition = stdev_stop_recognition;
679     stdev->send_sock = stdev->term_sock = -1;
680     stdev->opened = true;
681 
682     *device = &stdev->device.common; /* same address as stdev */
683 exit:
684     pthread_mutex_unlock(&stdev->lock);
685     return ret;
686 }
687 
688 static struct hw_module_methods_t hal_module_methods = {
689     .open = stdev_open,
690 };
691 
692 struct sound_trigger_module HAL_MODULE_INFO_SYM = {
693     .common = {
694         .tag = HARDWARE_MODULE_TAG,
695         .module_api_version = SOUND_TRIGGER_MODULE_API_VERSION_1_0,
696         .hal_api_version = HARDWARE_HAL_API_VERSION,
697         .id = SOUND_TRIGGER_HARDWARE_MODULE_ID,
698         .name = "Default sound trigger HAL",
699         .author = "The Android Open Source Project",
700         .methods = &hal_module_methods,
701     },
702 };
703