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