• 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     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 
~DvbManager()56 DvbManager::~DvbManager() {
57     reset();
58 }
59 
isFeLocked()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 
tune(JNIEnv * env,jobject thiz,const int frequency,const char * modulationStr,int timeout_ms)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 
stopTune()126 int DvbManager::stopTune() {
127     reset();
128     usleep(DVB_TUNE_STOP_DELAY_MS);
129     return 0;
130 }
131 
openDvbFeFromSystemApi(JNIEnv * env,jobject thiz)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 
openDvbDemuxFromSystemApi(JNIEnv * env,jobject thiz)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 
openDvbDvrFromSystemApi(JNIEnv * env,jobject thiz)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 
openDvbFe(JNIEnv * env,jobject thiz)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 
startTsPidFilter(JNIEnv * env,jobject thiz,int pid,int filterType)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 
closeAllDvbPidFilter()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 
closePatFilter()242 void DvbManager::closePatFilter() {
243     Mutex::Autolock autoLock(mFilterLock);
244 
245     if (mPatFilterFd != -1) {
246         close(mPatFilterFd);
247         mPatFilterFd = -1;
248     }
249 }
250 
openDvbDvr(JNIEnv * env,jobject thiz)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 
closeDvbFe()259 void DvbManager::closeDvbFe() {
260     if (mFeFd != -1) {
261         close(mFeFd);
262         mFeFd = -1;
263     }
264 }
265 
closeDvbDvr()266 void DvbManager::closeDvbDvr() {
267     if (mDvrFd != -1) {
268         close(mDvrFd);
269         mDvrFd = -1;
270     }
271 }
272 
reset()273 void DvbManager::reset() {
274     mFeHasLock = false;
275     closeDvbDvr();
276     closeAllDvbPidFilter();
277     closePatFilter();
278     closeDvbFe();
279 }
280 
resetExceptFe()281 void DvbManager::resetExceptFe() {
282     mFeHasLock = false;
283     closeDvbDvr();
284     closeAllDvbPidFilter();
285     closePatFilter();
286 }
287 
readTsStream(JNIEnv * env,jobject thiz,uint8_t * tsBuffer,int tsBufferSize,int timeout_ms)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