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