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 mFeHasLock(false), 46 mHasPendingTune(false) { 47 jclass clazz = env->FindClass( 48 "com/android/tv/tuner/TunerHal"); 49 mOpenDvbFrontEndMethodID = env->GetMethodID( 50 clazz, "openDvbFrontEndFd", "()I"); 51 mOpenDvbDemuxMethodID = env->GetMethodID( 52 clazz, "openDvbDemuxFd", "()I"); 53 mOpenDvbDvrMethodID = env->GetMethodID( 54 clazz, "openDvbDvrFd", "()I"); 55 } 56 57 DvbManager::~DvbManager() { 58 reset(); 59 } 60 61 bool DvbManager::isFeLocked() { 62 struct pollfd pollFd; 63 pollFd.fd = mFeFd; 64 pollFd.events = POLLIN; 65 pollFd.revents = 0; 66 int poll_result = poll(&pollFd, NUM_POLLFDS, FE_POLL_TIMEOUT_MS); 67 if (poll_result > 0 && (pollFd.revents & POLLIN)) { 68 struct dvb_frontend_event kevent; 69 memset(&kevent, 0, sizeof(kevent)); 70 if (ioctl(mFeFd, FE_GET_EVENT, &kevent) == 0) { 71 return (kevent.status & FE_HAS_LOCK); 72 } 73 } 74 return false; 75 } 76 77 int DvbManager::tune(JNIEnv *env, jobject thiz, 78 const int frequency, const char *modulationStr, int timeout_ms) { 79 resetExceptFe(); 80 81 struct dvb_frontend_parameters feParams; 82 memset(&feParams, 0, sizeof(struct dvb_frontend_parameters)); 83 feParams.frequency = frequency; 84 if (strcmp(modulationStr, "8VSB") == 0) { 85 feParams.u.vsb.modulation = VSB_8; 86 } else if (strcmp(modulationStr, "QAM256") == 0) { 87 feParams.u.vsb.modulation = QAM_256; 88 } else { 89 ALOGE("Unrecognized modulation mode : %s", modulationStr); 90 return -1; 91 } 92 93 if (mHasPendingTune) { 94 return -1; 95 } 96 if (openDvbFe(env, thiz) != 0) { 97 return -1; 98 } 99 100 feParams.inversion = INVERSION_AUTO; 101 /* Check frontend capability */ 102 struct dvb_frontend_info feInfo; 103 if (ioctl(mFeFd, FE_GET_INFO, &feInfo) != -1) { 104 if (!(feInfo.caps & FE_CAN_INVERSION_AUTO)) { 105 // FE can't do INVERSION_AUTO, trying INVERSION_OFF instead 106 feParams.inversion = INVERSION_OFF; 107 } 108 } 109 110 if (ioctl(mFeFd, FE_SET_FRONTEND, &feParams) != 0) { 111 ALOGD("Can't set Frontend : %s", strerror(errno)); 112 return -1; 113 } 114 115 int lockSuccessCount = 0; 116 double tuneClock = currentTimeMillis(); 117 while (currentTimeMillis() - tuneClock < timeout_ms) { 118 if (mHasPendingTune) { 119 // Return 0 here since we already call FE_SET_FRONTEND, and return due to having pending 120 // tune request. And the frontend setting could be successful. 121 mFeHasLock = true; 122 return 0; 123 } 124 bool lockStatus = isFeLocked(); 125 if (lockStatus) { 126 lockSuccessCount++; 127 } else { 128 lockSuccessCount = 0; 129 } 130 ALOGI("Lock status : %s", lockStatus ? "true" : "false"); 131 if (lockSuccessCount >= FE_CONSECUTIVE_LOCK_SUCCESS_COUNT) { 132 mFeHasLock = true; 133 openDvbDvr(env, thiz); 134 return 0; 135 } 136 } 137 138 return -1; 139 } 140 141 int DvbManager::stopTune() { 142 reset(); 143 usleep(DVB_TUNE_STOP_DELAY_MS); 144 return 0; 145 } 146 147 int DvbManager::openDvbFeFromSystemApi(JNIEnv *env, jobject thiz) { 148 int fd = (int) env->CallIntMethod(thiz, mOpenDvbFrontEndMethodID); 149 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); 150 return fd; 151 } 152 153 int DvbManager::openDvbDemuxFromSystemApi(JNIEnv *env, jobject thiz) { 154 int fd = (int) env->CallIntMethod(thiz, mOpenDvbDemuxMethodID); 155 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); 156 return fd; 157 } 158 159 int DvbManager::openDvbDvrFromSystemApi(JNIEnv *env, jobject thiz) { 160 int fd = (int) env->CallIntMethod(thiz, mOpenDvbDvrMethodID); 161 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); 162 return fd; 163 } 164 165 int DvbManager::openDvbFe(JNIEnv *env, jobject thiz) { 166 if (mFeFd == -1) { 167 if ((mFeFd = openDvbFeFromSystemApi(env, thiz)) < 0) { 168 ALOGD("Can't open FE file : %s", strerror(errno)); 169 return -1; 170 } 171 } 172 173 struct dvb_frontend_info info; 174 if (ioctl(mFeFd, FE_GET_INFO, &info) == 0) { 175 const char *types; 176 switch (info.type) { 177 case FE_QPSK: 178 types = "DVB-S"; 179 break; 180 case FE_QAM: 181 types = "DVB-C"; 182 break; 183 case FE_OFDM: 184 types = "DVB-T"; 185 break; 186 case FE_ATSC: 187 types = "ATSC"; 188 break; 189 default: 190 types = "Unknown"; 191 } 192 ALOGI("Using frontend \"%s\", type %s", info.name, types); 193 } 194 return 0; 195 } 196 197 int DvbManager::startTsPidFilter(JNIEnv *env, jobject thiz, int pid, int filterType) { 198 Mutex::Autolock autoLock(mFilterLock); 199 200 if (mPidFilters.find(pid) != mPidFilters.end() || (mPatFilterFd != -1 && pid == PAT_PID)) { 201 return 0; 202 } 203 204 if (mHasPendingTune) { 205 return -1; 206 } 207 208 int demuxFd; 209 if ((demuxFd = openDvbDemuxFromSystemApi(env, thiz)) < 0) { 210 ALOGD("Can't open DEMUX file : %s", strerror(errno)); 211 return -1; 212 } 213 214 struct dmx_pes_filter_params filter; 215 memset(&filter, 0, sizeof(filter)); 216 filter.pid = pid; 217 filter.input = DMX_IN_FRONTEND; 218 switch (filterType) { 219 case FILTER_TYPE_AUDIO: 220 filter.pes_type = DMX_PES_AUDIO; 221 break; 222 case FILTER_TYPE_VIDEO: 223 filter.pes_type = DMX_PES_VIDEO; 224 break; 225 case FILTER_TYPE_PCR: 226 filter.pes_type = DMX_PES_PCR; 227 break; 228 default: 229 filter.pes_type = DMX_PES_OTHER; 230 break; 231 } 232 filter.output = DMX_OUT_TS_TAP; 233 filter.flags |= (DMX_CHECK_CRC | DMX_IMMEDIATE_START); 234 235 // create a pes filter 236 if (ioctl(demuxFd, DMX_SET_PES_FILTER, &filter)) { 237 close(demuxFd); 238 return -1; 239 } 240 241 if (pid != PAT_PID) { 242 mPidFilters.insert(std::pair<int, int>(pid, demuxFd)); 243 } else { 244 mPatFilterFd = demuxFd; 245 } 246 247 return 0; 248 } 249 250 void DvbManager::closeAllDvbPidFilter() { 251 // Close all dvb pid filters except PAT filter to maintain the opening status of the device. 252 Mutex::Autolock autoLock(mFilterLock); 253 254 for (std::map<int, int>::iterator it(mPidFilters.begin()); 255 it != mPidFilters.end(); it++) { 256 close(it->second); 257 } 258 mPidFilters.clear(); 259 // Close mDvrFd to make sure there is not buffer from previous channel left. 260 closeDvbDvr(); 261 } 262 263 void DvbManager::closePatFilter() { 264 Mutex::Autolock autoLock(mFilterLock); 265 266 if (mPatFilterFd != -1) { 267 close(mPatFilterFd); 268 mPatFilterFd = -1; 269 } 270 } 271 272 int DvbManager::openDvbDvr(JNIEnv *env, jobject thiz) { 273 if ((mDvrFd = openDvbDvrFromSystemApi(env, thiz)) < 0) { 274 ALOGD("Can't open DVR file : %s", strerror(errno)); 275 return -1; 276 } 277 return 0; 278 } 279 280 void DvbManager::closeDvbFe() { 281 if (mFeFd != -1) { 282 close(mFeFd); 283 mFeFd = -1; 284 } 285 } 286 287 void DvbManager::closeDvbDvr() { 288 if (mDvrFd != -1) { 289 close(mDvrFd); 290 mDvrFd = -1; 291 } 292 } 293 294 void DvbManager::reset() { 295 mFeHasLock = false; 296 closeDvbDvr(); 297 closeAllDvbPidFilter(); 298 closePatFilter(); 299 closeDvbFe(); 300 } 301 302 void DvbManager::resetExceptFe() { 303 mFeHasLock = false; 304 closeDvbDvr(); 305 closeAllDvbPidFilter(); 306 closePatFilter(); 307 } 308 309 int DvbManager::readTsStream(JNIEnv *env, jobject thiz, 310 uint8_t *tsBuffer, int tsBufferSize, int timeout_ms) { 311 if (!mFeHasLock) { 312 usleep(DVB_ERROR_RETRY_INTERVAL_MS); 313 return -1; 314 } 315 316 if (mDvrFd == -1) { 317 openDvbDvr(env, thiz); 318 } 319 320 struct pollfd pollFd; 321 pollFd.fd = mDvrFd; 322 pollFd.events = POLLIN|POLLPRI|POLLERR; 323 pollFd.revents = 0; 324 int poll_result = poll(&pollFd, NUM_POLLFDS, timeout_ms); 325 if (poll_result == 0) { 326 return 0; 327 } else if (poll_result == -1 || pollFd.revents & POLLERR) { 328 ALOGD("Can't read DVR : %s", strerror(errno)); 329 // TODO: Find how to recover this situation correctly. 330 closeDvbDvr(); 331 usleep(DVB_ERROR_RETRY_INTERVAL_MS); 332 return -1; 333 } 334 return read(mDvrFd, tsBuffer, tsBufferSize); 335 } 336 337 void DvbManager::setHasPendingTune(bool hasPendingTune) { 338 mHasPendingTune = hasPendingTune; 339 } 340