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     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 
     56 DvbManager::~DvbManager() {
     57     reset();
     58 }
     59 
     60 bool 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 
     69 int 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 
    126 int DvbManager::stopTune() {
    127     reset();
    128     usleep(DVB_TUNE_STOP_DELAY_MS);
    129     return 0;
    130 }
    131 
    132 int 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 
    138 int 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 
    144 int 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 
    150 int 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 
    182 int 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 
    231 void 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 
    242 void DvbManager::closePatFilter() {
    243     Mutex::Autolock autoLock(mFilterLock);
    244 
    245     if (mPatFilterFd != -1) {
    246         close(mPatFilterFd);
    247         mPatFilterFd = -1;
    248     }
    249 }
    250 
    251 int 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 
    259 void DvbManager::closeDvbFe() {
    260     if (mFeFd != -1) {
    261         close(mFeFd);
    262         mFeFd = -1;
    263     }
    264 }
    265 
    266 void DvbManager::closeDvbDvr() {
    267     if (mDvrFd != -1) {
    268         close(mDvrFd);
    269         mDvrFd = -1;
    270     }
    271 }
    272 
    273 void DvbManager::reset() {
    274     mFeHasLock = false;
    275     closeDvbDvr();
    276     closeAllDvbPidFilter();
    277     closePatFilter();
    278     closeDvbFe();
    279 }
    280 
    281 void DvbManager::resetExceptFe() {
    282     mFeHasLock = false;
    283     closeDvbDvr();
    284     closeAllDvbPidFilter();
    285     closePatFilter();
    286 }
    287 
    288 int 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