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