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