1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <limits.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <sys/poll.h>
25 #include <sys/ioctl.h>
26 #include <linux/dvb/dmx.h>
27 #include <linux/dvb/frontend.h>
28
29 #define LOG_TAG "DvbManager"
30 #include "logging.h"
31
32 #include "DvbManager.h"
33
currentTimeMillis()34 static double currentTimeMillis() {
35 struct timeval tv;
36 gettimeofday(&tv, (struct timezone *) NULL);
37 return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
38 }
39
DvbManager(JNIEnv * env,jobject)40 DvbManager::DvbManager(JNIEnv *env, jobject)
41 : mFeFd(-1),
42 mDvrFd(-1),
43 mPatFilterFd(-1),
44 mDvbApiVersion(DVB_API_VERSION_UNDEFINED),
45 mDeliverySystemType(-1),
46 mFeHasLock(false),
47 mHasPendingTune(false) {
48 jclass clazz = env->FindClass("com/android/tv/tuner/TunerHal");
49 mOpenDvbFrontEndMethodID =
50 env->GetMethodID(clazz, "openDvbFrontEndFd", "()I");
51 mOpenDvbDemuxMethodID = env->GetMethodID(clazz, "openDvbDemuxFd", "()I");
52 mOpenDvbDvrMethodID = env->GetMethodID(clazz, "openDvbDvrFd", "()I");
53 }
54
~DvbManager()55 DvbManager::~DvbManager() {
56 reset();
57 }
58
isFeLocked()59 bool DvbManager::isFeLocked() {
60 if (mDvbApiVersion == DVB_API_VERSION5) {
61 fe_status_t status;
62 if (ioctl(mFeFd, FE_READ_STATUS, &status) < 0) {
63 return false;
64 }
65 if (status & FE_HAS_LOCK) {
66 return true;
67 }
68 } else {
69 struct pollfd pollFd;
70 pollFd.fd = mFeFd;
71 pollFd.events = POLLIN;
72 pollFd.revents = 0;
73 int poll_result = poll(&pollFd, NUM_POLLFDS, FE_POLL_TIMEOUT_MS);
74 if (poll_result > 0 && (pollFd.revents & POLLIN)) {
75 struct dvb_frontend_event kevent;
76 memset(&kevent, 0, sizeof(kevent));
77 if (ioctl(mFeFd, FE_GET_EVENT, &kevent) == 0) {
78 return (kevent.status & FE_HAS_LOCK);
79 }
80 }
81 }
82 return false;
83 }
84
tune(JNIEnv * env,jobject thiz,const int frequency,const char * modulationStr,int timeout_ms)85 int DvbManager::tune(JNIEnv *env, jobject thiz,
86 const int frequency, const char *modulationStr, int timeout_ms) {
87 resetExceptFe();
88
89 if (openDvbFe(env, thiz) != 0) {
90 return -1;
91 }
92
93 if (frequency < 0) {
94 return -1;
95 }
96
97 if (mDvbApiVersion == DVB_API_VERSION_UNDEFINED) {
98 struct dtv_property testProps[1] = {
99 { .cmd = DTV_DELIVERY_SYSTEM }
100 };
101 struct dtv_properties feProp = {
102 .num = 1, .props = testProps
103 };
104 // On fugu, DVB_API_VERSION is 5 but it doesn't support FE_SET_PROPERTY. Checking the device
105 // support FE_GET_PROPERTY or not to determine the DVB API version is greater than 5 or not.
106 if (ioctl(mFeFd, FE_GET_PROPERTY, &feProp) == -1) {
107 ALOGD("FE_GET_PROPERTY failed, %s", strerror(errno));
108 mDvbApiVersion = DVB_API_VERSION3;
109 } else {
110 mDvbApiVersion = DVB_API_VERSION5;
111 }
112 }
113
114 if (mDvbApiVersion == DVB_API_VERSION5) {
115 struct dtv_property deliverySystemProperty = {
116 .cmd = DTV_DELIVERY_SYSTEM
117 };
118 deliverySystemProperty.u.data = SYS_ATSC;
119 struct dtv_property frequencyProperty = {
120 .cmd = DTV_FREQUENCY
121 };
122 frequencyProperty.u.data = static_cast<__u32>(frequency);
123 struct dtv_property modulationProperty = { .cmd = DTV_MODULATION };
124 if (strncmp(modulationStr, "QAM", 3) == 0) {
125 modulationProperty.u.data = QAM_AUTO;
126 } else if (strcmp(modulationStr, "8VSB") == 0) {
127 modulationProperty.u.data = VSB_8;
128 } else {
129 ALOGE("Unrecognized modulation mode : %s", modulationStr);
130 return -1;
131 }
132 struct dtv_property tuneProperty = { .cmd = DTV_TUNE };
133
134 struct dtv_property props[] = {
135 deliverySystemProperty, frequencyProperty, modulationProperty, tuneProperty
136 };
137 struct dtv_properties dtvProperty = {
138 .num = 4, .props = props
139 };
140
141 if (mHasPendingTune) {
142 return -1;
143 }
144 if (ioctl(mFeFd, FE_SET_PROPERTY, &dtvProperty) != 0) {
145 ALOGD("Can't set Frontend : %s", strerror(errno));
146 return -1;
147 }
148 } else {
149 struct dvb_frontend_parameters feParams;
150 memset(&feParams, 0, sizeof(struct dvb_frontend_parameters));
151 feParams.frequency = frequency;
152 feParams.inversion = INVERSION_AUTO;
153 /* Check frontend capability */
154 struct dvb_frontend_info feInfo;
155 if (ioctl(mFeFd, FE_GET_INFO, &feInfo) != -1) {
156 if (!(feInfo.caps & FE_CAN_INVERSION_AUTO)) {
157 // FE can't do INVERSION_AUTO, trying INVERSION_OFF instead
158 feParams.inversion = INVERSION_OFF;
159 }
160 }
161 switch (feInfo.type) {
162 case FE_ATSC:
163 if (strcmp(modulationStr, "8VSB") == 0) {
164 feParams.u.vsb.modulation = VSB_8;
165 } else if (strncmp(modulationStr, "QAM", 3) == 0) {
166 feParams.u.vsb.modulation = QAM_AUTO;
167 } else {
168 ALOGE("Unrecognized modulation mode : %s", modulationStr);
169 return -1;
170 }
171 break;
172 case FE_OFDM:
173 if (strcmp(modulationStr, "8VSB") == 0) {
174 feParams.u.ofdm.constellation = VSB_8;
175 } else if (strcmp(modulationStr, "QAM16") == 0) {
176 feParams.u.ofdm.constellation = QAM_16;
177 } else if (strcmp(modulationStr, "QAM64") == 0) {
178 feParams.u.ofdm.constellation = QAM_64;
179 } else if (strcmp(modulationStr, "QAM256") == 0) {
180 feParams.u.ofdm.constellation = QAM_256;
181 } else if (strcmp(modulationStr, "QPSK") == 0) {
182 feParams.u.ofdm.constellation = QPSK;
183 } else {
184 ALOGE("Unrecognized modulation mode : %s", modulationStr);
185 return -1;
186 }
187 break;
188 default:
189 ALOGE("Unsupported delivery system.");
190 return -1;
191 }
192
193 if (mHasPendingTune) {
194 return -1;
195 }
196
197 if (ioctl(mFeFd, FE_SET_FRONTEND, &feParams) != 0) {
198 ALOGD("Can't set Frontend : %s", strerror(errno));
199 return -1;
200 }
201 }
202
203 int lockSuccessCount = 0;
204 double tuneClock = currentTimeMillis();
205 while (currentTimeMillis() - tuneClock < timeout_ms) {
206 if (mHasPendingTune) {
207 // Return 0 here since we already call FE_SET_FRONTEND, and return due to having pending
208 // tune request. And the frontend setting could be successful.
209 mFeHasLock = true;
210 return 0;
211 }
212 bool lockStatus = isFeLocked();
213 if (lockStatus) {
214 lockSuccessCount++;
215 } else {
216 lockSuccessCount = 0;
217 }
218 ALOGI("Lock status : %s", lockStatus ? "true" : "false");
219 if (lockSuccessCount >= FE_CONSECUTIVE_LOCK_SUCCESS_COUNT) {
220 mFeHasLock = true;
221 openDvbDvr(env, thiz);
222 return 0;
223 }
224 }
225
226 return -1;
227 }
228
stopTune()229 int DvbManager::stopTune() {
230 reset();
231 usleep(DVB_TUNE_STOP_DELAY_MS);
232 return 0;
233 }
234
openDvbFeFromSystemApi(JNIEnv * env,jobject thiz)235 int DvbManager::openDvbFeFromSystemApi(JNIEnv *env, jobject thiz) {
236 int fd = (int) env->CallIntMethod(thiz, mOpenDvbFrontEndMethodID);
237 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
238 return fd;
239 }
240
openDvbDemuxFromSystemApi(JNIEnv * env,jobject thiz)241 int DvbManager::openDvbDemuxFromSystemApi(JNIEnv *env, jobject thiz) {
242 int fd = (int) env->CallIntMethod(thiz, mOpenDvbDemuxMethodID);
243 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
244 return fd;
245 }
246
openDvbDvrFromSystemApi(JNIEnv * env,jobject thiz)247 int DvbManager::openDvbDvrFromSystemApi(JNIEnv *env, jobject thiz) {
248 int fd = (int) env->CallIntMethod(thiz, mOpenDvbDvrMethodID);
249 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
250 return fd;
251 }
252
openDvbFe(JNIEnv * env,jobject thiz)253 int DvbManager::openDvbFe(JNIEnv *env, jobject thiz) {
254 if (mFeFd == -1) {
255 if ((mFeFd = openDvbFeFromSystemApi(env, thiz)) < 0) {
256 ALOGD("Can't open FE file : %s", strerror(errno));
257 return -1;
258 }
259 }
260
261 struct dvb_frontend_info info;
262 if (ioctl(mFeFd, FE_GET_INFO, &info) == 0) {
263 const char *types;
264 switch (info.type) {
265 case FE_QPSK:
266 types = "DVB-S";
267 break;
268 case FE_QAM:
269 types = "DVB-C";
270 break;
271 case FE_OFDM:
272 types = "DVB-T";
273 break;
274 case FE_ATSC:
275 types = "ATSC";
276 break;
277 default:
278 types = "Unknown";
279 }
280 ALOGI("Using frontend \"%s\", type %s", info.name, types);
281 }
282 return 0;
283 }
284
startTsPidFilter(JNIEnv * env,jobject thiz,int pid,int filterType)285 int DvbManager::startTsPidFilter(JNIEnv *env, jobject thiz, int pid, int filterType) {
286 Mutex::Autolock autoLock(mFilterLock);
287
288 if (mPidFilters.find(pid) != mPidFilters.end() || (mPatFilterFd != -1 && pid == PAT_PID)) {
289 return 0;
290 }
291
292 if (mHasPendingTune) {
293 return -1;
294 }
295
296 int demuxFd;
297 if ((demuxFd = openDvbDemuxFromSystemApi(env, thiz)) < 0) {
298 ALOGD("Can't open DEMUX file : %s", strerror(errno));
299 return -1;
300 }
301
302 struct dmx_pes_filter_params filter;
303 memset(&filter, 0, sizeof(filter));
304 filter.pid = pid;
305 filter.input = DMX_IN_FRONTEND;
306 switch (filterType) {
307 case FILTER_TYPE_AUDIO:
308 filter.pes_type = DMX_PES_AUDIO;
309 break;
310 case FILTER_TYPE_VIDEO:
311 filter.pes_type = DMX_PES_VIDEO;
312 break;
313 case FILTER_TYPE_PCR:
314 filter.pes_type = DMX_PES_PCR;
315 break;
316 default:
317 filter.pes_type = DMX_PES_OTHER;
318 break;
319 }
320 filter.output = DMX_OUT_TS_TAP;
321 filter.flags |= (DMX_CHECK_CRC | DMX_IMMEDIATE_START);
322
323 // create a pes filter
324 if (ioctl(demuxFd, DMX_SET_PES_FILTER, &filter)) {
325 close(demuxFd);
326 return -1;
327 }
328
329 if (mDvbApiVersion == DVB_API_VERSION5) {
330 ioctl(demuxFd, DMX_START, 0);
331 }
332
333 if (pid != PAT_PID) {
334 mPidFilters.insert(std::pair<int, int>(pid, demuxFd));
335 } else {
336 mPatFilterFd = demuxFd;
337 }
338
339 return 0;
340 }
341
closeAllDvbPidFilter()342 void DvbManager::closeAllDvbPidFilter() {
343 // Close all dvb pid filters except PAT filter to maintain the opening status of the device.
344 Mutex::Autolock autoLock(mFilterLock);
345
346 for (std::map<int, int>::iterator it(mPidFilters.begin());
347 it != mPidFilters.end(); it++) {
348 close(it->second);
349 }
350 mPidFilters.clear();
351 // Close mDvrFd to make sure there is not buffer from previous channel left.
352 closeDvbDvr();
353 }
354
closePatFilter()355 void DvbManager::closePatFilter() {
356 Mutex::Autolock autoLock(mFilterLock);
357
358 if (mPatFilterFd != -1) {
359 close(mPatFilterFd);
360 mPatFilterFd = -1;
361 }
362 }
363
openDvbDvr(JNIEnv * env,jobject thiz)364 int DvbManager::openDvbDvr(JNIEnv *env, jobject thiz) {
365 if ((mDvrFd = openDvbDvrFromSystemApi(env, thiz)) < 0) {
366 ALOGD("Can't open DVR file : %s", strerror(errno));
367 return -1;
368 }
369 return 0;
370 }
371
closeDvbFe()372 void DvbManager::closeDvbFe() {
373 if (mFeFd != -1) {
374 close(mFeFd);
375 mFeFd = -1;
376 }
377 }
378
closeDvbDvr()379 void DvbManager::closeDvbDvr() {
380 if (mDvrFd != -1) {
381 close(mDvrFd);
382 mDvrFd = -1;
383 }
384 }
385
reset()386 void DvbManager::reset() {
387 mFeHasLock = false;
388 closeDvbDvr();
389 closeAllDvbPidFilter();
390 closePatFilter();
391 closeDvbFe();
392 }
393
resetExceptFe()394 void DvbManager::resetExceptFe() {
395 mFeHasLock = false;
396 closeDvbDvr();
397 closeAllDvbPidFilter();
398 closePatFilter();
399 }
400
readTsStream(JNIEnv * env,jobject thiz,uint8_t * tsBuffer,int tsBufferSize,int timeout_ms)401 int DvbManager::readTsStream(JNIEnv *env, jobject thiz,
402 uint8_t *tsBuffer, int tsBufferSize, int timeout_ms) {
403 if (!mFeHasLock) {
404 usleep(DVB_ERROR_RETRY_INTERVAL_MS);
405 return -1;
406 }
407
408 if (mDvrFd == -1) {
409 openDvbDvr(env, thiz);
410 }
411
412 struct pollfd pollFd;
413 pollFd.fd = mDvrFd;
414 pollFd.events = POLLIN|POLLPRI|POLLERR;
415 pollFd.revents = 0;
416 int poll_result = poll(&pollFd, NUM_POLLFDS, timeout_ms);
417 if (poll_result == 0) {
418 return 0;
419 } else if (poll_result == -1 || pollFd.revents & POLLERR) {
420 ALOGD("Can't read DVR : %s", strerror(errno));
421 // TODO: Find how to recover this situation correctly.
422 closeDvbDvr();
423 usleep(DVB_ERROR_RETRY_INTERVAL_MS);
424 return -1;
425 }
426 return read(mDvrFd, tsBuffer, tsBufferSize);
427 }
428
setHasPendingTune(bool hasPendingTune)429 void DvbManager::setHasPendingTune(bool hasPendingTune) {
430 mHasPendingTune = hasPendingTune;
431 }
432
getDeliverySystemType(JNIEnv * env,jobject thiz)433 int DvbManager::getDeliverySystemType(JNIEnv *env, jobject thiz) {
434 if (mDeliverySystemType != -1) {
435 return mDeliverySystemType;
436 }
437 if (mFeFd == -1) {
438 if ((mFeFd = openDvbFeFromSystemApi(env, thiz)) < 0) {
439 ALOGD("Can't open FE file : %s", strerror(errno));
440 return DELIVERY_SYSTEM_UNDEFINED;
441 }
442 }
443 struct dtv_property testProps[1] = {
444 { .cmd = DTV_DELIVERY_SYSTEM }
445 };
446 struct dtv_properties feProp = {
447 .num = 1, .props = testProps
448 };
449 mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
450 if (ioctl(mFeFd, FE_GET_PROPERTY, &feProp) == -1) {
451 mDvbApiVersion = DVB_API_VERSION3;
452 if (openDvbFe(env, thiz) == 0) {
453 struct dvb_frontend_info info;
454 if (ioctl(mFeFd, FE_GET_INFO, &info) == 0) {
455 switch (info.type) {
456 case FE_QPSK:
457 mDeliverySystemType = DELIVERY_SYSTEM_DVBS;
458 break;
459 case FE_QAM:
460 mDeliverySystemType = DELIVERY_SYSTEM_DVBC;
461 break;
462 case FE_OFDM:
463 mDeliverySystemType = DELIVERY_SYSTEM_DVBT;
464 break;
465 case FE_ATSC:
466 mDeliverySystemType = DELIVERY_SYSTEM_ATSC;
467 break;
468 default:
469 mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
470 break;
471 }
472 }
473 }
474 } else {
475 mDvbApiVersion = DVB_API_VERSION5;
476 switch (feProp.props[0].u.data) {
477 case SYS_DVBT:
478 mDeliverySystemType = DELIVERY_SYSTEM_DVBT;
479 break;
480 case SYS_DVBT2:
481 mDeliverySystemType = DELIVERY_SYSTEM_DVBT2;
482 break;
483 case SYS_DVBS:
484 mDeliverySystemType = DELIVERY_SYSTEM_DVBS;
485 break;
486 case SYS_DVBS2:
487 mDeliverySystemType = DELIVERY_SYSTEM_DVBS2;
488 break;
489 case SYS_DVBC_ANNEX_A:
490 case SYS_DVBC_ANNEX_B:
491 case SYS_DVBC_ANNEX_C:
492 mDeliverySystemType = DELIVERY_SYSTEM_DVBC;
493 break;
494 case SYS_ATSC:
495 mDeliverySystemType = DELIVERY_SYSTEM_ATSC;
496 break;
497 default:
498 mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
499 break;
500 }
501 }
502 return mDeliverySystemType;
503 }