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          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
59DvbManager::~DvbManager() {
60    reset();
61}
62
63bool 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
89int 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
226int DvbManager::stopTune() {
227    reset();
228    usleep(DVB_TUNE_STOP_DELAY_MS);
229    return 0;
230}
231
232int 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
238int 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
244int 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
250int 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
282int 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
339void 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
352void DvbManager::closePatFilter() {
353    Mutex::Autolock autoLock(mFilterLock);
354
355    if (mPatFilterFd != -1) {
356        close(mPatFilterFd);
357        mPatFilterFd = -1;
358    }
359}
360
361int 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
369void DvbManager::closeDvbFe() {
370    if (mFeFd != -1) {
371        close(mFeFd);
372        mFeFd = -1;
373    }
374}
375
376void DvbManager::closeDvbDvr() {
377    if (mDvrFd != -1) {
378        close(mDvrFd);
379        mDvrFd = -1;
380    }
381}
382
383void DvbManager::reset() {
384    mFeHasLock = false;
385    closeDvbDvr();
386    closeAllDvbPidFilter();
387    closePatFilter();
388    closeDvbFe();
389}
390
391void DvbManager::resetExceptFe() {
392    mFeHasLock = false;
393    closeDvbDvr();
394    closeAllDvbPidFilter();
395    closePatFilter();
396}
397
398int 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
426void DvbManager::setHasPendingTune(bool hasPendingTune) {
427    mHasPendingTune = hasPendingTune;
428}
429
430int 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}