• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
currentTimeMillis()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 
DvbManager(JNIEnv * env,jobject)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 
~DvbManager()57 DvbManager::~DvbManager() {
58     reset();
59 }
60 
isFeLocked()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 
tune(JNIEnv * env,jobject thiz,const int frequency,const char * modulationStr,int timeout_ms)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 
stopTune()141 int DvbManager::stopTune() {
142     reset();
143     usleep(DVB_TUNE_STOP_DELAY_MS);
144     return 0;
145 }
146 
openDvbFeFromSystemApi(JNIEnv * env,jobject thiz)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 
openDvbDemuxFromSystemApi(JNIEnv * env,jobject thiz)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 
openDvbDvrFromSystemApi(JNIEnv * env,jobject thiz)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 
openDvbFe(JNIEnv * env,jobject thiz)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 
startTsPidFilter(JNIEnv * env,jobject thiz,int pid,int filterType)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 
closeAllDvbPidFilter()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 
closePatFilter()263 void DvbManager::closePatFilter() {
264     Mutex::Autolock autoLock(mFilterLock);
265 
266     if (mPatFilterFd != -1) {
267         close(mPatFilterFd);
268         mPatFilterFd = -1;
269     }
270 }
271 
openDvbDvr(JNIEnv * env,jobject thiz)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 
closeDvbFe()280 void DvbManager::closeDvbFe() {
281     if (mFeFd != -1) {
282         close(mFeFd);
283         mFeFd = -1;
284     }
285 }
286 
closeDvbDvr()287 void DvbManager::closeDvbDvr() {
288     if (mDvrFd != -1) {
289         close(mDvrFd);
290         mDvrFd = -1;
291     }
292 }
293 
reset()294 void DvbManager::reset() {
295     mFeHasLock = false;
296     closeDvbDvr();
297     closeAllDvbPidFilter();
298     closePatFilter();
299     closeDvbFe();
300 }
301 
resetExceptFe()302 void DvbManager::resetExceptFe() {
303     mFeHasLock = false;
304     closeDvbDvr();
305     closeAllDvbPidFilter();
306     closePatFilter();
307 }
308 
readTsStream(JNIEnv * env,jobject thiz,uint8_t * tsBuffer,int tsBufferSize,int timeout_ms)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 
setHasPendingTune(bool hasPendingTune)337 void DvbManager::setHasPendingTune(bool hasPendingTune) {
338     mHasPendingTune = hasPendingTune;
339 }
340