blob: 802e12e932abe581df1d7fc4edb0329d608426cd [file] [log] [blame]
Dmitry Shmidt27002ea2014-05-27 15:07:12 -07001/*
2 * Copyright (C) 2014 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_flounder"
18/*#define LOG_NDEBUG 0*/
19
20#include <errno.h>
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070021#include <fcntl.h>
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070022#include <poll.h>
23#include <pthread.h>
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070024#include <sys/ioctl.h>
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070025#include <sys/prctl.h>
26#include <cutils/log.h>
27#include <cutils/uevent.h>
28
29#include <hardware/hardware.h>
30#include <system/sound_trigger.h>
31#include <hardware/sound_trigger.h>
32#include <tinyalsa/asoundlib.h>
33
Eric Laurent76923242014-08-10 12:24:59 -070034#define FLOUNDER_MIXER_VAD 0
Dmitry Shmidta69d1a22014-09-18 12:24:15 -070035#define FLOUNDER_CTRL_DSP "VAD Mode"
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070036#define UEVENT_MSG_LEN 1024
37
Eric Laurent76923242014-08-10 12:24:59 -070038#define FLOUNDER_VAD_DEV "/dev/snd/hwC0D0"
Chris Thornton1f88ec32014-08-28 15:04:13 -070039
40#define FLOUNDER_STREAMING_DELAY_USEC 1000
41#define FLOUNDER_STREAMING_READ_RETRY_ATTEMPTS 30
Chris Thornton01104702014-09-06 16:11:13 -070042#define FLOUNDER_STREAMING_BUFFER_SIZE (16 * 1024)
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070043
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070044static const struct sound_trigger_properties hw_properties = {
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070045 "The Android Open Source Project", // implementor
Eric Laurent73845f62014-07-30 08:46:55 -070046 "Volantis OK Google ", // description
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070047 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
Chris Thornton1f88ec32014-08-28 15:04:13 -070053 true, // capture_transition
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070054 0, // max_capture_ms
Dmitry Shmidt13c81bf2014-09-17 11:12:40 -070055 false, // concurrent_capture
Chris Thornton1f88ec32014-08-28 15:04:13 -070056 false, // trigger_in_event
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070057 0 // power_consumption_mw
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070058};
59
60struct flounder_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;
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070071 int vad_fd;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070072 struct mixer *mixer;
73 struct mixer_ctl *ctl_dsp;
Dmitry Shmidt17c7dc52014-07-29 11:30:24 -070074 struct sound_trigger_recognition_config *config;
Chris Thornton1f88ec32014-08-28 15:04:13 -070075 int is_streaming;
76 int opened;
Chris Thornton01104702014-09-06 16:11:13 -070077 char *streaming_buf;
78 size_t streaming_buf_read;
79 size_t streaming_buf_len;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070080};
81
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -070082struct rt_codec_cmd {
83 size_t number;
84 int *buf;
85};
86
87enum {
88 RT_READ_CODEC_DSP_IOCTL = _IOR('R', 0x04, struct rt_codec_cmd),
89 RT_WRITE_CODEC_DSP_IOCTL = _IOW('R', 0x04, struct rt_codec_cmd),
90};
91
Chris Thornton1f88ec32014-08-28 15:04:13 -070092// Since there's only ever one sound_trigger_device, keep it as a global so that other people can
93// dlopen this lib to get at the streaming audio.
94static struct flounder_sound_trigger_device g_stdev = { .lock = PTHREAD_MUTEX_INITIALIZER };
Dmitry Shmidt27002ea2014-05-27 15:07:12 -070095
Dmitry Shmidt757dd182014-08-27 10:54:09 -070096static void stdev_dsp_set_power(struct flounder_sound_trigger_device *stdev,
97 int val)
98{
Chris Thornton1f88ec32014-08-28 15:04:13 -070099 stdev->is_streaming = 0;
Chris Thornton01104702014-09-06 16:11:13 -0700100 stdev->streaming_buf_read = 0;
101 stdev->streaming_buf_len = 0;
Dmitry Shmidt757dd182014-08-27 10:54:09 -0700102 mixer_ctl_set_value(stdev->ctl_dsp, 0, val);
Dmitry Shmidt757dd182014-08-27 10:54:09 -0700103}
104
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700105static int stdev_init_mixer(struct flounder_sound_trigger_device *stdev)
106{
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700107 int ret = -1;
108
109 stdev->vad_fd = open(FLOUNDER_VAD_DEV, O_RDWR);
110 if (stdev->vad_fd < 0) {
111 ALOGE("Error opening vad device");
112 return ret;
113 }
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700114
115 stdev->mixer = mixer_open(FLOUNDER_MIXER_VAD);
116 if (!stdev->mixer)
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700117 goto err;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700118
Dmitry Shmidta69d1a22014-09-18 12:24:15 -0700119 stdev->ctl_dsp = mixer_get_ctl_by_name(stdev->mixer, FLOUNDER_CTRL_DSP);
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700120 if (!stdev->ctl_dsp)
121 goto err;
122
Chris Thornton1f88ec32014-08-28 15:04:13 -0700123 stdev_dsp_set_power(stdev, 0); // Disable DSP at the beginning
Dmitry Shmidt757dd182014-08-27 10:54:09 -0700124
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700125 return 0;
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700126
127err:
128 close(stdev->vad_fd);
129 if (stdev->mixer)
130 mixer_close(stdev->mixer);
131 return ret;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700132}
133
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700134static void stdev_close_term_sock(struct flounder_sound_trigger_device *stdev)
135{
136 if (stdev->send_sock >=0) {
137 close(stdev->send_sock);
138 stdev->send_sock = -1;
139 }
140 if (stdev->term_sock >=0) {
141 close(stdev->term_sock);
142 stdev->term_sock = -1;
143 }
144}
145
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700146static void stdev_close_mixer(struct flounder_sound_trigger_device *stdev)
147{
148 if (stdev) {
Chris Thornton1f88ec32014-08-28 15:04:13 -0700149 stdev_dsp_set_power(stdev, 0);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700150 mixer_close(stdev->mixer);
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700151 stdev_close_term_sock(stdev);
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700152 close(stdev->vad_fd);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700153 }
154}
155
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700156static int vad_load_sound_model(struct flounder_sound_trigger_device *stdev,
157 char *buf, size_t len)
158{
159 struct rt_codec_cmd cmd;
Dmitry Shmidteac6f012014-07-21 10:32:21 -0700160 int ret = 0;
161
162 if (!buf || (len == 0))
163 return ret;
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700164
Dmitry Shmidt6c63d442014-07-02 15:56:28 -0700165 cmd.number = len / sizeof(int);
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700166 cmd.buf = (int *)buf;
167
168 ret = ioctl(stdev->vad_fd, RT_WRITE_CODEC_DSP_IOCTL, &cmd);
169 if (ret)
170 ALOGE("Error VAD write ioctl");
171 return ret;
172}
173
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700174static char *sound_trigger_event_alloc(struct flounder_sound_trigger_device *
175 stdev)
176{
Dmitry Shmidt215fc3c2014-08-11 14:45:04 -0700177 char *data;
178 struct sound_trigger_phrase_recognition_event *event;
Dmitry Shmidt215fc3c2014-08-11 14:45:04 -0700179
180 data = (char *)calloc(1,
Chris Thornton1f88ec32014-08-28 15:04:13 -0700181 sizeof(struct sound_trigger_phrase_recognition_event));
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700182 if (!data)
183 return NULL;
Dmitry Shmidt215fc3c2014-08-11 14:45:04 -0700184
185 event = (struct sound_trigger_phrase_recognition_event *)data;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700186 event->common.status = RECOGNITION_STATUS_SUCCESS;
187 event->common.type = SOUND_MODEL_TYPE_KEYPHRASE;
188 event->common.model = stdev->model_handle;
Dmitry Shmidt17c7dc52014-07-29 11:30:24 -0700189
190 if (stdev->config) {
191 unsigned int i;
192
193 event->num_phrases = stdev->config->num_phrases;
194 if (event->num_phrases > SOUND_TRIGGER_MAX_PHRASES)
195 event->num_phrases = SOUND_TRIGGER_MAX_PHRASES;
196 for (i=0; i < event->num_phrases; i++)
197 memcpy(&event->phrase_extras[i], &stdev->config->phrases[i],
198 sizeof(struct sound_trigger_phrase_recognition_extra));
199 }
Eric Laurent73845f62014-07-30 08:46:55 -0700200
201 event->num_phrases = 1;
202 event->phrase_extras[0].confidence_level = 100;
203 event->phrase_extras[0].num_levels = 1;
204 event->phrase_extras[0].levels[0].level = 100;
205 event->phrase_extras[0].levels[0].user_id = 0;
Chris Thornton1f88ec32014-08-28 15:04:13 -0700206 // Signify that all the data is comming through streaming, not through the
207 // buffer.
208 event->common.capture_available = true;
Eric Laurent73845f62014-07-30 08:46:55 -0700209
Chris Thornton1f88ec32014-08-28 15:04:13 -0700210 event->common.audio_config = AUDIO_CONFIG_INITIALIZER;
211 event->common.audio_config.sample_rate = 16000;
212 event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO;
213 event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
Eric Laurent73845f62014-07-30 08:46:55 -0700214
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700215 return data;
216}
217
Chris Thornton01104702014-09-06 16:11:13 -0700218// The stdev should be locked when you call this function.
219static int fetch_streaming_buffer(struct flounder_sound_trigger_device * stdev)
220{
221 struct rt_codec_cmd cmd;
222 int ret = 0;
223 int i;
224
225 cmd.number = FLOUNDER_STREAMING_BUFFER_SIZE / sizeof(int);
226 cmd.buf = (int*) stdev->streaming_buf;
227
228 ALOGV("%s: Fetching bytes", __func__);
229 for (i = 0; i < FLOUNDER_STREAMING_READ_RETRY_ATTEMPTS; i++) {
230 ret = ioctl(stdev->vad_fd, RT_READ_CODEC_DSP_IOCTL, &cmd);
231 if (ret == 0) {
232 usleep(FLOUNDER_STREAMING_DELAY_USEC);
233 } else if (ret < 0) {
234 ALOGV("%s: IOCTL failed with code %d", __func__, ret);
235 return ret;
236 } else {
237 // The IOCTL returns the number of int16 samples that were read, so we need to multipy
238 // it by 2 .
239 ALOGV("%s: IOCTL captured %d samples", __func__, ret);
240 stdev->streaming_buf_read = 0;
241 stdev->streaming_buf_len = ret << 1;
242 return 0;
243 }
244 }
245 ALOGV("%s: Timeout waiting for data from dsp", __func__);
246 return 0;
247}
248
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700249static void *callback_thread_loop(void *context)
250{
251 char msg[UEVENT_MSG_LEN];
252 struct flounder_sound_trigger_device *stdev =
253 (struct flounder_sound_trigger_device *)context;
254 struct pollfd fds[2];
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700255 int exit_sockets[2];
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700256 int err = 0;
257 int i, n;
258
259 ALOGI("%s", __func__);
260 prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0);
261
262 pthread_mutex_lock(&stdev->lock);
263 if (stdev->recognition_callback == NULL)
264 goto exit;
265
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700266 if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1)
267 goto exit;
268
269 stdev_close_term_sock(stdev);
270 stdev->send_sock = exit_sockets[0];
271 stdev->term_sock = exit_sockets[1];
272
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700273 memset(fds, 0, 2 * sizeof(struct pollfd));
274 fds[0].events = POLLIN;
275 fds[0].fd = uevent_open_socket(64*1024, true);
276 if (fds[0].fd == -1) {
277 ALOGE("Error opening socket for hotplug uevent");
278 goto exit;
279 }
280 fds[1].events = POLLIN;
281 fds[1].fd = stdev->term_sock;
282
Dmitry Shmidt757dd182014-08-27 10:54:09 -0700283 stdev_dsp_set_power(stdev, 1);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700284
285 pthread_mutex_unlock(&stdev->lock);
286
287 while (1) {
288 err = poll(fds, 2, -1);
289 pthread_mutex_lock(&stdev->lock);
290 if ((err < 0) || (stdev->recognition_callback == NULL)) {
291 ALOGE("Error in hotplug CPU poll: %d", errno);
292 break;
293 }
294
295 if (fds[0].revents & POLLIN) {
296 n = uevent_kernel_multicast_recv(fds[0].fd, msg, UEVENT_MSG_LEN);
Dmitry Shmidta9deea52014-07-22 16:53:36 -0700297 if (n <= 0) {
298 pthread_mutex_unlock(&stdev->lock);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700299 continue;
Dmitry Shmidta9deea52014-07-22 16:53:36 -0700300 }
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700301 for (i=0; i < n;) {
302 if (strstr(msg + i, "HOTWORD")) {
303 struct sound_trigger_phrase_recognition_event *event;
304
305 event = (struct sound_trigger_phrase_recognition_event *)
306 sound_trigger_event_alloc(stdev);
307 if (event) {
Chris Thornton1f88ec32014-08-28 15:04:13 -0700308 stdev->is_streaming = 1;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700309 ALOGI("%s send callback model %d", __func__,
310 stdev->model_handle);
311 stdev->recognition_callback(&event->common,
312 stdev->recognition_cookie);
313 free(event);
Chris Thornton01104702014-09-06 16:11:13 -0700314 // Start reading data from the DSP while the upper levels do their thing.
315 if (stdev->config && stdev->config->capture_requested) {
316 fetch_streaming_buffer(stdev);
317 }
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700318 }
319 goto found;
320 }
321 i += strlen(msg + i) + 1;
322 }
Dmitry Shmidta9deea52014-07-22 16:53:36 -0700323 } else if (fds[1].revents & POLLIN) {
Dmitry Shmidt0931b5f2014-07-25 10:24:15 -0700324 read(fds[1].fd, &n, sizeof(n)); /* clear the socket */
Dmitry Shmidta2e95682014-09-02 16:20:38 -0700325 ALOGI("%s: Termination message", __func__);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700326 break;
Dmitry Shmidta9deea52014-07-22 16:53:36 -0700327 } else {
328 ALOGI("%s: Message to ignore", __func__);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700329 }
330 pthread_mutex_unlock(&stdev->lock);
331 }
332
333found:
334 close(fds[0].fd);
335
336exit:
337 stdev->recognition_callback = NULL;
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700338 stdev_close_term_sock(stdev);
339
Chris Thornton1f88ec32014-08-28 15:04:13 -0700340 if (stdev->config && !stdev->config->capture_requested)
341 stdev_dsp_set_power(stdev, 0);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700342
343 pthread_mutex_unlock(&stdev->lock);
344
345 return (void *)(long)err;
346}
347
348static int stdev_get_properties(const struct sound_trigger_hw_device *dev,
349 struct sound_trigger_properties *properties)
350{
351 struct flounder_sound_trigger_device *stdev =
352 (struct flounder_sound_trigger_device *)dev;
353
354 ALOGI("%s", __func__);
355 if (properties == NULL)
356 return -EINVAL;
357 memcpy(properties, &hw_properties, sizeof(struct sound_trigger_properties));
358 return 0;
359}
360
361static int stdev_load_sound_model(const struct sound_trigger_hw_device *dev,
362 struct sound_trigger_sound_model *sound_model,
363 sound_model_callback_t callback,
364 void *cookie,
365 sound_model_handle_t *handle)
366{
367 struct flounder_sound_trigger_device *stdev =
368 (struct flounder_sound_trigger_device *)dev;
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700369 int ret = 0;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700370
371 ALOGI("%s", __func__);
372 pthread_mutex_lock(&stdev->lock);
373 if (handle == NULL || sound_model == NULL) {
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700374 ret = -EINVAL;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700375 goto exit;
376 }
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700377
378 if (stdev->model_handle == 1) {
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700379 ret = -ENOSYS;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700380 goto exit;
381 }
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700382
383 ret = vad_load_sound_model(stdev,
384 (char *)sound_model + sound_model->data_offset,
385 sound_model->data_size);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700386 stdev->model_handle = 1;
387 stdev->sound_model_callback = callback;
388 stdev->sound_model_cookie = cookie;
389 *handle = stdev->model_handle;
390
391exit:
392 pthread_mutex_unlock(&stdev->lock);
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700393 return ret;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700394}
395
396static int stdev_unload_sound_model(const struct sound_trigger_hw_device *dev,
397 sound_model_handle_t handle)
398{
399 struct flounder_sound_trigger_device *stdev =
400 (struct flounder_sound_trigger_device *)dev;
401 int status = 0;
402
403 ALOGI("%s handle %d", __func__, handle);
404 pthread_mutex_lock(&stdev->lock);
405 if (handle != 1) {
406 status = -EINVAL;
407 goto exit;
408 }
409 if (stdev->model_handle == 0) {
410 status = -ENOSYS;
411 goto exit;
412 }
413 stdev->model_handle = 0;
Dmitry Shmidt17c7dc52014-07-29 11:30:24 -0700414 free(stdev->config);
415 stdev->config = NULL;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700416 if (stdev->recognition_callback != NULL) {
417 stdev->recognition_callback = NULL;
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700418 if (stdev->send_sock >=0)
419 write(stdev->send_sock, "T", 1);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700420 pthread_mutex_unlock(&stdev->lock);
421
422 pthread_join(stdev->callback_thread, (void **)NULL);
423
424 pthread_mutex_lock(&stdev->lock);
425 }
426
427exit:
Chris Thornton1f88ec32014-08-28 15:04:13 -0700428 stdev_dsp_set_power(stdev, 0);
429
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700430 pthread_mutex_unlock(&stdev->lock);
431 return status;
432}
433
434static int stdev_start_recognition(const struct sound_trigger_hw_device *dev,
435 sound_model_handle_t sound_model_handle,
Dmitry Shmidt17c7dc52014-07-29 11:30:24 -0700436 const struct sound_trigger_recognition_config *config,
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700437 recognition_callback_t callback,
Eric Laurent1e1ca132014-07-06 16:12:48 -0700438 void *cookie)
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700439{
440 struct flounder_sound_trigger_device *stdev =
441 (struct flounder_sound_trigger_device *)dev;
442 int status = 0;
443
444 ALOGI("%s sound model %d", __func__, sound_model_handle);
445 pthread_mutex_lock(&stdev->lock);
446 if (stdev->model_handle != sound_model_handle) {
447 status = -ENOSYS;
448 goto exit;
449 }
450 if (stdev->recognition_callback != NULL) {
451 status = -ENOSYS;
452 goto exit;
453 }
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700454
Dmitry Shmidt17c7dc52014-07-29 11:30:24 -0700455 free(stdev->config);
456 stdev->config = NULL;
457 if (config) {
458 stdev->config = malloc(sizeof(*config));
459 if (!stdev->config) {
460 status = -ENOMEM;
461 goto exit;
462 }
463 memcpy(stdev->config, config, sizeof(*config));
464 }
465
Chris Thornton1f88ec32014-08-28 15:04:13 -0700466 stdev_dsp_set_power(stdev, 0);
467
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700468 stdev->recognition_callback = callback;
469 stdev->recognition_cookie = cookie;
470 pthread_create(&stdev->callback_thread, (const pthread_attr_t *) NULL,
471 callback_thread_loop, stdev);
472exit:
473 pthread_mutex_unlock(&stdev->lock);
474 return status;
475}
476
477static int stdev_stop_recognition(const struct sound_trigger_hw_device *dev,
478 sound_model_handle_t sound_model_handle)
479{
480 struct flounder_sound_trigger_device *stdev =
481 (struct flounder_sound_trigger_device *)dev;
482 int status = 0;
483
484 ALOGI("%s sound model %d", __func__, sound_model_handle);
485 pthread_mutex_lock(&stdev->lock);
486 if (stdev->model_handle != sound_model_handle) {
487 status = -ENOSYS;
488 goto exit;
489 }
490 if (stdev->recognition_callback == NULL) {
491 status = -ENOSYS;
492 goto exit;
493 }
Dmitry Shmidt17c7dc52014-07-29 11:30:24 -0700494 free(stdev->config);
495 stdev->config = NULL;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700496 stdev->recognition_callback = NULL;
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700497 if (stdev->send_sock >=0)
498 write(stdev->send_sock, "T", 1);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700499 pthread_mutex_unlock(&stdev->lock);
500
501 pthread_join(stdev->callback_thread, (void **)NULL);
502
503 pthread_mutex_lock(&stdev->lock);
504
505exit:
Chris Thornton1f88ec32014-08-28 15:04:13 -0700506 stdev_dsp_set_power(stdev, 0);
507
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700508 pthread_mutex_unlock(&stdev->lock);
509 return status;
510}
511
Chris Thornton1f88ec32014-08-28 15:04:13 -0700512__attribute__ ((visibility ("default")))
513int sound_trigger_open_for_streaming()
514{
515 struct flounder_sound_trigger_device *stdev = &g_stdev;
516 int ret = 0;
517
518 pthread_mutex_lock(&stdev->lock);
519
520 if (!stdev->opened) {
521 ALOGE("%s: stdev has not been opened", __func__);
522 ret = -EFAULT;
523 goto exit;
524 }
525 if (!stdev->is_streaming) {
526 ALOGE("%s: DSP is not currently streaming", __func__);
527 ret = -EBUSY;
528 goto exit;
529 }
530 // TODO: Probably want to get something from whoever called us to bind to it/assert that it's a
531 // valid connection. Perhaps returning a more
532 // meaningful handle would be a good idea as well.
533 ret = 1;
534exit:
535 pthread_mutex_unlock(&stdev->lock);
536 return ret;
537}
538
539__attribute__ ((visibility ("default")))
540size_t sound_trigger_read_samples(int audio_handle, void *buffer, size_t buffer_len)
541{
Chris Thornton1f88ec32014-08-28 15:04:13 -0700542 struct flounder_sound_trigger_device *stdev = &g_stdev;
543 int i;
Chris Thornton01104702014-09-06 16:11:13 -0700544 size_t ret = 0;
Chris Thornton1f88ec32014-08-28 15:04:13 -0700545
546 if (audio_handle <= 0) {
547 ALOGE("%s: invalid audio handle", __func__);
548 return -EINVAL;
549 }
550
551 pthread_mutex_lock(&stdev->lock);
552
553 if (!stdev->opened) {
554 ALOGE("%s: stdev has not been opened", __func__);
555 ret = -EFAULT;
556 goto exit;
557 }
558 if (!stdev->is_streaming) {
559 ALOGE("%s: DSP is not currently streaming", __func__);
560 ret = -EINVAL;
561 goto exit;
562 }
563
Chris Thornton01104702014-09-06 16:11:13 -0700564 if (stdev->streaming_buf_read == stdev->streaming_buf_len) {
565 ret = fetch_streaming_buffer(stdev);
Chris Thornton1f88ec32014-08-28 15:04:13 -0700566 }
Chris Thornton01104702014-09-06 16:11:13 -0700567
568 if (!ret) {
569 ret = stdev->streaming_buf_len - stdev->streaming_buf_read;
570 if (ret > buffer_len)
571 ret = buffer_len;
572 memcpy(buffer, stdev->streaming_buf + stdev->streaming_buf_read, ret);
573 stdev->streaming_buf_read += ret;
Dmitry Shmidta69d1a22014-09-18 12:24:15 -0700574 ALOGV("%s: Sent %zu bytes to buffer", __func__, ret);
Chris Thornton01104702014-09-06 16:11:13 -0700575 }
576
Chris Thornton1f88ec32014-08-28 15:04:13 -0700577exit:
578 pthread_mutex_unlock(&stdev->lock);
579 return ret;
580}
581
582__attribute__ ((visibility ("default")))
583int sound_trigger_close_for_streaming(int audio_handle __unused)
584{
585 // TODO: Power down the DSP? I think we shouldn't in case we want to open this mic for streaming
586 // for the voice search?
587 return 0;
588}
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700589
590static int stdev_close(hw_device_t *device)
591{
592 struct flounder_sound_trigger_device *stdev =
593 (struct flounder_sound_trigger_device *)device;
Chris Thornton1f88ec32014-08-28 15:04:13 -0700594 int ret = 0;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700595
Chris Thornton1f88ec32014-08-28 15:04:13 -0700596 pthread_mutex_lock(&stdev->lock);
597 if (!stdev->opened) {
598 ALOGE("%s: device already closed", __func__);
599 ret = -EFAULT;
600 goto exit;
601 }
Chris Thornton01104702014-09-06 16:11:13 -0700602 free(stdev->streaming_buf);
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700603 stdev_close_mixer(stdev);
Chris Thornton1f88ec32014-08-28 15:04:13 -0700604 stdev->model_handle = 0;
605 stdev->send_sock = 0;
606 stdev->term_sock = 0;
607 stdev->opened = false;
608
609exit:
610 pthread_mutex_unlock(&stdev->lock);
611 return ret;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700612}
613
614static int stdev_open(const hw_module_t *module, const char *name,
615 hw_device_t **device)
616{
617 struct flounder_sound_trigger_device *stdev;
618 int ret;
619
620 if (strcmp(name, SOUND_TRIGGER_HARDWARE_INTERFACE) != 0)
621 return -EINVAL;
622
Chris Thornton1f88ec32014-08-28 15:04:13 -0700623 stdev = &g_stdev;
624 pthread_mutex_lock(&stdev->lock);
625
626 if (stdev->opened) {
627 ALOGE("%s: Only one sountrigger can be opened at a time", __func__);
628 ret = -EBUSY;
629 goto exit;
630 }
631
Chris Thornton01104702014-09-06 16:11:13 -0700632 stdev->streaming_buf = malloc(FLOUNDER_STREAMING_BUFFER_SIZE);
633 if (!stdev->streaming_buf) {
634 ret = -ENOMEM;
635 goto exit;
636 }
637
Chris Thornton1f88ec32014-08-28 15:04:13 -0700638 ret = stdev_init_mixer(stdev);
639 if (ret) {
640 ALOGE("Error mixer init");
Chris Thornton01104702014-09-06 16:11:13 -0700641 free(stdev->streaming_buf);
Chris Thornton1f88ec32014-08-28 15:04:13 -0700642 goto exit;
643 }
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700644
645 stdev->device.common.tag = HARDWARE_DEVICE_TAG;
646 stdev->device.common.version = SOUND_TRIGGER_DEVICE_API_VERSION_1_0;
647 stdev->device.common.module = (struct hw_module_t *)module;
648 stdev->device.common.close = stdev_close;
649 stdev->device.get_properties = stdev_get_properties;
650 stdev->device.load_sound_model = stdev_load_sound_model;
651 stdev->device.unload_sound_model = stdev_unload_sound_model;
652 stdev->device.start_recognition = stdev_start_recognition;
653 stdev->device.stop_recognition = stdev_stop_recognition;
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700654 stdev->send_sock = stdev->term_sock = -1;
Chris Thornton01104702014-09-06 16:11:13 -0700655 stdev->streaming_buf_read = 0;
656 stdev->streaming_buf_len = 0;
Chris Thornton1f88ec32014-08-28 15:04:13 -0700657 stdev->opened = true;
Dmitry Shmidt104ffd22014-07-28 13:57:49 -0700658
Chris Thornton1f88ec32014-08-28 15:04:13 -0700659 *device = &stdev->device.common; /* same address as stdev */
660exit:
661 pthread_mutex_unlock(&stdev->lock);
Dmitry Shmidt5afa3ed2014-06-27 12:48:59 -0700662 return ret;
Dmitry Shmidt27002ea2014-05-27 15:07:12 -0700663}
664
665static struct hw_module_methods_t hal_module_methods = {
666 .open = stdev_open,
667};
668
669struct sound_trigger_module HAL_MODULE_INFO_SYM = {
670 .common = {
671 .tag = HARDWARE_MODULE_TAG,
672 .module_api_version = SOUND_TRIGGER_MODULE_API_VERSION_1_0,
673 .hal_api_version = HARDWARE_HAL_API_VERSION,
674 .id = SOUND_TRIGGER_HARDWARE_MODULE_ID,
675 .name = "Default sound trigger HAL",
676 .author = "The Android Open Source Project",
677 .methods = &hal_module_methods,
678 },
679};