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           mDvbApiVersion(DVB_API_VERSION_UNDEFINED),
     46           mDeliverySystemType(-1),
     47           mFeHasLock(false),
     48           mHasPendingTune(false) {
     49     jclass clazz = env->FindClass(
     50         "com/android/tv/tuner/TunerHal");
     51     mOpenDvbFrontEndMethodID = env->GetMethodID(
     52         clazz, "openDvbFrontEndFd", "()I");
     53     mOpenDvbDemuxMethodID = env->GetMethodID(
     54         clazz, "openDvbDemuxFd", "()I");
     55     mOpenDvbDvrMethodID = env->GetMethodID(
     56         clazz, "openDvbDvrFd", "()I");
     57 }
     58 
     59 DvbManager::~DvbManager() {
     60     reset();
     61 }
     62 
     63 bool DvbManager::isFeLocked() {
     64     if (mDvbApiVersion == DVB_API_VERSION5) {
     65         fe_status_t status;
     66         if (ioctl(mFeFd, FE_READ_STATUS, &status) < 0) {
     67             return false;
     68         }
     69         if (status & FE_HAS_LOCK) {
     70             return true;
     71         }
     72     } else {
     73         struct pollfd pollFd;
     74         pollFd.fd = mFeFd;
     75         pollFd.events = POLLIN;
     76         pollFd.revents = 0;
     77         int poll_result = poll(&pollFd, NUM_POLLFDS, FE_POLL_TIMEOUT_MS);
     78         if (poll_result > 0 && (pollFd.revents & POLLIN)) {
     79             struct dvb_frontend_event kevent;
     80             memset(&kevent, 0, sizeof(kevent));
     81             if (ioctl(mFeFd, FE_GET_EVENT, &kevent) == 0) {
     82                 return (kevent.status & FE_HAS_LOCK);
     83             }
     84         }
     85     }
     86     return false;
     87 }
     88 
     89 int DvbManager::tune(JNIEnv *env, jobject thiz,
     90         const int frequency, const char *modulationStr, int timeout_ms) {
     91     resetExceptFe();
     92 
     93     if (openDvbFe(env, thiz) != 0) {
     94         return -1;
     95     }
     96     if (mDvbApiVersion == DVB_API_VERSION_UNDEFINED) {
     97         struct dtv_property testProps[1] = {
     98             { .cmd = DTV_DELIVERY_SYSTEM }
     99         };
    100         struct dtv_properties feProp = {
    101             .num = 1, .props = testProps
    102         };
    103         // On fugu, DVB_API_VERSION is 5 but it doesn't support FE_SET_PROPERTY. Checking the device
    104         // support FE_GET_PROPERTY or not to determine the DVB API version is greater than 5 or not.
    105         if (ioctl(mFeFd, FE_GET_PROPERTY, &feProp) == -1) {
    106             ALOGD("FE_GET_PROPERTY failed, %s", strerror(errno));
    107             mDvbApiVersion = DVB_API_VERSION3;
    108         } else {
    109             mDvbApiVersion = DVB_API_VERSION5;
    110         }
    111     }
    112 
    113     if (mDvbApiVersion == DVB_API_VERSION5) {
    114         struct dtv_property deliverySystemProperty = {
    115             .cmd = DTV_DELIVERY_SYSTEM, .u.data = SYS_ATSC
    116         };
    117         struct dtv_property frequencyProperty = {
    118             .cmd = DTV_FREQUENCY, .u.data = frequency
    119         };
    120         struct dtv_property modulationProperty = { .cmd = DTV_MODULATION };
    121         if (strncmp(modulationStr, "QAM", 3) == 0) {
    122             modulationProperty.u.data = QAM_AUTO;
    123         } else if (strcmp(modulationStr, "8VSB") == 0) {
    124             modulationProperty.u.data = VSB_8;
    125         } else {
    126             ALOGE("Unrecognized modulation mode : %s", modulationStr);
    127             return -1;
    128         }
    129         struct dtv_property tuneProperty = { .cmd = DTV_TUNE };
    130 
    131         struct dtv_property props[] = {
    132                 deliverySystemProperty, frequencyProperty, modulationProperty, tuneProperty
    133         };
    134         struct dtv_properties dtvProperty = {
    135             .num = 4, .props = props
    136         };
    137 
    138         if (mHasPendingTune) {
    139             return -1;
    140         }
    141         if (ioctl(mFeFd, FE_SET_PROPERTY, &dtvProperty) != 0) {
    142             ALOGD("Can't set Frontend : %s", strerror(errno));
    143             return -1;
    144         }
    145     } else {
    146         struct dvb_frontend_parameters feParams;
    147         memset(&feParams, 0, sizeof(struct dvb_frontend_parameters));
    148         feParams.frequency = frequency;
    149         feParams.inversion = INVERSION_AUTO;
    150         /* Check frontend capability */
    151         struct dvb_frontend_info feInfo;
    152         if (ioctl(mFeFd, FE_GET_INFO, &feInfo) != -1) {
    153             if (!(feInfo.caps & FE_CAN_INVERSION_AUTO)) {
    154                 // FE can't do INVERSION_AUTO, trying INVERSION_OFF instead
    155                 feParams.inversion = INVERSION_OFF;
    156             }
    157         }
    158         switch (feInfo.type) {
    159             case FE_ATSC:
    160                 if (strcmp(modulationStr, "8VSB") == 0) {
    161                     feParams.u.vsb.modulation = VSB_8;
    162                 } else if (strncmp(modulationStr, "QAM", 3) == 0) {
    163                     feParams.u.vsb.modulation = QAM_AUTO;
    164                 } else {
    165                     ALOGE("Unrecognized modulation mode : %s", modulationStr);
    166                     return -1;
    167                 }
    168                 break;
    169             case FE_OFDM:
    170                 if (strcmp(modulationStr, "8VSB") == 0) {
    171                     feParams.u.ofdm.constellation = VSB_8;
    172                 } else if (strcmp(modulationStr, "QAM16") == 0) {
    173                     feParams.u.ofdm.constellation = QAM_16;
    174                 } else if (strcmp(modulationStr, "QAM64") == 0) {
    175                     feParams.u.ofdm.constellation = QAM_64;
    176                 } else if (strcmp(modulationStr, "QAM256") == 0) {
    177                     feParams.u.ofdm.constellation = QAM_256;
    178                 } else if (strcmp(modulationStr, "QPSK") == 0) {
    179                     feParams.u.ofdm.constellation = QPSK;
    180                 } else {
    181                     ALOGE("Unrecognized modulation mode : %s", modulationStr);
    182                     return -1;
    183                 }
    184                 break;
    185             default:
    186                 ALOGE("Unsupported delivery system.");
    187                 return -1;
    188         }
    189 
    190         if (mHasPendingTune) {
    191             return -1;
    192         }
    193 
    194         if (ioctl(mFeFd, FE_SET_FRONTEND, &feParams) != 0) {
    195             ALOGD("Can't set Frontend : %s", strerror(errno));
    196             return -1;
    197         }
    198     }
    199 
    200     int lockSuccessCount = 0;
    201     double tuneClock = currentTimeMillis();
    202     while (currentTimeMillis() - tuneClock < timeout_ms) {
    203         if (mHasPendingTune) {
    204             // Return 0 here since we already call FE_SET_FRONTEND, and return due to having pending
    205             // tune request. And the frontend setting could be successful.
    206             mFeHasLock = true;
    207             return 0;
    208         }
    209         bool lockStatus = isFeLocked();
    210         if (lockStatus) {
    211             lockSuccessCount++;
    212         } else {
    213             lockSuccessCount = 0;
    214         }
    215         ALOGI("Lock status : %s", lockStatus ? "true" : "false");
    216         if (lockSuccessCount >= FE_CONSECUTIVE_LOCK_SUCCESS_COUNT) {
    217             mFeHasLock = true;
    218             openDvbDvr(env, thiz);
    219             return 0;
    220         }
    221     }
    222 
    223     return -1;
    224 }
    225 
    226 int DvbManager::stopTune() {
    227     reset();
    228     usleep(DVB_TUNE_STOP_DELAY_MS);
    229     return 0;
    230 }
    231 
    232 int DvbManager::openDvbFeFromSystemApi(JNIEnv *env, jobject thiz) {
    233     int fd = (int) env->CallIntMethod(thiz, mOpenDvbFrontEndMethodID);
    234     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
    235     return fd;
    236 }
    237 
    238 int DvbManager::openDvbDemuxFromSystemApi(JNIEnv *env, jobject thiz) {
    239     int fd = (int) env->CallIntMethod(thiz, mOpenDvbDemuxMethodID);
    240     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
    241     return fd;
    242 }
    243 
    244 int DvbManager::openDvbDvrFromSystemApi(JNIEnv *env, jobject thiz) {
    245     int fd = (int) env->CallIntMethod(thiz, mOpenDvbDvrMethodID);
    246     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
    247     return fd;
    248 }
    249 
    250 int DvbManager::openDvbFe(JNIEnv *env, jobject thiz) {
    251     if (mFeFd == -1) {
    252         if ((mFeFd = openDvbFeFromSystemApi(env, thiz)) < 0) {
    253             ALOGD("Can't open FE file : %s", strerror(errno));
    254             return -1;
    255         }
    256     }
    257 
    258     struct dvb_frontend_info info;
    259     if (ioctl(mFeFd, FE_GET_INFO, &info) == 0) {
    260         const char *types;
    261         switch (info.type) {
    262             case FE_QPSK:
    263                 types = "DVB-S";
    264                 break;
    265             case FE_QAM:
    266                 types = "DVB-C";
    267                 break;
    268             case FE_OFDM:
    269                 types = "DVB-T";
    270                 break;
    271             case FE_ATSC:
    272                 types = "ATSC";
    273                 break;
    274             default:
    275                 types = "Unknown";
    276         }
    277         ALOGI("Using frontend \"%s\", type %s", info.name, types);
    278     }
    279     return 0;
    280 }
    281 
    282 int DvbManager::startTsPidFilter(JNIEnv *env, jobject thiz, int pid, int filterType) {
    283     Mutex::Autolock autoLock(mFilterLock);
    284 
    285     if (mPidFilters.find(pid) != mPidFilters.end() || (mPatFilterFd != -1 && pid == PAT_PID)) {
    286         return 0;
    287     }
    288 
    289     if (mHasPendingTune) {
    290         return -1;
    291     }
    292 
    293     int demuxFd;
    294     if ((demuxFd = openDvbDemuxFromSystemApi(env, thiz)) < 0) {
    295         ALOGD("Can't open DEMUX file : %s", strerror(errno));
    296         return -1;
    297     }
    298 
    299     struct dmx_pes_filter_params filter;
    300     memset(&filter, 0, sizeof(filter));
    301     filter.pid = pid;
    302     filter.input = DMX_IN_FRONTEND;
    303     switch (filterType) {
    304         case FILTER_TYPE_AUDIO:
    305             filter.pes_type = DMX_PES_AUDIO;
    306             break;
    307         case FILTER_TYPE_VIDEO:
    308             filter.pes_type = DMX_PES_VIDEO;
    309             break;
    310         case FILTER_TYPE_PCR:
    311             filter.pes_type = DMX_PES_PCR;
    312             break;
    313         default:
    314             filter.pes_type = DMX_PES_OTHER;
    315             break;
    316     }
    317     filter.output = DMX_OUT_TS_TAP;
    318     filter.flags |= (DMX_CHECK_CRC | DMX_IMMEDIATE_START);
    319 
    320     // create a pes filter
    321     if (ioctl(demuxFd, DMX_SET_PES_FILTER, &filter)) {
    322         close(demuxFd);
    323         return -1;
    324     }
    325 
    326     if (mDvbApiVersion == DVB_API_VERSION5) {
    327         ioctl(demuxFd, DMX_START, 0);
    328     }
    329 
    330     if (pid != PAT_PID) {
    331         mPidFilters.insert(std::pair<int, int>(pid, demuxFd));
    332     } else {
    333         mPatFilterFd = demuxFd;
    334     }
    335 
    336     return 0;
    337 }
    338 
    339 void DvbManager::closeAllDvbPidFilter() {
    340     // Close all dvb pid filters except PAT filter to maintain the opening status of the device.
    341     Mutex::Autolock autoLock(mFilterLock);
    342 
    343     for (std::map<int, int>::iterator it(mPidFilters.begin());
    344                 it != mPidFilters.end(); it++) {
    345         close(it->second);
    346     }
    347     mPidFilters.clear();
    348     // Close mDvrFd to make sure there is not buffer from previous channel left.
    349     closeDvbDvr();
    350 }
    351 
    352 void DvbManager::closePatFilter() {
    353     Mutex::Autolock autoLock(mFilterLock);
    354 
    355     if (mPatFilterFd != -1) {
    356         close(mPatFilterFd);
    357         mPatFilterFd = -1;
    358     }
    359 }
    360 
    361 int DvbManager::openDvbDvr(JNIEnv *env, jobject thiz) {
    362     if ((mDvrFd = openDvbDvrFromSystemApi(env, thiz)) < 0) {
    363         ALOGD("Can't open DVR file : %s", strerror(errno));
    364         return -1;
    365     }
    366     return 0;
    367 }
    368 
    369 void DvbManager::closeDvbFe() {
    370     if (mFeFd != -1) {
    371         close(mFeFd);
    372         mFeFd = -1;
    373     }
    374 }
    375 
    376 void DvbManager::closeDvbDvr() {
    377     if (mDvrFd != -1) {
    378         close(mDvrFd);
    379         mDvrFd = -1;
    380     }
    381 }
    382 
    383 void DvbManager::reset() {
    384     mFeHasLock = false;
    385     closeDvbDvr();
    386     closeAllDvbPidFilter();
    387     closePatFilter();
    388     closeDvbFe();
    389 }
    390 
    391 void DvbManager::resetExceptFe() {
    392     mFeHasLock = false;
    393     closeDvbDvr();
    394     closeAllDvbPidFilter();
    395     closePatFilter();
    396 }
    397 
    398 int DvbManager::readTsStream(JNIEnv *env, jobject thiz,
    399         uint8_t *tsBuffer, int tsBufferSize, int timeout_ms) {
    400     if (!mFeHasLock) {
    401         usleep(DVB_ERROR_RETRY_INTERVAL_MS);
    402         return -1;
    403     }
    404 
    405     if (mDvrFd == -1) {
    406         openDvbDvr(env, thiz);
    407     }
    408 
    409     struct pollfd pollFd;
    410     pollFd.fd = mDvrFd;
    411     pollFd.events = POLLIN|POLLPRI|POLLERR;
    412     pollFd.revents = 0;
    413     int poll_result = poll(&pollFd, NUM_POLLFDS, timeout_ms);
    414     if (poll_result == 0) {
    415         return 0;
    416     } else if (poll_result == -1 || pollFd.revents & POLLERR) {
    417         ALOGD("Can't read DVR : %s", strerror(errno));
    418         // TODO: Find how to recover this situation correctly.
    419         closeDvbDvr();
    420         usleep(DVB_ERROR_RETRY_INTERVAL_MS);
    421         return -1;
    422     }
    423     return read(mDvrFd, tsBuffer, tsBufferSize);
    424 }
    425 
    426 void DvbManager::setHasPendingTune(bool hasPendingTune) {
    427     mHasPendingTune = hasPendingTune;
    428 }
    429 
    430 int DvbManager::getDeliverySystemType(JNIEnv *env, jobject thiz) {
    431     if (mDeliverySystemType != -1) {
    432         return mDeliverySystemType;
    433     }
    434     if (mFeFd == -1) {
    435         if ((mFeFd = openDvbFeFromSystemApi(env, thiz)) < 0) {
    436             ALOGD("Can't open FE file : %s", strerror(errno));
    437             return DELIVERY_SYSTEM_UNDEFINED;
    438         }
    439     }
    440     struct dtv_property testProps[1] = {
    441         { .cmd = DTV_DELIVERY_SYSTEM }
    442     };
    443     struct dtv_properties feProp = {
    444         .num = 1, .props = testProps
    445     };
    446     mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
    447     if (ioctl(mFeFd, FE_GET_PROPERTY, &feProp) == -1) {
    448         mDvbApiVersion = DVB_API_VERSION3;
    449         if (openDvbFe(env, thiz) == 0) {
    450             struct dvb_frontend_info info;
    451             if (ioctl(mFeFd, FE_GET_INFO, &info) == 0) {
    452                 switch (info.type) {
    453                     case FE_QPSK:
    454                         mDeliverySystemType = DELIVERY_SYSTEM_DVBS;
    455                         break;
    456                     case FE_QAM:
    457                         mDeliverySystemType = DELIVERY_SYSTEM_DVBC;
    458                         break;
    459                     case FE_OFDM:
    460                         mDeliverySystemType = DELIVERY_SYSTEM_DVBT;
    461                         break;
    462                     case FE_ATSC:
    463                         mDeliverySystemType = DELIVERY_SYSTEM_ATSC;
    464                         break;
    465                     default:
    466                         mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
    467                         break;
    468                 }
    469             }
    470         }
    471     } else {
    472         mDvbApiVersion = DVB_API_VERSION5;
    473         switch (feProp.props[0].u.data) {
    474             case SYS_DVBT:
    475                 mDeliverySystemType = DELIVERY_SYSTEM_DVBT;
    476                 break;
    477             case SYS_DVBT2:
    478                 mDeliverySystemType = DELIVERY_SYSTEM_DVBT2;
    479                 break;
    480             case SYS_DVBS:
    481                 mDeliverySystemType = DELIVERY_SYSTEM_DVBS;
    482                 break;
    483             case SYS_DVBS2:
    484                 mDeliverySystemType = DELIVERY_SYSTEM_DVBS2;
    485                 break;
    486             case SYS_DVBC_ANNEX_A:
    487             case SYS_DVBC_ANNEX_B:
    488             case SYS_DVBC_ANNEX_C:
    489                 mDeliverySystemType = DELIVERY_SYSTEM_DVBC;
    490                 break;
    491             case SYS_ATSC:
    492                 mDeliverySystemType = DELIVERY_SYSTEM_ATSC;
    493                 break;
    494             default:
    495                 mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
    496                 break;
    497         }
    498     }
    499     return mDeliverySystemType;
    500 }