Home | History | Annotate | Download | only in jni
      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