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