• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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