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