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