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