blob: 29204df54bcb67541cbfc914d0f179bcb6158696 [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
Eric Laurent97d2ba62015-03-05 12:52:14 -080020#include <errno.h>
Mark Salyzynd88dfe82017-04-11 08:56:09 -070021#include <fcntl.h>
Eric Laurent97d2ba62015-03-05 12:52:14 -080022#include <pthread.h>
Mark Salyzynd88dfe82017-04-11 08:56:09 -070023#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
Eric Laurent97d2ba62015-03-05 12:52:14 -080026#include <sys/prctl.h>
Mark Salyzynd88dfe82017-04-11 08:56:09 -070027#include <sys/stat.h>
Eric Laurent97d2ba62015-03-05 12:52:14 -080028#include <sys/time.h>
29#include <sys/types.h>
Mark Salyzynd88dfe82017-04-11 08:56:09 -070030#include <time.h>
Eric Laurent97d2ba62015-03-05 12:52:14 -080031#include <unistd.h>
Mark Salyzynd88dfe82017-04-11 08:56:09 -070032
Eric Laurent97d2ba62015-03-05 12:52:14 -080033#include <cutils/list.h>
Mark Salyzynd88dfe82017-04-11 08:56:09 -070034#include <log/log.h>
35
Eric Laurent97d2ba62015-03-05 12:52:14 -080036#include <hardware/hardware.h>
37#include <hardware/radio.h>
Mark Salyzynd88dfe82017-04-11 08:56:09 -070038#include <system/radio.h>
39#include <system/radio_metadata.h>
Eric Laurent97d2ba62015-03-05 12:52:14 -080040
41static 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,
Eric Laurentde8e0132016-12-13 14:20:26 -080054 .antenna_connected = true,
Eric Laurent97d2ba62015-03-05 12:52:14 -080055 .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,
Sanket Agarwala83e0a82015-10-07 17:15:08 -070065 .ea = true,
Eric Laurent97d2ba62015-03-05 12:52:14 -080066 }
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
Sanket Agarwala83e0a82015-10-07 17:15:08 -070082static const radio_metadata_clock_t hw_clock = {
83 .utc_seconds_since_epoch = 1234567890,
84 .timezone_offset_in_minutes = (-8 * 60),
85};
86
Eric Laurent97d2ba62015-03-05 12:52:14 -080087struct 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
101struct stub_radio_device {
102 struct radio_hw_device device;
103 struct stub_radio_tuner *tuner;
104 pthread_mutex_t lock;
105};
106
107
108typedef enum {
109 CMD_EXIT,
110 CMD_CONFIG,
111 CMD_STEP,
112 CMD_SCAN,
113 CMD_TUNE,
114 CMD_CANCEL,
115 CMD_METADATA,
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700116 CMD_ANNOUNCEMENTS,
Eric Laurent97d2ba62015-03-05 12:52:14 -0800117} thread_cmd_type_t;
118
119struct thread_command {
120 struct listnode node;
121 thread_cmd_type_t type;
122 struct timespec ts;
123 union {
124 unsigned int param;
125 radio_hal_band_config_t config;
126 };
127};
128
129/* must be called with out->lock locked */
130static int send_command_l(struct stub_radio_tuner *tuner,
131 thread_cmd_type_t type,
132 unsigned int delay_ms,
133 void *param)
134{
135 struct thread_command *cmd = (struct thread_command *)calloc(1, sizeof(struct thread_command));
136 struct timespec ts;
137
138 if (cmd == NULL)
139 return -ENOMEM;
140
141 ALOGV("%s %d delay_ms %d", __func__, type, delay_ms);
142
143 cmd->type = type;
144 if (param != NULL) {
145 if (cmd->type == CMD_CONFIG) {
146 cmd->config = *(radio_hal_band_config_t *)param;
147 ALOGV("%s CMD_CONFIG type %d", __func__, cmd->config.type);
148 } else
149 cmd->param = *(unsigned int *)param;
150 }
151
152 clock_gettime(CLOCK_REALTIME, &ts);
153
154 ts.tv_sec += delay_ms/1000;
155 ts.tv_nsec += (delay_ms%1000) * 1000000;
156 if (ts.tv_nsec >= 1000000000) {
157 ts.tv_nsec -= 1000000000;
158 ts.tv_sec += 1;
159 }
160 cmd->ts = ts;
161 list_add_tail(&tuner->command_list, &cmd->node);
162 pthread_cond_signal(&tuner->cond);
163 return 0;
164}
165
Eric Laurent854a10a2016-02-19 14:41:51 -0800166#define BITMAP_FILE_PATH "/data/misc/audioserver/android.png"
Eric Laurent97d2ba62015-03-05 12:52:14 -0800167
168static int add_bitmap_metadata(radio_metadata_t **metadata, radio_metadata_key_t key,
169 const char *source)
170{
171 int fd;
172 ssize_t ret = 0;
173 struct stat info;
174 void *data = NULL;
175 size_t size;
176
177 fd = open(source, O_RDONLY);
178 if (fd < 0)
179 return -EPIPE;
180
181 fstat(fd, &info);
182 size = info.st_size;
183 data = malloc(size);
184 if (data == NULL) {
185 ret = -ENOMEM;
186 goto exit;
187 }
188 ret = read(fd, data, size);
189 if (ret < 0)
190 goto exit;
191 ret = radio_metadata_add_raw(metadata, key, (const unsigned char *)data, size);
192
193exit:
194 close(fd);
195 free(data);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700196 ALOGE_IF(ret != 0, "%s error %d", __func__, (int)ret);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800197 return (int)ret;
198}
199
200static int prepare_metadata(struct stub_radio_tuner *tuner,
201 radio_metadata_t **metadata, bool program)
202{
203 int ret = 0;
204 char text[RADIO_STRING_LEN_MAX];
205 struct timespec ts;
206
207 if (metadata == NULL)
208 return -EINVAL;
209
210 if (*metadata != NULL)
211 radio_metadata_deallocate(*metadata);
212
213 *metadata = NULL;
214
215 ret = radio_metadata_allocate(metadata, tuner->program.channel, 0);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700216
Eric Laurent97d2ba62015-03-05 12:52:14 -0800217 if (ret != 0)
218 return ret;
219
220 if (program) {
221 ret = radio_metadata_add_int(metadata, RADIO_METADATA_KEY_RBDS_PTY, 5);
222 if (ret != 0)
223 goto exit;
224 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_RDS_PS, "RockBand");
225 if (ret != 0)
226 goto exit;
227 ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ICON, BITMAP_FILE_PATH);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700228 if (ret != 0 && ret != -EPIPE)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800229 goto exit;
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700230 ret = radio_metadata_add_clock(metadata, RADIO_METADATA_KEY_CLOCK, &hw_clock);
231 if (ret != 0)
232 goto exit;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800233 } else {
234 ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ART, BITMAP_FILE_PATH);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700235 if (ret != 0 && ret != -EPIPE)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800236 goto exit;
237 }
238
239 clock_gettime(CLOCK_REALTIME, &ts);
240 snprintf(text, RADIO_STRING_LEN_MAX, "Artist %ld", ts.tv_sec % 10);
241 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_ARTIST, text);
242 if (ret != 0)
243 goto exit;
244
245 snprintf(text, RADIO_STRING_LEN_MAX, "Song %ld", ts.tv_nsec % 10);
246 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_TITLE, text);
247 if (ret != 0)
248 goto exit;
249
250 return 0;
251
252exit:
253 radio_metadata_deallocate(*metadata);
254 *metadata = NULL;
255 return ret;
256}
257
258static void *callback_thread_loop(void *context)
259{
260 struct stub_radio_tuner *tuner = (struct stub_radio_tuner *)context;
261 struct timespec ts = {0, 0};
262
263 ALOGI("%s", __func__);
264
265 prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0);
266
267 pthread_mutex_lock(&tuner->lock);
268
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700269 // Fields which are used to toggle the state of traffic announcements and
270 // ea announcements at random. They are access protected by tuner->lock.
271 bool ea_state = false;
272
Eric Laurent97d2ba62015-03-05 12:52:14 -0800273 while (true) {
274 struct thread_command *cmd = NULL;
275 struct listnode *item;
276 struct listnode *tmp;
277 struct timespec cur_ts;
Eric Laurentacccf642015-04-23 19:11:36 -0700278 bool got_cancel = false;
279 bool send_meta_data = false;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800280
281 if (list_empty(&tuner->command_list) || ts.tv_sec != 0) {
282 ALOGV("%s SLEEPING", __func__);
283 if (ts.tv_sec != 0) {
284 ALOGV("%s SLEEPING with timeout", __func__);
285 pthread_cond_timedwait(&tuner->cond, &tuner->lock, &ts);
286 } else {
287 ALOGV("%s SLEEPING forever", __func__);
288 pthread_cond_wait(&tuner->cond, &tuner->lock);
289 }
290 ts.tv_sec = 0;
291 ALOGV("%s RUNNING", __func__);
292 }
293
294 clock_gettime(CLOCK_REALTIME, &cur_ts);
295
296 list_for_each_safe(item, tmp, &tuner->command_list) {
297 cmd = node_to_item(item, struct thread_command, node);
298
Eric Laurentacccf642015-04-23 19:11:36 -0700299 if (got_cancel && (cmd->type == CMD_STEP || cmd->type == CMD_SCAN ||
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700300 cmd->type == CMD_TUNE || cmd->type == CMD_METADATA ||
301 cmd->type == CMD_ANNOUNCEMENTS)) {
Eric Laurentacccf642015-04-23 19:11:36 -0700302 list_remove(item);
303 free(cmd);
304 continue;
305 }
306
Eric Laurent97d2ba62015-03-05 12:52:14 -0800307 if ((cmd->ts.tv_sec < cur_ts.tv_sec) ||
308 ((cmd->ts.tv_sec == cur_ts.tv_sec) && (cmd->ts.tv_nsec < cur_ts.tv_nsec))) {
309 radio_hal_event_t event;
Eric Laurentacccf642015-04-23 19:11:36 -0700310 radio_metadata_t *metadata = NULL;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800311
312 event.type = RADIO_EVENT_HW_FAILURE;
313 list_remove(item);
314
315 ALOGV("%s processing command %d time %ld.%ld", __func__, cmd->type, cmd->ts.tv_sec,
316 cmd->ts.tv_nsec);
317
318 switch (cmd->type) {
319 default:
320 case CMD_EXIT:
321 free(cmd);
322 goto exit;
323
324 case CMD_CONFIG: {
325 tuner->config = cmd->config;
Tomasz Wasilczyk13f56282017-05-17 16:23:28 -0700326 tuner->config.antenna_connected = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800327 event.type = RADIO_EVENT_CONFIG;
328 event.config = tuner->config;
329 ALOGV("%s CMD_CONFIG type %d low %d up %d",
330 __func__, tuner->config.type,
331 tuner->config.lower_limit, tuner->config.upper_limit);
332 if (tuner->config.type == RADIO_BAND_FM) {
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700333 ALOGV(" - stereo %d\n - rds %d\n - ta %d\n - af %d\n"
334 " - ea %d\n",
Eric Laurent97d2ba62015-03-05 12:52:14 -0800335 tuner->config.fm.stereo, tuner->config.fm.rds,
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700336 tuner->config.fm.ta, tuner->config.fm.af,
337 tuner->config.fm.ea);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800338 } else {
339 ALOGV(" - stereo %d", tuner->config.am.stereo);
340 }
341 } break;
342
343 case CMD_STEP: {
344 int frequency;
345 frequency = tuner->program.channel;
346 if (cmd->param == RADIO_DIRECTION_UP) {
347 frequency += tuner->config.spacings[0];
348 } else {
349 frequency -= tuner->config.spacings[0];
350 }
351 if (frequency > (int)tuner->config.upper_limit) {
352 frequency = tuner->config.lower_limit;
353 }
354 if (frequency < (int)tuner->config.lower_limit) {
355 frequency = tuner->config.upper_limit;
356 }
357 tuner->program.channel = frequency;
358 tuner->program.tuned = (frequency / (tuner->config.spacings[0] * 5)) % 2;
359 tuner->program.signal_strength = 20;
360 if (tuner->config.type == RADIO_BAND_FM)
361 tuner->program.stereo = false;
362 else
363 tuner->program.stereo = false;
Tomasz Wasilczyk3c7297a2017-01-06 14:19:52 -0800364 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800365
366 event.type = RADIO_EVENT_TUNED;
367 event.info = tuner->program;
368 } break;
369
370 case CMD_SCAN: {
371 int frequency;
372 frequency = tuner->program.channel;
373 if (cmd->param == RADIO_DIRECTION_UP) {
374 frequency += tuner->config.spacings[0] * 25;
375 } else {
376 frequency -= tuner->config.spacings[0] * 25;
377 }
378 if (frequency > (int)tuner->config.upper_limit) {
379 frequency = tuner->config.lower_limit;
380 }
381 if (frequency < (int)tuner->config.lower_limit) {
382 frequency = tuner->config.upper_limit;
383 }
384 tuner->program.channel = (unsigned int)frequency;
385 tuner->program.tuned = true;
386 if (tuner->config.type == RADIO_BAND_FM)
387 tuner->program.stereo = tuner->config.fm.stereo;
388 else
389 tuner->program.stereo = tuner->config.am.stereo;
390 tuner->program.signal_strength = 50;
Tomasz Wasilczyk3c7297a2017-01-06 14:19:52 -0800391 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800392
393 event.type = RADIO_EVENT_TUNED;
394 event.info = tuner->program;
Eric Laurentacccf642015-04-23 19:11:36 -0700395 send_meta_data = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800396 } break;
397
398 case CMD_TUNE: {
399 tuner->program.channel = cmd->param;
400 tuner->program.tuned = (tuner->program.channel /
401 (tuner->config.spacings[0] * 5)) % 2;
402
403 if (tuner->program.tuned) {
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700404 send_command_l(tuner, CMD_ANNOUNCEMENTS, 1000, NULL);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800405 }
406 tuner->program.signal_strength = 100;
407 if (tuner->config.type == RADIO_BAND_FM)
408 tuner->program.stereo =
409 tuner->program.tuned ? tuner->config.fm.stereo : false;
410 else
411 tuner->program.stereo =
412 tuner->program.tuned ? tuner->config.am.stereo : false;
Tomasz Wasilczyk3c7297a2017-01-06 14:19:52 -0800413 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
414
Eric Laurent97d2ba62015-03-05 12:52:14 -0800415 event.type = RADIO_EVENT_TUNED;
416 event.info = tuner->program;
Eric Laurentacccf642015-04-23 19:11:36 -0700417 send_meta_data = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800418 } break;
419
420 case CMD_METADATA: {
Eric Laurentacccf642015-04-23 19:11:36 -0700421 int ret = prepare_metadata(tuner, &metadata, false);
422 if (ret == 0) {
423 event.type = RADIO_EVENT_METADATA;
424 event.metadata = metadata;
425 }
Eric Laurent97d2ba62015-03-05 12:52:14 -0800426 } break;
427
428 case CMD_CANCEL: {
Eric Laurentacccf642015-04-23 19:11:36 -0700429 got_cancel = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800430 } break;
431
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700432 // Fire emergency announcements if they are enabled in the config. Stub
433 // implementation simply fires an announcement for 5 second
434 // duration with a gap of 5 seconds.
435 case CMD_ANNOUNCEMENTS: {
436 ALOGV("In annoucements. %d %d %d\n",
437 ea_state, tuner->config.type, tuner->config.fm.ea);
438 if (tuner->config.type == RADIO_BAND_FM ||
439 tuner->config.type == RADIO_BAND_FM_HD) {
440 if (ea_state) {
441 ea_state = false;
442 event.type = RADIO_EVENT_EA;
443 } else if (tuner->config.fm.ea) {
444 ea_state = true;
445 event.type = RADIO_EVENT_EA;
446 }
447 event.on = ea_state;
448
449 if (tuner->config.fm.ea) {
450 send_command_l(tuner, CMD_ANNOUNCEMENTS, 5000, NULL);
451 }
452 }
453 } break;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800454 }
455 if (event.type != RADIO_EVENT_HW_FAILURE && tuner->callback != NULL) {
456 pthread_mutex_unlock(&tuner->lock);
457 tuner->callback(&event, tuner->cookie);
458 pthread_mutex_lock(&tuner->lock);
Eric Laurentacccf642015-04-23 19:11:36 -0700459 if (event.type == RADIO_EVENT_METADATA && metadata != NULL) {
460 radio_metadata_deallocate(metadata);
461 metadata = NULL;
462 }
Eric Laurent97d2ba62015-03-05 12:52:14 -0800463 }
464 ALOGV("%s processed command %d", __func__, cmd->type);
465 free(cmd);
466 } else {
467 if ((ts.tv_sec == 0) ||
468 (cmd->ts.tv_sec < ts.tv_sec) ||
469 ((cmd->ts.tv_sec == ts.tv_sec) && (cmd->ts.tv_nsec < ts.tv_nsec))) {
470 ts.tv_sec = cmd->ts.tv_sec;
471 ts.tv_nsec = cmd->ts.tv_nsec;
472 }
473 }
474 }
Eric Laurentacccf642015-04-23 19:11:36 -0700475
476 if (send_meta_data) {
477 list_for_each_safe(item, tmp, &tuner->command_list) {
478 cmd = node_to_item(item, struct thread_command, node);
479 if (cmd->type == CMD_METADATA) {
480 list_remove(item);
481 free(cmd);
482 }
483 }
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700484 send_command_l(tuner, CMD_METADATA, 1000, NULL);
Eric Laurentacccf642015-04-23 19:11:36 -0700485 }
Eric Laurent97d2ba62015-03-05 12:52:14 -0800486 }
487
488exit:
489 pthread_mutex_unlock(&tuner->lock);
490
491 ALOGV("%s Exiting", __func__);
492
493 return NULL;
494}
495
496
497static int tuner_set_configuration(const struct radio_tuner *tuner,
498 const radio_hal_band_config_t *config)
499{
500 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
501 int status = 0;
502
503 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
504 pthread_mutex_lock(&stub_tuner->lock);
505 if (config == NULL) {
506 status = -EINVAL;
507 goto exit;
508 }
Tomasz Wasilczykad78c8d2017-03-07 17:05:41 -0800509 if (config->lower_limit > config->upper_limit) {
510 status = -EINVAL;
511 goto exit;
512 }
Eric Laurent97d2ba62015-03-05 12:52:14 -0800513 send_command_l(stub_tuner, CMD_CANCEL, 0, NULL);
514 send_command_l(stub_tuner, CMD_CONFIG, 500, (void *)config);
515
516exit:
517 pthread_mutex_unlock(&stub_tuner->lock);
518 return status;
519}
520
521static int tuner_get_configuration(const struct radio_tuner *tuner,
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700522 radio_hal_band_config_t *config)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800523{
524 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
525 int status = 0;
526 struct listnode *item;
527 radio_hal_band_config_t *src_config;
528
529 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
530 pthread_mutex_lock(&stub_tuner->lock);
531 src_config = &stub_tuner->config;
532
533 if (config == NULL) {
534 status = -EINVAL;
535 goto exit;
536 }
537 list_for_each(item, &stub_tuner->command_list) {
538 struct thread_command *cmd = node_to_item(item, struct thread_command, node);
539 if (cmd->type == CMD_CONFIG) {
540 src_config = &cmd->config;
541 }
542 }
543 *config = *src_config;
544
545exit:
546 pthread_mutex_unlock(&stub_tuner->lock);
547 return status;
548}
549
550static int tuner_step(const struct radio_tuner *tuner,
551 radio_direction_t direction, bool skip_sub_channel)
552{
553 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
554
555 ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
556 __func__, stub_tuner, direction, skip_sub_channel);
557
558 pthread_mutex_lock(&stub_tuner->lock);
559 send_command_l(stub_tuner, CMD_STEP, 20, &direction);
560 pthread_mutex_unlock(&stub_tuner->lock);
561 return 0;
562}
563
564static int tuner_scan(const struct radio_tuner *tuner,
565 radio_direction_t direction, bool skip_sub_channel)
566{
567 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
568
569 ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
570 __func__, stub_tuner, direction, skip_sub_channel);
571
572 pthread_mutex_lock(&stub_tuner->lock);
573 send_command_l(stub_tuner, CMD_SCAN, 200, &direction);
574 pthread_mutex_unlock(&stub_tuner->lock);
575 return 0;
576}
577
578static int tuner_tune(const struct radio_tuner *tuner,
579 unsigned int channel, unsigned int sub_channel)
580{
581 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
582
583 ALOGI("%s stub_tuner %p channel %d, sub_channel %d",
584 __func__, stub_tuner, channel, sub_channel);
585
586 pthread_mutex_lock(&stub_tuner->lock);
587 if (channel < stub_tuner->config.lower_limit || channel > stub_tuner->config.upper_limit) {
588 pthread_mutex_unlock(&stub_tuner->lock);
589 ALOGI("%s channel out of range", __func__);
590 return -EINVAL;
591 }
592 send_command_l(stub_tuner, CMD_TUNE, 100, &channel);
593 pthread_mutex_unlock(&stub_tuner->lock);
594 return 0;
595}
596
597static int tuner_cancel(const struct radio_tuner *tuner)
598{
599 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
600
601 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
602
603 pthread_mutex_lock(&stub_tuner->lock);
604 send_command_l(stub_tuner, CMD_CANCEL, 0, NULL);
605 pthread_mutex_unlock(&stub_tuner->lock);
606 return 0;
607}
608
609static int tuner_get_program_information(const struct radio_tuner *tuner,
610 radio_program_info_t *info)
611{
612 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
613 int status = 0;
614 radio_metadata_t *metadata;
615
616 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
617 pthread_mutex_lock(&stub_tuner->lock);
618 if (info == NULL) {
619 status = -EINVAL;
620 goto exit;
621 }
622 metadata = info->metadata;
623 *info = stub_tuner->program;
624 info->metadata = metadata;
Tomasz Wasilczyk33683bf2017-02-15 16:57:20 -0800625 if (metadata == NULL) {
626 ALOGE("%s metadata is a nullptr", __func__);
627 status = -EINVAL;
628 goto exit;
629 }
630 if (stub_tuner->program.metadata != NULL)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800631 radio_metadata_add_metadata(&info->metadata, stub_tuner->program.metadata);
632
633exit:
634 pthread_mutex_unlock(&stub_tuner->lock);
635 return status;
636}
637
638static int rdev_get_properties(const struct radio_hw_device *dev,
639 radio_hal_properties_t *properties)
640{
Eric Laurent97d2ba62015-03-05 12:52:14 -0800641 ALOGI("%s", __func__);
642 if (properties == NULL)
643 return -EINVAL;
644 memcpy(properties, &hw_properties, sizeof(radio_hal_properties_t));
645 return 0;
646}
647
648static int rdev_open_tuner(const struct radio_hw_device *dev,
649 const radio_hal_band_config_t *config,
650 bool audio,
651 radio_callback_t callback,
652 void *cookie,
653 const struct radio_tuner **tuner)
654{
655 struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
656 int status = 0;
657
658 ALOGI("%s rdev %p", __func__, rdev);
659 pthread_mutex_lock(&rdev->lock);
660
661 if (rdev->tuner != NULL) {
Tomasz Wasilczyk12ddfd02017-04-17 17:05:38 -0700662 ALOGE("Can't open tuner twice");
Eric Laurent97d2ba62015-03-05 12:52:14 -0800663 status = -ENOSYS;
664 goto exit;
665 }
666
667 if (config == NULL || callback == NULL || tuner == NULL) {
668 status = -EINVAL;
669 goto exit;
670 }
671
672 rdev->tuner = (struct stub_radio_tuner *)calloc(1, sizeof(struct stub_radio_tuner));
673 if (rdev->tuner == NULL) {
674 status = -ENOMEM;
675 goto exit;
676 }
677
678 rdev->tuner->interface.set_configuration = tuner_set_configuration;
679 rdev->tuner->interface.get_configuration = tuner_get_configuration;
680 rdev->tuner->interface.scan = tuner_scan;
681 rdev->tuner->interface.step = tuner_step;
682 rdev->tuner->interface.tune = tuner_tune;
683 rdev->tuner->interface.cancel = tuner_cancel;
684 rdev->tuner->interface.get_program_information = tuner_get_program_information;
685
686 rdev->tuner->audio = audio;
687 rdev->tuner->callback = callback;
688 rdev->tuner->cookie = cookie;
689
690 rdev->tuner->dev = rdev;
691
692 pthread_mutex_init(&rdev->tuner->lock, (const pthread_mutexattr_t *) NULL);
693 pthread_cond_init(&rdev->tuner->cond, (const pthread_condattr_t *) NULL);
694 pthread_create(&rdev->tuner->callback_thread, (const pthread_attr_t *) NULL,
695 callback_thread_loop, rdev->tuner);
696 list_init(&rdev->tuner->command_list);
697
698 pthread_mutex_lock(&rdev->tuner->lock);
699 send_command_l(rdev->tuner, CMD_CONFIG, 500, (void *)config);
700 pthread_mutex_unlock(&rdev->tuner->lock);
701
702 *tuner = &rdev->tuner->interface;
703
704exit:
705 pthread_mutex_unlock(&rdev->lock);
706 ALOGI("%s DONE", __func__);
707 return status;
708}
709
710static int rdev_close_tuner(const struct radio_hw_device *dev,
711 const struct radio_tuner *tuner)
712{
713 struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
714 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
715 int status = 0;
716
717 ALOGI("%s tuner %p", __func__, tuner);
718 pthread_mutex_lock(&rdev->lock);
719
720 if (tuner == NULL) {
721 status = -EINVAL;
722 goto exit;
723 }
724
725 pthread_mutex_lock(&stub_tuner->lock);
726 stub_tuner->callback = NULL;
727 send_command_l(stub_tuner, CMD_EXIT, 0, NULL);
728 pthread_mutex_unlock(&stub_tuner->lock);
729 pthread_join(stub_tuner->callback_thread, (void **) NULL);
730
731 if (stub_tuner->program.metadata != NULL)
732 radio_metadata_deallocate(stub_tuner->program.metadata);
733
734 free(stub_tuner);
735 rdev->tuner = NULL;
736
737exit:
738 pthread_mutex_unlock(&rdev->lock);
739 return status;
740}
741
742static int rdev_close(hw_device_t *device)
743{
744 struct stub_radio_device *rdev = (struct stub_radio_device *)device;
745 if (rdev != NULL) {
746 free(rdev->tuner);
747 }
748 free(rdev);
749 return 0;
750}
751
752static int rdev_open(const hw_module_t* module, const char* name,
753 hw_device_t** device)
754{
755 struct stub_radio_device *rdev;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800756
757 if (strcmp(name, RADIO_HARDWARE_DEVICE) != 0)
758 return -EINVAL;
759
760 rdev = calloc(1, sizeof(struct stub_radio_device));
761 if (!rdev)
762 return -ENOMEM;
763
764 rdev->device.common.tag = HARDWARE_DEVICE_TAG;
765 rdev->device.common.version = RADIO_DEVICE_API_VERSION_1_0;
766 rdev->device.common.module = (struct hw_module_t *) module;
767 rdev->device.common.close = rdev_close;
768 rdev->device.get_properties = rdev_get_properties;
769 rdev->device.open_tuner = rdev_open_tuner;
770 rdev->device.close_tuner = rdev_close_tuner;
771
772 pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL);
773
774 *device = &rdev->device.common;
775
776 return 0;
777}
778
779
780static struct hw_module_methods_t hal_module_methods = {
781 .open = rdev_open,
782};
783
784struct radio_module HAL_MODULE_INFO_SYM = {
785 .common = {
786 .tag = HARDWARE_MODULE_TAG,
787 .module_api_version = RADIO_MODULE_API_VERSION_1_0,
788 .hal_api_version = HARDWARE_HAL_API_VERSION,
789 .id = RADIO_HARDWARE_MODULE_ID,
790 .name = "Stub radio HAL",
791 .author = "The Android Open Source Project",
792 .methods = &hal_module_methods,
793 },
794};