blob: 93b8c88913a47f0843d835ed0783087c77bc2c06 [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,
Liu Changchengd1d3a962017-04-18 08:59:10 +0800117 CMD_NUM
Eric Laurent97d2ba62015-03-05 12:52:14 -0800118} thread_cmd_type_t;
119
Liu Changchengd1d3a962017-04-18 08:59:10 +0800120uint32_t thread_cmd_delay_ms[CMD_NUM] = {
121 [CMD_EXIT] = 0,
122 [CMD_CONFIG] = 50,
123 [CMD_STEP] = 100,
124 [CMD_SCAN] = 200,
125 [CMD_TUNE] = 150,
126 [CMD_CANCEL] = 0,
127 [CMD_METADATA] = 1000,
128 [CMD_ANNOUNCEMENTS] = 1000
129};
Eric Laurent97d2ba62015-03-05 12:52:14 -0800130struct thread_command {
131 struct listnode node;
132 thread_cmd_type_t type;
133 struct timespec ts;
134 union {
135 unsigned int param;
136 radio_hal_band_config_t config;
137 };
138};
139
140/* must be called with out->lock locked */
141static int send_command_l(struct stub_radio_tuner *tuner,
142 thread_cmd_type_t type,
143 unsigned int delay_ms,
144 void *param)
145{
146 struct thread_command *cmd = (struct thread_command *)calloc(1, sizeof(struct thread_command));
147 struct timespec ts;
148
149 if (cmd == NULL)
150 return -ENOMEM;
151
152 ALOGV("%s %d delay_ms %d", __func__, type, delay_ms);
153
154 cmd->type = type;
155 if (param != NULL) {
156 if (cmd->type == CMD_CONFIG) {
157 cmd->config = *(radio_hal_band_config_t *)param;
158 ALOGV("%s CMD_CONFIG type %d", __func__, cmd->config.type);
159 } else
160 cmd->param = *(unsigned int *)param;
161 }
162
163 clock_gettime(CLOCK_REALTIME, &ts);
164
165 ts.tv_sec += delay_ms/1000;
166 ts.tv_nsec += (delay_ms%1000) * 1000000;
167 if (ts.tv_nsec >= 1000000000) {
168 ts.tv_nsec -= 1000000000;
169 ts.tv_sec += 1;
170 }
171 cmd->ts = ts;
172 list_add_tail(&tuner->command_list, &cmd->node);
173 pthread_cond_signal(&tuner->cond);
174 return 0;
175}
176
Eric Laurent854a10a2016-02-19 14:41:51 -0800177#define BITMAP_FILE_PATH "/data/misc/audioserver/android.png"
Eric Laurent97d2ba62015-03-05 12:52:14 -0800178
179static int add_bitmap_metadata(radio_metadata_t **metadata, radio_metadata_key_t key,
180 const char *source)
181{
182 int fd;
183 ssize_t ret = 0;
184 struct stat info;
185 void *data = NULL;
186 size_t size;
187
188 fd = open(source, O_RDONLY);
189 if (fd < 0)
190 return -EPIPE;
191
192 fstat(fd, &info);
193 size = info.st_size;
194 data = malloc(size);
195 if (data == NULL) {
196 ret = -ENOMEM;
197 goto exit;
198 }
199 ret = read(fd, data, size);
200 if (ret < 0)
201 goto exit;
202 ret = radio_metadata_add_raw(metadata, key, (const unsigned char *)data, size);
203
204exit:
205 close(fd);
206 free(data);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700207 ALOGE_IF(ret != 0, "%s error %d", __func__, (int)ret);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800208 return (int)ret;
209}
210
211static int prepare_metadata(struct stub_radio_tuner *tuner,
212 radio_metadata_t **metadata, bool program)
213{
214 int ret = 0;
215 char text[RADIO_STRING_LEN_MAX];
216 struct timespec ts;
217
218 if (metadata == NULL)
219 return -EINVAL;
220
221 if (*metadata != NULL)
222 radio_metadata_deallocate(*metadata);
223
224 *metadata = NULL;
225
226 ret = radio_metadata_allocate(metadata, tuner->program.channel, 0);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700227
Eric Laurent97d2ba62015-03-05 12:52:14 -0800228 if (ret != 0)
229 return ret;
230
231 if (program) {
232 ret = radio_metadata_add_int(metadata, RADIO_METADATA_KEY_RBDS_PTY, 5);
233 if (ret != 0)
234 goto exit;
235 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_RDS_PS, "RockBand");
236 if (ret != 0)
237 goto exit;
238 ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ICON, BITMAP_FILE_PATH);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700239 if (ret != 0 && ret != -EPIPE)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800240 goto exit;
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700241 ret = radio_metadata_add_clock(metadata, RADIO_METADATA_KEY_CLOCK, &hw_clock);
242 if (ret != 0)
243 goto exit;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800244 } else {
245 ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ART, BITMAP_FILE_PATH);
Eric Laurentc37e6fe2016-10-25 11:20:54 -0700246 if (ret != 0 && ret != -EPIPE)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800247 goto exit;
248 }
249
250 clock_gettime(CLOCK_REALTIME, &ts);
251 snprintf(text, RADIO_STRING_LEN_MAX, "Artist %ld", ts.tv_sec % 10);
252 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_ARTIST, text);
253 if (ret != 0)
254 goto exit;
255
256 snprintf(text, RADIO_STRING_LEN_MAX, "Song %ld", ts.tv_nsec % 10);
257 ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_TITLE, text);
258 if (ret != 0)
259 goto exit;
260
261 return 0;
262
263exit:
264 radio_metadata_deallocate(*metadata);
265 *metadata = NULL;
266 return ret;
267}
268
269static void *callback_thread_loop(void *context)
270{
271 struct stub_radio_tuner *tuner = (struct stub_radio_tuner *)context;
272 struct timespec ts = {0, 0};
273
274 ALOGI("%s", __func__);
275
276 prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0);
277
278 pthread_mutex_lock(&tuner->lock);
279
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700280 // Fields which are used to toggle the state of traffic announcements and
281 // ea announcements at random. They are access protected by tuner->lock.
282 bool ea_state = false;
283
Eric Laurent97d2ba62015-03-05 12:52:14 -0800284 while (true) {
285 struct thread_command *cmd = NULL;
286 struct listnode *item;
287 struct listnode *tmp;
288 struct timespec cur_ts;
Eric Laurentacccf642015-04-23 19:11:36 -0700289 bool got_cancel = false;
290 bool send_meta_data = false;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800291
292 if (list_empty(&tuner->command_list) || ts.tv_sec != 0) {
293 ALOGV("%s SLEEPING", __func__);
294 if (ts.tv_sec != 0) {
295 ALOGV("%s SLEEPING with timeout", __func__);
296 pthread_cond_timedwait(&tuner->cond, &tuner->lock, &ts);
297 } else {
298 ALOGV("%s SLEEPING forever", __func__);
299 pthread_cond_wait(&tuner->cond, &tuner->lock);
300 }
301 ts.tv_sec = 0;
302 ALOGV("%s RUNNING", __func__);
303 }
304
305 clock_gettime(CLOCK_REALTIME, &cur_ts);
306
307 list_for_each_safe(item, tmp, &tuner->command_list) {
308 cmd = node_to_item(item, struct thread_command, node);
309
Eric Laurentacccf642015-04-23 19:11:36 -0700310 if (got_cancel && (cmd->type == CMD_STEP || cmd->type == CMD_SCAN ||
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700311 cmd->type == CMD_TUNE || cmd->type == CMD_METADATA ||
312 cmd->type == CMD_ANNOUNCEMENTS)) {
Eric Laurentacccf642015-04-23 19:11:36 -0700313 list_remove(item);
314 free(cmd);
315 continue;
316 }
317
Eric Laurent97d2ba62015-03-05 12:52:14 -0800318 if ((cmd->ts.tv_sec < cur_ts.tv_sec) ||
319 ((cmd->ts.tv_sec == cur_ts.tv_sec) && (cmd->ts.tv_nsec < cur_ts.tv_nsec))) {
320 radio_hal_event_t event;
Eric Laurentacccf642015-04-23 19:11:36 -0700321 radio_metadata_t *metadata = NULL;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800322
323 event.type = RADIO_EVENT_HW_FAILURE;
324 list_remove(item);
325
326 ALOGV("%s processing command %d time %ld.%ld", __func__, cmd->type, cmd->ts.tv_sec,
327 cmd->ts.tv_nsec);
328
329 switch (cmd->type) {
330 default:
331 case CMD_EXIT:
332 free(cmd);
333 goto exit;
334
335 case CMD_CONFIG: {
336 tuner->config = cmd->config;
Tomasz Wasilczyk13f56282017-05-17 16:23:28 -0700337 tuner->config.antenna_connected = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800338 event.type = RADIO_EVENT_CONFIG;
339 event.config = tuner->config;
340 ALOGV("%s CMD_CONFIG type %d low %d up %d",
341 __func__, tuner->config.type,
342 tuner->config.lower_limit, tuner->config.upper_limit);
343 if (tuner->config.type == RADIO_BAND_FM) {
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700344 ALOGV(" - stereo %d\n - rds %d\n - ta %d\n - af %d\n"
345 " - ea %d\n",
Eric Laurent97d2ba62015-03-05 12:52:14 -0800346 tuner->config.fm.stereo, tuner->config.fm.rds,
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700347 tuner->config.fm.ta, tuner->config.fm.af,
348 tuner->config.fm.ea);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800349 } else {
350 ALOGV(" - stereo %d", tuner->config.am.stereo);
351 }
352 } break;
353
354 case CMD_STEP: {
355 int frequency;
356 frequency = tuner->program.channel;
357 if (cmd->param == RADIO_DIRECTION_UP) {
358 frequency += tuner->config.spacings[0];
359 } else {
360 frequency -= tuner->config.spacings[0];
361 }
362 if (frequency > (int)tuner->config.upper_limit) {
363 frequency = tuner->config.lower_limit;
364 }
365 if (frequency < (int)tuner->config.lower_limit) {
366 frequency = tuner->config.upper_limit;
367 }
368 tuner->program.channel = frequency;
369 tuner->program.tuned = (frequency / (tuner->config.spacings[0] * 5)) % 2;
370 tuner->program.signal_strength = 20;
371 if (tuner->config.type == RADIO_BAND_FM)
372 tuner->program.stereo = false;
373 else
374 tuner->program.stereo = false;
Tomasz Wasilczyk3c7297a2017-01-06 14:19:52 -0800375 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800376
377 event.type = RADIO_EVENT_TUNED;
378 event.info = tuner->program;
379 } break;
380
381 case CMD_SCAN: {
382 int frequency;
383 frequency = tuner->program.channel;
384 if (cmd->param == RADIO_DIRECTION_UP) {
385 frequency += tuner->config.spacings[0] * 25;
386 } else {
387 frequency -= tuner->config.spacings[0] * 25;
388 }
389 if (frequency > (int)tuner->config.upper_limit) {
390 frequency = tuner->config.lower_limit;
391 }
392 if (frequency < (int)tuner->config.lower_limit) {
393 frequency = tuner->config.upper_limit;
394 }
395 tuner->program.channel = (unsigned int)frequency;
396 tuner->program.tuned = true;
397 if (tuner->config.type == RADIO_BAND_FM)
398 tuner->program.stereo = tuner->config.fm.stereo;
399 else
400 tuner->program.stereo = tuner->config.am.stereo;
401 tuner->program.signal_strength = 50;
Tomasz Wasilczyk3c7297a2017-01-06 14:19:52 -0800402 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800403
404 event.type = RADIO_EVENT_TUNED;
405 event.info = tuner->program;
Eric Laurentacccf642015-04-23 19:11:36 -0700406 send_meta_data = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800407 } break;
408
409 case CMD_TUNE: {
410 tuner->program.channel = cmd->param;
411 tuner->program.tuned = (tuner->program.channel /
412 (tuner->config.spacings[0] * 5)) % 2;
413
414 if (tuner->program.tuned) {
Liu Changchengd1d3a962017-04-18 08:59:10 +0800415 send_command_l(tuner, CMD_ANNOUNCEMENTS, thread_cmd_delay_ms[CMD_ANNOUNCEMENTS], NULL);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800416 }
417 tuner->program.signal_strength = 100;
418 if (tuner->config.type == RADIO_BAND_FM)
419 tuner->program.stereo =
420 tuner->program.tuned ? tuner->config.fm.stereo : false;
421 else
422 tuner->program.stereo =
423 tuner->program.tuned ? tuner->config.am.stereo : false;
Tomasz Wasilczyk3c7297a2017-01-06 14:19:52 -0800424 prepare_metadata(tuner, &tuner->program.metadata, tuner->program.tuned);
425
Eric Laurent97d2ba62015-03-05 12:52:14 -0800426 event.type = RADIO_EVENT_TUNED;
427 event.info = tuner->program;
Eric Laurentacccf642015-04-23 19:11:36 -0700428 send_meta_data = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800429 } break;
430
431 case CMD_METADATA: {
Eric Laurentacccf642015-04-23 19:11:36 -0700432 int ret = prepare_metadata(tuner, &metadata, false);
433 if (ret == 0) {
434 event.type = RADIO_EVENT_METADATA;
435 event.metadata = metadata;
436 }
Eric Laurent97d2ba62015-03-05 12:52:14 -0800437 } break;
438
439 case CMD_CANCEL: {
Eric Laurentacccf642015-04-23 19:11:36 -0700440 got_cancel = true;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800441 } break;
442
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700443 // Fire emergency announcements if they are enabled in the config. Stub
444 // implementation simply fires an announcement for 5 second
445 // duration with a gap of 5 seconds.
446 case CMD_ANNOUNCEMENTS: {
447 ALOGV("In annoucements. %d %d %d\n",
448 ea_state, tuner->config.type, tuner->config.fm.ea);
449 if (tuner->config.type == RADIO_BAND_FM ||
450 tuner->config.type == RADIO_BAND_FM_HD) {
451 if (ea_state) {
452 ea_state = false;
453 event.type = RADIO_EVENT_EA;
454 } else if (tuner->config.fm.ea) {
455 ea_state = true;
456 event.type = RADIO_EVENT_EA;
457 }
458 event.on = ea_state;
459
460 if (tuner->config.fm.ea) {
461 send_command_l(tuner, CMD_ANNOUNCEMENTS, 5000, NULL);
462 }
463 }
464 } break;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800465 }
466 if (event.type != RADIO_EVENT_HW_FAILURE && tuner->callback != NULL) {
467 pthread_mutex_unlock(&tuner->lock);
468 tuner->callback(&event, tuner->cookie);
469 pthread_mutex_lock(&tuner->lock);
Eric Laurentacccf642015-04-23 19:11:36 -0700470 if (event.type == RADIO_EVENT_METADATA && metadata != NULL) {
471 radio_metadata_deallocate(metadata);
472 metadata = NULL;
473 }
Eric Laurent97d2ba62015-03-05 12:52:14 -0800474 }
475 ALOGV("%s processed command %d", __func__, cmd->type);
476 free(cmd);
477 } else {
478 if ((ts.tv_sec == 0) ||
479 (cmd->ts.tv_sec < ts.tv_sec) ||
480 ((cmd->ts.tv_sec == ts.tv_sec) && (cmd->ts.tv_nsec < ts.tv_nsec))) {
481 ts.tv_sec = cmd->ts.tv_sec;
482 ts.tv_nsec = cmd->ts.tv_nsec;
483 }
484 }
485 }
Eric Laurentacccf642015-04-23 19:11:36 -0700486
487 if (send_meta_data) {
488 list_for_each_safe(item, tmp, &tuner->command_list) {
489 cmd = node_to_item(item, struct thread_command, node);
490 if (cmd->type == CMD_METADATA) {
491 list_remove(item);
492 free(cmd);
493 }
494 }
Liu Changchengd1d3a962017-04-18 08:59:10 +0800495 send_command_l(tuner, CMD_METADATA, thread_cmd_delay_ms[CMD_METADATA], NULL);
Eric Laurentacccf642015-04-23 19:11:36 -0700496 }
Eric Laurent97d2ba62015-03-05 12:52:14 -0800497 }
498
499exit:
500 pthread_mutex_unlock(&tuner->lock);
501
502 ALOGV("%s Exiting", __func__);
503
504 return NULL;
505}
506
507
508static int tuner_set_configuration(const struct radio_tuner *tuner,
509 const radio_hal_band_config_t *config)
510{
511 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
512 int status = 0;
513
514 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
515 pthread_mutex_lock(&stub_tuner->lock);
516 if (config == NULL) {
517 status = -EINVAL;
518 goto exit;
519 }
Tomasz Wasilczykad78c8d2017-03-07 17:05:41 -0800520 if (config->lower_limit > config->upper_limit) {
521 status = -EINVAL;
522 goto exit;
523 }
Liu Changchengd1d3a962017-04-18 08:59:10 +0800524 send_command_l(stub_tuner, CMD_CANCEL, thread_cmd_delay_ms[CMD_CANCEL], NULL);
525 send_command_l(stub_tuner, CMD_CONFIG, thread_cmd_delay_ms[CMD_CONFIG], (void *)config);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800526
527exit:
528 pthread_mutex_unlock(&stub_tuner->lock);
529 return status;
530}
531
532static int tuner_get_configuration(const struct radio_tuner *tuner,
Sanket Agarwala83e0a82015-10-07 17:15:08 -0700533 radio_hal_band_config_t *config)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800534{
535 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
536 int status = 0;
537 struct listnode *item;
538 radio_hal_band_config_t *src_config;
539
540 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
541 pthread_mutex_lock(&stub_tuner->lock);
542 src_config = &stub_tuner->config;
543
544 if (config == NULL) {
545 status = -EINVAL;
546 goto exit;
547 }
548 list_for_each(item, &stub_tuner->command_list) {
549 struct thread_command *cmd = node_to_item(item, struct thread_command, node);
550 if (cmd->type == CMD_CONFIG) {
551 src_config = &cmd->config;
552 }
553 }
554 *config = *src_config;
555
556exit:
557 pthread_mutex_unlock(&stub_tuner->lock);
558 return status;
559}
560
561static int tuner_step(const struct radio_tuner *tuner,
562 radio_direction_t direction, bool skip_sub_channel)
563{
564 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
565
566 ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
567 __func__, stub_tuner, direction, skip_sub_channel);
568
569 pthread_mutex_lock(&stub_tuner->lock);
Liu Changchengd1d3a962017-04-18 08:59:10 +0800570 send_command_l(stub_tuner, CMD_STEP, thread_cmd_delay_ms[CMD_STEP], &direction);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800571 pthread_mutex_unlock(&stub_tuner->lock);
572 return 0;
573}
574
575static int tuner_scan(const struct radio_tuner *tuner,
576 radio_direction_t direction, bool skip_sub_channel)
577{
578 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
579
580 ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
581 __func__, stub_tuner, direction, skip_sub_channel);
582
583 pthread_mutex_lock(&stub_tuner->lock);
Liu Changchengd1d3a962017-04-18 08:59:10 +0800584 send_command_l(stub_tuner, CMD_SCAN, thread_cmd_delay_ms[CMD_SCAN], &direction);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800585 pthread_mutex_unlock(&stub_tuner->lock);
586 return 0;
587}
588
589static int tuner_tune(const struct radio_tuner *tuner,
590 unsigned int channel, unsigned int sub_channel)
591{
592 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
593
594 ALOGI("%s stub_tuner %p channel %d, sub_channel %d",
595 __func__, stub_tuner, channel, sub_channel);
596
597 pthread_mutex_lock(&stub_tuner->lock);
598 if (channel < stub_tuner->config.lower_limit || channel > stub_tuner->config.upper_limit) {
599 pthread_mutex_unlock(&stub_tuner->lock);
600 ALOGI("%s channel out of range", __func__);
601 return -EINVAL;
602 }
Liu Changchengd1d3a962017-04-18 08:59:10 +0800603 send_command_l(stub_tuner, CMD_TUNE, thread_cmd_delay_ms[CMD_TUNE], &channel);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800604 pthread_mutex_unlock(&stub_tuner->lock);
605 return 0;
606}
607
608static int tuner_cancel(const struct radio_tuner *tuner)
609{
610 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
611
612 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
613
614 pthread_mutex_lock(&stub_tuner->lock);
Liu Changchengd1d3a962017-04-18 08:59:10 +0800615 send_command_l(stub_tuner, CMD_CANCEL, thread_cmd_delay_ms[CMD_CANCEL], NULL);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800616 pthread_mutex_unlock(&stub_tuner->lock);
617 return 0;
618}
619
620static int tuner_get_program_information(const struct radio_tuner *tuner,
621 radio_program_info_t *info)
622{
623 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
624 int status = 0;
625 radio_metadata_t *metadata;
626
627 ALOGI("%s stub_tuner %p", __func__, stub_tuner);
628 pthread_mutex_lock(&stub_tuner->lock);
629 if (info == NULL) {
630 status = -EINVAL;
631 goto exit;
632 }
633 metadata = info->metadata;
634 *info = stub_tuner->program;
635 info->metadata = metadata;
Tomasz Wasilczyk33683bf2017-02-15 16:57:20 -0800636 if (metadata == NULL) {
637 ALOGE("%s metadata is a nullptr", __func__);
638 status = -EINVAL;
639 goto exit;
640 }
641 if (stub_tuner->program.metadata != NULL)
Eric Laurent97d2ba62015-03-05 12:52:14 -0800642 radio_metadata_add_metadata(&info->metadata, stub_tuner->program.metadata);
643
644exit:
645 pthread_mutex_unlock(&stub_tuner->lock);
646 return status;
647}
648
649static int rdev_get_properties(const struct radio_hw_device *dev,
650 radio_hal_properties_t *properties)
651{
Eric Laurent97d2ba62015-03-05 12:52:14 -0800652 ALOGI("%s", __func__);
653 if (properties == NULL)
654 return -EINVAL;
655 memcpy(properties, &hw_properties, sizeof(radio_hal_properties_t));
656 return 0;
657}
658
659static int rdev_open_tuner(const struct radio_hw_device *dev,
660 const radio_hal_band_config_t *config,
661 bool audio,
662 radio_callback_t callback,
663 void *cookie,
664 const struct radio_tuner **tuner)
665{
666 struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
667 int status = 0;
668
669 ALOGI("%s rdev %p", __func__, rdev);
670 pthread_mutex_lock(&rdev->lock);
671
672 if (rdev->tuner != NULL) {
Tomasz Wasilczyk12ddfd02017-04-17 17:05:38 -0700673 ALOGE("Can't open tuner twice");
Eric Laurent97d2ba62015-03-05 12:52:14 -0800674 status = -ENOSYS;
675 goto exit;
676 }
677
678 if (config == NULL || callback == NULL || tuner == NULL) {
679 status = -EINVAL;
680 goto exit;
681 }
682
683 rdev->tuner = (struct stub_radio_tuner *)calloc(1, sizeof(struct stub_radio_tuner));
684 if (rdev->tuner == NULL) {
685 status = -ENOMEM;
686 goto exit;
687 }
688
689 rdev->tuner->interface.set_configuration = tuner_set_configuration;
690 rdev->tuner->interface.get_configuration = tuner_get_configuration;
691 rdev->tuner->interface.scan = tuner_scan;
692 rdev->tuner->interface.step = tuner_step;
693 rdev->tuner->interface.tune = tuner_tune;
694 rdev->tuner->interface.cancel = tuner_cancel;
695 rdev->tuner->interface.get_program_information = tuner_get_program_information;
696
697 rdev->tuner->audio = audio;
698 rdev->tuner->callback = callback;
699 rdev->tuner->cookie = cookie;
700
701 rdev->tuner->dev = rdev;
702
703 pthread_mutex_init(&rdev->tuner->lock, (const pthread_mutexattr_t *) NULL);
704 pthread_cond_init(&rdev->tuner->cond, (const pthread_condattr_t *) NULL);
705 pthread_create(&rdev->tuner->callback_thread, (const pthread_attr_t *) NULL,
706 callback_thread_loop, rdev->tuner);
707 list_init(&rdev->tuner->command_list);
708
709 pthread_mutex_lock(&rdev->tuner->lock);
Liu Changchengd1d3a962017-04-18 08:59:10 +0800710 send_command_l(rdev->tuner, CMD_CONFIG, thread_cmd_delay_ms[CMD_CONFIG], (void *)config);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800711 pthread_mutex_unlock(&rdev->tuner->lock);
712
713 *tuner = &rdev->tuner->interface;
714
715exit:
716 pthread_mutex_unlock(&rdev->lock);
717 ALOGI("%s DONE", __func__);
718 return status;
719}
720
721static int rdev_close_tuner(const struct radio_hw_device *dev,
722 const struct radio_tuner *tuner)
723{
724 struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
725 struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
726 int status = 0;
727
728 ALOGI("%s tuner %p", __func__, tuner);
729 pthread_mutex_lock(&rdev->lock);
730
731 if (tuner == NULL) {
732 status = -EINVAL;
733 goto exit;
734 }
735
736 pthread_mutex_lock(&stub_tuner->lock);
737 stub_tuner->callback = NULL;
Liu Changchengd1d3a962017-04-18 08:59:10 +0800738 send_command_l(stub_tuner, CMD_EXIT, thread_cmd_delay_ms[CMD_EXIT], NULL);
Eric Laurent97d2ba62015-03-05 12:52:14 -0800739 pthread_mutex_unlock(&stub_tuner->lock);
740 pthread_join(stub_tuner->callback_thread, (void **) NULL);
741
742 if (stub_tuner->program.metadata != NULL)
743 radio_metadata_deallocate(stub_tuner->program.metadata);
744
745 free(stub_tuner);
746 rdev->tuner = NULL;
747
748exit:
749 pthread_mutex_unlock(&rdev->lock);
750 return status;
751}
752
753static int rdev_close(hw_device_t *device)
754{
755 struct stub_radio_device *rdev = (struct stub_radio_device *)device;
756 if (rdev != NULL) {
757 free(rdev->tuner);
758 }
759 free(rdev);
760 return 0;
761}
762
763static int rdev_open(const hw_module_t* module, const char* name,
764 hw_device_t** device)
765{
766 struct stub_radio_device *rdev;
Eric Laurent97d2ba62015-03-05 12:52:14 -0800767
768 if (strcmp(name, RADIO_HARDWARE_DEVICE) != 0)
769 return -EINVAL;
770
771 rdev = calloc(1, sizeof(struct stub_radio_device));
772 if (!rdev)
773 return -ENOMEM;
774
775 rdev->device.common.tag = HARDWARE_DEVICE_TAG;
776 rdev->device.common.version = RADIO_DEVICE_API_VERSION_1_0;
777 rdev->device.common.module = (struct hw_module_t *) module;
778 rdev->device.common.close = rdev_close;
779 rdev->device.get_properties = rdev_get_properties;
780 rdev->device.open_tuner = rdev_open_tuner;
781 rdev->device.close_tuner = rdev_close_tuner;
782
783 pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL);
784
785 *device = &rdev->device.common;
786
787 return 0;
788}
789
790
791static struct hw_module_methods_t hal_module_methods = {
792 .open = rdev_open,
793};
794
795struct radio_module HAL_MODULE_INFO_SYM = {
796 .common = {
797 .tag = HARDWARE_MODULE_TAG,
798 .module_api_version = RADIO_MODULE_API_VERSION_1_0,
799 .hal_api_version = HARDWARE_HAL_API_VERSION,
800 .id = RADIO_HARDWARE_MODULE_ID,
801 .name = "Stub radio HAL",
802 .author = "The Android Open Source Project",
803 .methods = &hal_module_methods,
804 },
805};