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 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 55 DvbManager::~DvbManager() { 56 reset(); 57 } 58 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 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 229 int DvbManager::stopTune() { 230 reset(); 231 usleep(DVB_TUNE_STOP_DELAY_MS); 232 return 0; 233 } 234 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 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 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 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 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 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 355 void DvbManager::closePatFilter() { 356 Mutex::Autolock autoLock(mFilterLock); 357 358 if (mPatFilterFd != -1) { 359 close(mPatFilterFd); 360 mPatFilterFd = -1; 361 } 362 } 363 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 372 void DvbManager::closeDvbFe() { 373 if (mFeFd != -1) { 374 close(mFeFd); 375 mFeFd = -1; 376 } 377 } 378 379 void DvbManager::closeDvbDvr() { 380 if (mDvrFd != -1) { 381 close(mDvrFd); 382 mDvrFd = -1; 383 } 384 } 385 386 void DvbManager::reset() { 387 mFeHasLock = false; 388 closeDvbDvr(); 389 closeAllDvbPidFilter(); 390 closePatFilter(); 391 closeDvbFe(); 392 } 393 394 void DvbManager::resetExceptFe() { 395 mFeHasLock = false; 396 closeDvbDvr(); 397 closeAllDvbPidFilter(); 398 closePatFilter(); 399 } 400 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 429 void DvbManager::setHasPendingTune(bool hasPendingTune) { 430 mHasPendingTune = hasPendingTune; 431 } 432 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 }