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 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 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 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 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 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 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 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 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 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 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 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 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"))) 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"))) 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"))) 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 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 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