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 "radio_hw_stub" 18 #define LOG_NDEBUG 0 19 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <pthread.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <sys/prctl.h> 27 #include <sys/stat.h> 28 #include <sys/time.h> 29 #include <sys/types.h> 30 #include <time.h> 31 #include <unistd.h> 32 33 #include <cutils/list.h> 34 #include <log/log.h> 35 36 #include <hardware/hardware.h> 37 #include <hardware/radio.h> 38 #include <system/radio.h> 39 #include <system/radio_metadata.h> 40 41 static const radio_hal_properties_t hw_properties = { 42 .class_id = RADIO_CLASS_AM_FM, 43 .implementor = "The Android Open Source Project", 44 .product = "Radio stub HAL", 45 .version = "0.1", 46 .serial = "0123456789", 47 .num_tuners = 1, 48 .num_audio_sources = 1, 49 .supports_capture = false, 50 .num_bands = 2, 51 .bands = { 52 { 53 .type = RADIO_BAND_FM, 54 .antenna_connected = true, 55 .lower_limit = 87900, 56 .upper_limit = 107900, 57 .num_spacings = 1, 58 .spacings = { 200 }, 59 .fm = { 60 .deemphasis = RADIO_DEEMPHASIS_75, 61 .stereo = true, 62 .rds = RADIO_RDS_US, 63 .ta = false, 64 .af = false, 65 .ea = true, 66 } 67 }, 68 { 69 .type = RADIO_BAND_AM, 70 .antenna_connected = true, 71 .lower_limit = 540, 72 .upper_limit = 1610, 73 .num_spacings = 1, 74 .spacings = { 10 }, 75 .am = { 76 .stereo = true, 77 } 78 } 79 } 80 }; 81 82 static const radio_metadata_clock_t hw_clock = { 83 .utc_seconds_since_epoch = 1234567890, 84 .timezone_offset_in_minutes = (-8 * 60), 85 }; 86 87 struct stub_radio_tuner { 88 struct radio_tuner interface; 89 struct stub_radio_device *dev; 90 radio_callback_t callback; 91 void *cookie; 92 radio_hal_band_config_t config; 93 radio_program_info_t program; 94 bool audio; 95 pthread_t callback_thread; 96 pthread_mutex_t lock; 97 pthread_cond_t cond; 98 struct listnode command_list; 99 }; 100 101 struct stub_radio_device { 102 struct radio_hw_device device; 103 struct stub_radio_tuner *tuner; 104 pthread_mutex_t lock; 105 }; 106 107 108 typedef enum { 109 CMD_EXIT, 110 CMD_CONFIG, 111 CMD_STEP, 112 CMD_SCAN, 113 CMD_TUNE, 114 CMD_CANCEL, 115 CMD_METADATA, 116 CMD_ANNOUNCEMENTS, 117 CMD_NUM 118 } thread_cmd_type_t; 119 120 uint32_t thread_cmd_delay_ms[CMD_NUM] = { 121 [CMD_EXIT] = 0, 122 [CMD_CONFIG] = 50, 123 [CMD_STEP] = 100, 124 [CMD_SCAN] = 200, 125 [CMD_TUNE] = 150, 126 [CMD_CANCEL] = 0, 127 [CMD_METADATA] = 1000, 128 [CMD_ANNOUNCEMENTS] = 1000 129 }; 130 struct thread_command { 131 struct listnode node; 132 thread_cmd_type_t type; 133 struct timespec ts; 134 union { 135 unsigned int param; 136 radio_hal_band_config_t config; 137 }; 138 }; 139 140 /* must be called with out->lock locked */ 141 static int send_command_l(struct stub_radio_tuner *tuner, 142 thread_cmd_type_t type, 143 unsigned int delay_ms, 144 void *param) 145 { 146 struct thread_command *cmd = (struct thread_command *)calloc(1, sizeof(struct thread_command)); 147 struct timespec ts; 148 149 if (cmd == NULL) 150 return -ENOMEM; 151 152 ALOGV("%s %d delay_ms %d", __func__, type, delay_ms); 153 154 cmd->type = type; 155 if (param != NULL) { 156 if (cmd->type == CMD_CONFIG) { 157 cmd->config = *(radio_hal_band_config_t *)param; 158 ALOGV("%s CMD_CONFIG type %d", __func__, cmd->config.type); 159 } else 160 cmd->param = *(unsigned int *)param; 161 } 162 163 clock_gettime(CLOCK_REALTIME, &ts); 164 165 ts.tv_sec += delay_ms/1000; 166 ts.tv_nsec += (delay_ms%1000) * 1000000; 167 if (ts.tv_nsec >= 1000000000) { 168 ts.tv_nsec -= 1000000000; 169 ts.tv_sec += 1; 170 } 171 cmd->ts = ts; 172 list_add_tail(&tuner->command_list, &cmd->node); 173 pthread_cond_signal(&tuner->cond); 174 return 0; 175 } 176 177 #define BITMAP_FILE_PATH "/data/misc/audioserver/android.png" 178 179 static int add_bitmap_metadata(radio_metadata_t **metadata, radio_metadata_key_t key, 180 const char *source) 181 { 182 int fd; 183 ssize_t ret = 0; 184 struct stat info; 185 void *data = NULL; 186 size_t size; 187 188 fd = open(source, O_RDONLY); 189 if (fd < 0) 190 return -EPIPE; 191 192 fstat(fd, &info); 193 size = info.st_size; 194 data = malloc(size); 195 if (data == NULL) { 196 ret = -ENOMEM; 197 goto exit; 198 } 199 ret = read(fd, data, size); 200 if (ret < 0) 201 goto exit; 202 ret = radio_metadata_add_raw(metadata, key, (const unsigned char *)data, size); 203 204 exit: 205 close(fd); 206 free(data); 207 ALOGE_IF(ret != 0, "%s error %d", __func__, (int)ret); 208 return (int)ret; 209 } 210 211 static int prepare_metadata(struct stub_radio_tuner *tuner, 212 radio_metadata_t **metadata, bool program) 213 { 214 int ret = 0; 215 char text[RADIO_STRING_LEN_MAX]; 216 struct timespec ts; 217 218 if (metadata == NULL) 219 return -EINVAL; 220 221 if (*metadata != NULL) 222 radio_metadata_deallocate(*metadata); 223 224 *metadata = NULL; 225 226 ret = radio_metadata_allocate(metadata, tuner->program.channel, 0); 227 228 if (ret != 0) 229 return ret; 230 231 if (program) { 232 ret = radio_metadata_add_int(metadata, RADIO_METADATA_KEY_RBDS_PTY, 5); 233 if (ret != 0) 234 goto exit; 235 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_RDS_PS, "RockBand"); 236 if (ret != 0) 237 goto exit; 238 ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ICON, BITMAP_FILE_PATH); 239 if (ret != 0 && ret != -EPIPE) 240 goto exit; 241 ret = radio_metadata_add_clock(metadata, RADIO_METADATA_KEY_CLOCK, &hw_clock); 242 if (ret != 0) 243 goto exit; 244 } else { 245 ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ART, BITMAP_FILE_PATH); 246 if (ret != 0 && ret != -EPIPE) 247 goto exit; 248 } 249 250 clock_gettime(CLOCK_REALTIME, &ts); 251 snprintf(text, RADIO_STRING_LEN_MAX, "Artist %ld", ts.tv_sec % 10); 252 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_ARTIST, text); 253 if (ret != 0) 254 goto exit; 255 256 snprintf(text, RADIO_STRING_LEN_MAX, "Song %ld", ts.tv_nsec % 10); 257 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_TITLE, text); 258 if (ret != 0) 259 goto exit; 260 261 return 0; 262 263 exit: 264 radio_metadata_deallocate(*metadata); 265 *metadata = NULL; 266 return ret; 267 } 268 269 static void *callback_thread_loop(void *context) 270 { 271 struct stub_radio_tuner *tuner = (struct stub_radio_tuner *)context; 272 struct timespec ts = {0, 0}; 273 274 ALOGI("%s", __func__); 275 276 prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0); 277 278 pthread_mutex_lock(&tuner->lock); 279 280 // Fields which are used to toggle the state of traffic announcements and 281 // ea announcements at random. They are access protected by tuner->lock. 282 bool ea_state = false; 283 284 while (true) { 285 struct thread_command *cmd = NULL; 286 struct listnode *item; 287 struct listnode *tmp; 288 struct timespec cur_ts; 289 bool got_cancel = false; 290 bool send_meta_data = false; 291 292 if (list_empty(&tuner->command_list) || ts.tv_sec != 0) { 293 ALOGV("%s SLEEPING", __func__); 294 if (ts.tv_sec != 0) { 295 ALOGV("%s SLEEPING with timeout", __func__); 296 pthread_cond_timedwait(&tuner->cond, &tuner->lock, &ts); 297 } else { 298 ALOGV("%s SLEEPING forever", __func__); 299 pthread_cond_wait(&tuner->cond, &tuner->lock); 300 } 301 ts.tv_sec = 0; 302 ALOGV("%s RUNNING", __func__); 303 } 304 305 clock_gettime(CLOCK_REALTIME, &cur_ts); 306 307 list_for_each_safe(item, tmp, &tuner->command_list) { 308 cmd = node_to_item(item, struct thread_command, node); 309 310 if (got_cancel && (cmd->type == CMD_STEP || cmd->type == CMD_SCAN || 311 cmd->type == CMD_TUNE || cmd->type == CMD_METADATA || 312 cmd->type == CMD_ANNOUNCEMENTS)) { 313 list_remove(item); 314 free(cmd); 315 continue; 316 } 317 318 if ((cmd->ts.tv_sec < cur_ts.tv_sec) || 319 ((cmd->ts.tv_sec == cur_ts.tv_sec) && (cmd->ts.tv_nsec < cur_ts.tv_nsec))) { 320 radio_hal_event_t event; 321 radio_metadata_t *metadata = NULL; 322 323 event.type = RADIO_EVENT_HW_FAILURE; 324 list_remove(item); 325 326 ALOGV("%s processing command %d time %ld.%ld", __func__, cmd->type, cmd->ts.tv_sec, 327 cmd->ts.tv_nsec); 328 329 switch (cmd->type) { 330 default: 331 case CMD_EXIT: 332 free(cmd); 333 goto exit; 334 335 case CMD_CONFIG: { 336 tuner->config = cmd->config; 337 tuner->config.antenna_connected = true; 338 event.type = RADIO_EVENT_CONFIG; 339 event.config = tuner->config; 340 ALOGV("%s CMD_CONFIG type %d low %d up %d", 341 __func__, tuner->config.type, 342 tuner->config.lower_limit, tuner->config.upper_limit); 343 if (tuner->config.type == RADIO_BAND_FM) { 344 ALOGV(" - stereo %d\n - rds %d\n - ta %d\n - af %d\n" 345 " - ea %d\n", 346 tuner->config.fm.stereo, tuner->config.fm.rds, 347 tuner->config.fm.ta, tuner->config.fm.af, 348 tuner->config.fm.ea); 349 } else { 350 ALOGV(" - stereo %d", tuner->config.am.stereo); 351 } 352 } break; 353 354 case CMD_STEP: { 355 int frequency; 356 frequency = tuner->program.channel; 357 if (cmd->param == RADIO_DIRECTION_UP) { 358 frequency += tuner->config.spacings[0]; 359 } else { 360 frequency -= tuner->config.spacings[0]; 361 } 362 if (frequency > (int)tuner->config.upper_limit) { 363 frequency = tuner->config.lower_limit; 364 } 365 if (frequency < (int)tuner->config.lower_limit) { 366 frequency = tuner->config.upper_limit; 367 } 368 tuner->program.channel = frequency; 369 tuner->program.tuned = (frequency / (tuner->config.spacings[0] * 5)) % 2; 370 tuner->program.signal_strength = 20; 371 if (tuner->config.type == RADIO_BAND_FM) 372 tuner->program.stereo = false; 373 else 374 tuner->program.stereo = false; 375 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned); 376 377 event.type = RADIO_EVENT_TUNED; 378 event.info = tuner->program; 379 } break; 380 381 case CMD_SCAN: { 382 int frequency; 383 frequency = tuner->program.channel; 384 if (cmd->param == RADIO_DIRECTION_UP) { 385 frequency += tuner->config.spacings[0] * 25; 386 } else { 387 frequency -= tuner->config.spacings[0] * 25; 388 } 389 if (frequency > (int)tuner->config.upper_limit) { 390 frequency = tuner->config.lower_limit; 391 } 392 if (frequency < (int)tuner->config.lower_limit) { 393 frequency = tuner->config.upper_limit; 394 } 395 tuner->program.channel = (unsigned int)frequency; 396 tuner->program.tuned = true; 397 if (tuner->config.type == RADIO_BAND_FM) 398 tuner->program.stereo = tuner->config.fm.stereo; 399 else 400 tuner->program.stereo = tuner->config.am.stereo; 401 tuner->program.signal_strength = 50; 402 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned); 403 404 event.type = RADIO_EVENT_TUNED; 405 event.info = tuner->program; 406 send_meta_data = true; 407 } break; 408 409 case CMD_TUNE: { 410 tuner->program.channel = cmd->param; 411 tuner->program.tuned = (tuner->program.channel / 412 (tuner->config.spacings[0] * 5)) % 2; 413 414 if (tuner->program.tuned) { 415 send_command_l(tuner, CMD_ANNOUNCEMENTS, thread_cmd_delay_ms[CMD_ANNOUNCEMENTS], NULL); 416 } 417 tuner->program.signal_strength = 100; 418 if (tuner->config.type == RADIO_BAND_FM) 419 tuner->program.stereo = 420 tuner->program.tuned ? tuner->config.fm.stereo : false; 421 else 422 tuner->program.stereo = 423 tuner->program.tuned ? tuner->config.am.stereo : false; 424 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned); 425 426 event.type = RADIO_EVENT_TUNED; 427 event.info = tuner->program; 428 send_meta_data = true; 429 } break; 430 431 case CMD_METADATA: { 432 int ret = prepare_metadata(tuner, &metadata, false); 433 if (ret == 0) { 434 event.type = RADIO_EVENT_METADATA; 435 event.metadata = metadata; 436 } 437 } break; 438 439 case CMD_CANCEL: { 440 got_cancel = true; 441 } break; 442 443 // Fire emergency announcements if they are enabled in the config. Stub 444 // implementation simply fires an announcement for 5 second 445 // duration with a gap of 5 seconds. 446 case CMD_ANNOUNCEMENTS: { 447 ALOGV("In annoucements. %d %d %d\n", 448 ea_state, tuner->config.type, tuner->config.fm.ea); 449 if (tuner->config.type == RADIO_BAND_FM || 450 tuner->config.type == RADIO_BAND_FM_HD) { 451 if (ea_state) { 452 ea_state = false; 453 event.type = RADIO_EVENT_EA; 454 } else if (tuner->config.fm.ea) { 455 ea_state = true; 456 event.type = RADIO_EVENT_EA; 457 } 458 event.on = ea_state; 459 460 if (tuner->config.fm.ea) { 461 send_command_l(tuner, CMD_ANNOUNCEMENTS, 5000, NULL); 462 } 463 } 464 } break; 465 } 466 if (event.type != RADIO_EVENT_HW_FAILURE && tuner->callback != NULL) { 467 pthread_mutex_unlock(&tuner->lock); 468 tuner->callback(&event, tuner->cookie); 469 pthread_mutex_lock(&tuner->lock); 470 if (event.type == RADIO_EVENT_METADATA && metadata != NULL) { 471 radio_metadata_deallocate(metadata); 472 metadata = NULL; 473 } 474 } 475 ALOGV("%s processed command %d", __func__, cmd->type); 476 free(cmd); 477 } else { 478 if ((ts.tv_sec == 0) || 479 (cmd->ts.tv_sec < ts.tv_sec) || 480 ((cmd->ts.tv_sec == ts.tv_sec) && (cmd->ts.tv_nsec < ts.tv_nsec))) { 481 ts.tv_sec = cmd->ts.tv_sec; 482 ts.tv_nsec = cmd->ts.tv_nsec; 483 } 484 } 485 } 486 487 if (send_meta_data) { 488 list_for_each_safe(item, tmp, &tuner->command_list) { 489 cmd = node_to_item(item, struct thread_command, node); 490 if (cmd->type == CMD_METADATA) { 491 list_remove(item); 492 free(cmd); 493 } 494 } 495 send_command_l(tuner, CMD_METADATA, thread_cmd_delay_ms[CMD_METADATA], NULL); 496 } 497 } 498 499 exit: 500 pthread_mutex_unlock(&tuner->lock); 501 502 ALOGV("%s Exiting", __func__); 503 504 return NULL; 505 } 506 507 508 static int tuner_set_configuration(const struct radio_tuner *tuner, 509 const radio_hal_band_config_t *config) 510 { 511 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 512 int status = 0; 513 514 ALOGI("%s stub_tuner %p", __func__, stub_tuner); 515 pthread_mutex_lock(&stub_tuner->lock); 516 if (config == NULL) { 517 status = -EINVAL; 518 goto exit; 519 } 520 if (config->lower_limit > config->upper_limit) { 521 status = -EINVAL; 522 goto exit; 523 } 524 send_command_l(stub_tuner, CMD_CANCEL, thread_cmd_delay_ms[CMD_CANCEL], NULL); 525 send_command_l(stub_tuner, CMD_CONFIG, thread_cmd_delay_ms[CMD_CONFIG], (void *)config); 526 527 exit: 528 pthread_mutex_unlock(&stub_tuner->lock); 529 return status; 530 } 531 532 static int tuner_get_configuration(const struct radio_tuner *tuner, 533 radio_hal_band_config_t *config) 534 { 535 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 536 int status = 0; 537 struct listnode *item; 538 radio_hal_band_config_t *src_config; 539 540 ALOGI("%s stub_tuner %p", __func__, stub_tuner); 541 pthread_mutex_lock(&stub_tuner->lock); 542 src_config = &stub_tuner->config; 543 544 if (config == NULL) { 545 status = -EINVAL; 546 goto exit; 547 } 548 list_for_each(item, &stub_tuner->command_list) { 549 struct thread_command *cmd = node_to_item(item, struct thread_command, node); 550 if (cmd->type == CMD_CONFIG) { 551 src_config = &cmd->config; 552 } 553 } 554 *config = *src_config; 555 556 exit: 557 pthread_mutex_unlock(&stub_tuner->lock); 558 return status; 559 } 560 561 static int tuner_step(const struct radio_tuner *tuner, 562 radio_direction_t direction, bool skip_sub_channel) 563 { 564 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 565 566 ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d", 567 __func__, stub_tuner, direction, skip_sub_channel); 568 569 pthread_mutex_lock(&stub_tuner->lock); 570 send_command_l(stub_tuner, CMD_STEP, thread_cmd_delay_ms[CMD_STEP], &direction); 571 pthread_mutex_unlock(&stub_tuner->lock); 572 return 0; 573 } 574 575 static int tuner_scan(const struct radio_tuner *tuner, 576 radio_direction_t direction, bool skip_sub_channel) 577 { 578 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 579 580 ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d", 581 __func__, stub_tuner, direction, skip_sub_channel); 582 583 pthread_mutex_lock(&stub_tuner->lock); 584 send_command_l(stub_tuner, CMD_SCAN, thread_cmd_delay_ms[CMD_SCAN], &direction); 585 pthread_mutex_unlock(&stub_tuner->lock); 586 return 0; 587 } 588 589 static int tuner_tune(const struct radio_tuner *tuner, 590 unsigned int channel, unsigned int sub_channel) 591 { 592 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 593 594 ALOGI("%s stub_tuner %p channel %d, sub_channel %d", 595 __func__, stub_tuner, channel, sub_channel); 596 597 pthread_mutex_lock(&stub_tuner->lock); 598 if (channel < stub_tuner->config.lower_limit || channel > stub_tuner->config.upper_limit) { 599 pthread_mutex_unlock(&stub_tuner->lock); 600 ALOGI("%s channel out of range", __func__); 601 return -EINVAL; 602 } 603 send_command_l(stub_tuner, CMD_TUNE, thread_cmd_delay_ms[CMD_TUNE], &channel); 604 pthread_mutex_unlock(&stub_tuner->lock); 605 return 0; 606 } 607 608 static int tuner_cancel(const struct radio_tuner *tuner) 609 { 610 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 611 612 ALOGI("%s stub_tuner %p", __func__, stub_tuner); 613 614 pthread_mutex_lock(&stub_tuner->lock); 615 send_command_l(stub_tuner, CMD_CANCEL, thread_cmd_delay_ms[CMD_CANCEL], NULL); 616 pthread_mutex_unlock(&stub_tuner->lock); 617 return 0; 618 } 619 620 static int tuner_get_program_information(const struct radio_tuner *tuner, 621 radio_program_info_t *info) 622 { 623 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 624 int status = 0; 625 radio_metadata_t *metadata; 626 627 ALOGI("%s stub_tuner %p", __func__, stub_tuner); 628 pthread_mutex_lock(&stub_tuner->lock); 629 if (info == NULL) { 630 status = -EINVAL; 631 goto exit; 632 } 633 metadata = info->metadata; 634 *info = stub_tuner->program; 635 info->metadata = metadata; 636 if (metadata == NULL) { 637 ALOGE("%s metadata is a nullptr", __func__); 638 status = -EINVAL; 639 goto exit; 640 } 641 if (stub_tuner->program.metadata != NULL) 642 radio_metadata_add_metadata(&info->metadata, stub_tuner->program.metadata); 643 644 exit: 645 pthread_mutex_unlock(&stub_tuner->lock); 646 return status; 647 } 648 649 static int rdev_get_properties(const struct radio_hw_device *dev, 650 radio_hal_properties_t *properties) 651 { 652 ALOGI("%s", __func__); 653 if (properties == NULL) 654 return -EINVAL; 655 memcpy(properties, &hw_properties, sizeof(radio_hal_properties_t)); 656 return 0; 657 } 658 659 static int rdev_open_tuner(const struct radio_hw_device *dev, 660 const radio_hal_band_config_t *config, 661 bool audio, 662 radio_callback_t callback, 663 void *cookie, 664 const struct radio_tuner **tuner) 665 { 666 struct stub_radio_device *rdev = (struct stub_radio_device *)dev; 667 int status = 0; 668 669 ALOGI("%s rdev %p", __func__, rdev); 670 pthread_mutex_lock(&rdev->lock); 671 672 if (rdev->tuner != NULL) { 673 ALOGE("Can't open tuner twice"); 674 status = -ENOSYS; 675 goto exit; 676 } 677 678 if (config == NULL || callback == NULL || tuner == NULL) { 679 status = -EINVAL; 680 goto exit; 681 } 682 683 rdev->tuner = (struct stub_radio_tuner *)calloc(1, sizeof(struct stub_radio_tuner)); 684 if (rdev->tuner == NULL) { 685 status = -ENOMEM; 686 goto exit; 687 } 688 689 rdev->tuner->interface.set_configuration = tuner_set_configuration; 690 rdev->tuner->interface.get_configuration = tuner_get_configuration; 691 rdev->tuner->interface.scan = tuner_scan; 692 rdev->tuner->interface.step = tuner_step; 693 rdev->tuner->interface.tune = tuner_tune; 694 rdev->tuner->interface.cancel = tuner_cancel; 695 rdev->tuner->interface.get_program_information = tuner_get_program_information; 696 697 rdev->tuner->audio = audio; 698 rdev->tuner->callback = callback; 699 rdev->tuner->cookie = cookie; 700 701 rdev->tuner->dev = rdev; 702 703 pthread_mutex_init(&rdev->tuner->lock, (const pthread_mutexattr_t *) NULL); 704 pthread_cond_init(&rdev->tuner->cond, (const pthread_condattr_t *) NULL); 705 pthread_create(&rdev->tuner->callback_thread, (const pthread_attr_t *) NULL, 706 callback_thread_loop, rdev->tuner); 707 list_init(&rdev->tuner->command_list); 708 709 pthread_mutex_lock(&rdev->tuner->lock); 710 send_command_l(rdev->tuner, CMD_CONFIG, thread_cmd_delay_ms[CMD_CONFIG], (void *)config); 711 pthread_mutex_unlock(&rdev->tuner->lock); 712 713 *tuner = &rdev->tuner->interface; 714 715 exit: 716 pthread_mutex_unlock(&rdev->lock); 717 ALOGI("%s DONE", __func__); 718 return status; 719 } 720 721 static int rdev_close_tuner(const struct radio_hw_device *dev, 722 const struct radio_tuner *tuner) 723 { 724 struct stub_radio_device *rdev = (struct stub_radio_device *)dev; 725 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner; 726 int status = 0; 727 728 ALOGI("%s tuner %p", __func__, tuner); 729 pthread_mutex_lock(&rdev->lock); 730 731 if (tuner == NULL) { 732 status = -EINVAL; 733 goto exit; 734 } 735 736 pthread_mutex_lock(&stub_tuner->lock); 737 stub_tuner->callback = NULL; 738 send_command_l(stub_tuner, CMD_EXIT, thread_cmd_delay_ms[CMD_EXIT], NULL); 739 pthread_mutex_unlock(&stub_tuner->lock); 740 pthread_join(stub_tuner->callback_thread, (void **) NULL); 741 742 if (stub_tuner->program.metadata != NULL) 743 radio_metadata_deallocate(stub_tuner->program.metadata); 744 745 free(stub_tuner); 746 rdev->tuner = NULL; 747 748 exit: 749 pthread_mutex_unlock(&rdev->lock); 750 return status; 751 } 752 753 static int rdev_close(hw_device_t *device) 754 { 755 struct stub_radio_device *rdev = (struct stub_radio_device *)device; 756 if (rdev != NULL) { 757 free(rdev->tuner); 758 } 759 free(rdev); 760 return 0; 761 } 762 763 static int rdev_open(const hw_module_t* module, const char* name, 764 hw_device_t** device) 765 { 766 struct stub_radio_device *rdev; 767 768 if (strcmp(name, RADIO_HARDWARE_DEVICE) != 0) 769 return -EINVAL; 770 771 rdev = calloc(1, sizeof(struct stub_radio_device)); 772 if (!rdev) 773 return -ENOMEM; 774 775 rdev->device.common.tag = HARDWARE_DEVICE_TAG; 776 rdev->device.common.version = RADIO_DEVICE_API_VERSION_1_0; 777 rdev->device.common.module = (struct hw_module_t *) module; 778 rdev->device.common.close = rdev_close; 779 rdev->device.get_properties = rdev_get_properties; 780 rdev->device.open_tuner = rdev_open_tuner; 781 rdev->device.close_tuner = rdev_close_tuner; 782 783 pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL); 784 785 *device = &rdev->device.common; 786 787 return 0; 788 } 789 790 791 static struct hw_module_methods_t hal_module_methods = { 792 .open = rdev_open, 793 }; 794 795 struct radio_module HAL_MODULE_INFO_SYM = { 796 .common = { 797 .tag = HARDWARE_MODULE_TAG, 798 .module_api_version = RADIO_MODULE_API_VERSION_1_0, 799 .hal_api_version = HARDWARE_HAL_API_VERSION, 800 .id = RADIO_HARDWARE_MODULE_ID, 801 .name = "Stub radio HAL", 802 .author = "The Android Open Source Project", 803 .methods = &hal_module_methods, 804 }, 805 }; 806