• 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           mDvrFd(-1),
43           mPatFilterFd(-1),
44           mDvbApiVersion(DVB_API_VERSION_UNDEFINED),
45           mDeliverySystemType(-1),
46           mFeHasLock(false),
47           mHasPendingTune(false) {
48   jclass clazz = env->FindClass("com/android/tv/tuner/TunerHal");
49   mOpenDvbFrontEndMethodID =
50       env->GetMethodID(clazz, "openDvbFrontEndFd", "()I");
51   mOpenDvbDemuxMethodID = env->GetMethodID(clazz, "openDvbDemuxFd", "()I");
52   mOpenDvbDvrMethodID = env->GetMethodID(clazz, "openDvbDvrFd", "()I");
53 }
54 
~DvbManager()55 DvbManager::~DvbManager() {
56     reset();
57 }
58 
isFeLocked()59 bool DvbManager::isFeLocked() {
60     if (mDvbApiVersion == DVB_API_VERSION5) {
61         fe_status_t status;
62         if (ioctl(mFeFd, FE_READ_STATUS, &status) < 0) {
63             return false;
64         }
65         if (status & FE_HAS_LOCK) {
66             return true;
67         }
68     } else {
69         struct pollfd pollFd;
70         pollFd.fd = mFeFd;
71         pollFd.events = POLLIN;
72         pollFd.revents = 0;
73         int poll_result = poll(&pollFd, NUM_POLLFDS, FE_POLL_TIMEOUT_MS);
74         if (poll_result > 0 && (pollFd.revents & POLLIN)) {
75             struct dvb_frontend_event kevent;
76             memset(&kevent, 0, sizeof(kevent));
77             if (ioctl(mFeFd, FE_GET_EVENT, &kevent) == 0) {
78                 return (kevent.status & FE_HAS_LOCK);
79             }
80         }
81     }
82     return false;
83 }
84 
tune(JNIEnv * env,jobject thiz,const int frequency,const char * modulationStr,int timeout_ms)85 int DvbManager::tune(JNIEnv *env, jobject thiz,
86         const int frequency, const char *modulationStr, int timeout_ms) {
87     resetExceptFe();
88 
89     if (openDvbFe(env, thiz) != 0) {
90         return -1;
91     }
92 
93     if (frequency < 0) {
94         return -1;
95     }
96 
97     if (mDvbApiVersion == DVB_API_VERSION_UNDEFINED) {
98         struct dtv_property testProps[1] = {
99             { .cmd = DTV_DELIVERY_SYSTEM }
100         };
101         struct dtv_properties feProp = {
102             .num = 1, .props = testProps
103         };
104         // On fugu, DVB_API_VERSION is 5 but it doesn't support FE_SET_PROPERTY. Checking the device
105         // support FE_GET_PROPERTY or not to determine the DVB API version is greater than 5 or not.
106         if (ioctl(mFeFd, FE_GET_PROPERTY, &feProp) == -1) {
107             ALOGD("FE_GET_PROPERTY failed, %s", strerror(errno));
108             mDvbApiVersion = DVB_API_VERSION3;
109         } else {
110             mDvbApiVersion = DVB_API_VERSION5;
111         }
112     }
113 
114     if (mDvbApiVersion == DVB_API_VERSION5) {
115         struct dtv_property deliverySystemProperty = {
116             .cmd = DTV_DELIVERY_SYSTEM
117         };
118         deliverySystemProperty.u.data = SYS_ATSC;
119         struct dtv_property frequencyProperty = {
120             .cmd = DTV_FREQUENCY
121         };
122         frequencyProperty.u.data = static_cast<__u32>(frequency);
123         struct dtv_property modulationProperty = { .cmd = DTV_MODULATION };
124         if (strncmp(modulationStr, "QAM", 3) == 0) {
125             modulationProperty.u.data = QAM_AUTO;
126         } else if (strcmp(modulationStr, "8VSB") == 0) {
127             modulationProperty.u.data = VSB_8;
128         } else {
129             ALOGE("Unrecognized modulation mode : %s", modulationStr);
130             return -1;
131         }
132         struct dtv_property tuneProperty = { .cmd = DTV_TUNE };
133 
134         struct dtv_property props[] = {
135                 deliverySystemProperty, frequencyProperty, modulationProperty, tuneProperty
136         };
137         struct dtv_properties dtvProperty = {
138             .num = 4, .props = props
139         };
140 
141         if (mHasPendingTune) {
142             return -1;
143         }
144         if (ioctl(mFeFd, FE_SET_PROPERTY, &dtvProperty) != 0) {
145             ALOGD("Can't set Frontend : %s", strerror(errno));
146             return -1;
147         }
148     } else {
149         struct dvb_frontend_parameters feParams;
150         memset(&feParams, 0, sizeof(struct dvb_frontend_parameters));
151         feParams.frequency = frequency;
152         feParams.inversion = INVERSION_AUTO;
153         /* Check frontend capability */
154         struct dvb_frontend_info feInfo;
155         if (ioctl(mFeFd, FE_GET_INFO, &feInfo) != -1) {
156             if (!(feInfo.caps & FE_CAN_INVERSION_AUTO)) {
157                 // FE can't do INVERSION_AUTO, trying INVERSION_OFF instead
158                 feParams.inversion = INVERSION_OFF;
159             }
160         }
161         switch (feInfo.type) {
162             case FE_ATSC:
163                 if (strcmp(modulationStr, "8VSB") == 0) {
164                     feParams.u.vsb.modulation = VSB_8;
165                 } else if (strncmp(modulationStr, "QAM", 3) == 0) {
166                     feParams.u.vsb.modulation = QAM_AUTO;
167                 } else {
168                     ALOGE("Unrecognized modulation mode : %s", modulationStr);
169                     return -1;
170                 }
171                 break;
172             case FE_OFDM:
173                 if (strcmp(modulationStr, "8VSB") == 0) {
174                     feParams.u.ofdm.constellation = VSB_8;
175                 } else if (strcmp(modulationStr, "QAM16") == 0) {
176                     feParams.u.ofdm.constellation = QAM_16;
177                 } else if (strcmp(modulationStr, "QAM64") == 0) {
178                     feParams.u.ofdm.constellation = QAM_64;
179                 } else if (strcmp(modulationStr, "QAM256") == 0) {
180                     feParams.u.ofdm.constellation = QAM_256;
181                 } else if (strcmp(modulationStr, "QPSK") == 0) {
182                     feParams.u.ofdm.constellation = QPSK;
183                 } else {
184                     ALOGE("Unrecognized modulation mode : %s", modulationStr);
185                     return -1;
186                 }
187                 break;
188             default:
189                 ALOGE("Unsupported delivery system.");
190                 return -1;
191         }
192 
193         if (mHasPendingTune) {
194             return -1;
195         }
196 
197         if (ioctl(mFeFd, FE_SET_FRONTEND, &feParams) != 0) {
198             ALOGD("Can't set Frontend : %s", strerror(errno));
199             return -1;
200         }
201     }
202 
203     int lockSuccessCount = 0;
204     double tuneClock = currentTimeMillis();
205     while (currentTimeMillis() - tuneClock < timeout_ms) {
206         if (mHasPendingTune) {
207             // Return 0 here since we already call FE_SET_FRONTEND, and return due to having pending
208             // tune request. And the frontend setting could be successful.
209             mFeHasLock = true;
210             return 0;
211         }
212         bool lockStatus = isFeLocked();
213         if (lockStatus) {
214             lockSuccessCount++;
215         } else {
216             lockSuccessCount = 0;
217         }
218         ALOGI("Lock status : %s", lockStatus ? "true" : "false");
219         if (lockSuccessCount >= FE_CONSECUTIVE_LOCK_SUCCESS_COUNT) {
220             mFeHasLock = true;
221             openDvbDvr(env, thiz);
222             return 0;
223         }
224     }
225 
226     return -1;
227 }
228 
stopTune()229 int DvbManager::stopTune() {
230     reset();
231     usleep(DVB_TUNE_STOP_DELAY_MS);
232     return 0;
233 }
234 
openDvbFeFromSystemApi(JNIEnv * env,jobject thiz)235 int DvbManager::openDvbFeFromSystemApi(JNIEnv *env, jobject thiz) {
236     int fd = (int) env->CallIntMethod(thiz, mOpenDvbFrontEndMethodID);
237     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
238     return fd;
239 }
240 
openDvbDemuxFromSystemApi(JNIEnv * env,jobject thiz)241 int DvbManager::openDvbDemuxFromSystemApi(JNIEnv *env, jobject thiz) {
242     int fd = (int) env->CallIntMethod(thiz, mOpenDvbDemuxMethodID);
243     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
244     return fd;
245 }
246 
openDvbDvrFromSystemApi(JNIEnv * env,jobject thiz)247 int DvbManager::openDvbDvrFromSystemApi(JNIEnv *env, jobject thiz) {
248     int fd = (int) env->CallIntMethod(thiz, mOpenDvbDvrMethodID);
249     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
250     return fd;
251 }
252 
openDvbFe(JNIEnv * env,jobject thiz)253 int DvbManager::openDvbFe(JNIEnv *env, jobject thiz) {
254     if (mFeFd == -1) {
255         if ((mFeFd = openDvbFeFromSystemApi(env, thiz)) < 0) {
256             ALOGD("Can't open FE file : %s", strerror(errno));
257             return -1;
258         }
259     }
260 
261     struct dvb_frontend_info info;
262     if (ioctl(mFeFd, FE_GET_INFO, &info) == 0) {
263         const char *types;
264         switch (info.type) {
265             case FE_QPSK:
266                 types = "DVB-S";
267                 break;
268             case FE_QAM:
269                 types = "DVB-C";
270                 break;
271             case FE_OFDM:
272                 types = "DVB-T";
273                 break;
274             case FE_ATSC:
275                 types = "ATSC";
276                 break;
277             default:
278                 types = "Unknown";
279         }
280         ALOGI("Using frontend \"%s\", type %s", info.name, types);
281     }
282     return 0;
283 }
284 
startTsPidFilter(JNIEnv * env,jobject thiz,int pid,int filterType)285 int DvbManager::startTsPidFilter(JNIEnv *env, jobject thiz, int pid, int filterType) {
286     Mutex::Autolock autoLock(mFilterLock);
287 
288     if (mPidFilters.find(pid) != mPidFilters.end() || (mPatFilterFd != -1 && pid == PAT_PID)) {
289         return 0;
290     }
291 
292     if (mHasPendingTune) {
293         return -1;
294     }
295 
296     int demuxFd;
297     if ((demuxFd = openDvbDemuxFromSystemApi(env, thiz)) < 0) {
298         ALOGD("Can't open DEMUX file : %s", strerror(errno));
299         return -1;
300     }
301 
302     struct dmx_pes_filter_params filter;
303     memset(&filter, 0, sizeof(filter));
304     filter.pid = pid;
305     filter.input = DMX_IN_FRONTEND;
306     switch (filterType) {
307         case FILTER_TYPE_AUDIO:
308             filter.pes_type = DMX_PES_AUDIO;
309             break;
310         case FILTER_TYPE_VIDEO:
311             filter.pes_type = DMX_PES_VIDEO;
312             break;
313         case FILTER_TYPE_PCR:
314             filter.pes_type = DMX_PES_PCR;
315             break;
316         default:
317             filter.pes_type = DMX_PES_OTHER;
318             break;
319     }
320     filter.output = DMX_OUT_TS_TAP;
321     filter.flags |= (DMX_CHECK_CRC | DMX_IMMEDIATE_START);
322 
323     // create a pes filter
324     if (ioctl(demuxFd, DMX_SET_PES_FILTER, &filter)) {
325         close(demuxFd);
326         return -1;
327     }
328 
329     if (mDvbApiVersion == DVB_API_VERSION5) {
330         ioctl(demuxFd, DMX_START, 0);
331     }
332 
333     if (pid != PAT_PID) {
334         mPidFilters.insert(std::pair<int, int>(pid, demuxFd));
335     } else {
336         mPatFilterFd = demuxFd;
337     }
338 
339     return 0;
340 }
341 
closeAllDvbPidFilter()342 void DvbManager::closeAllDvbPidFilter() {
343     // Close all dvb pid filters except PAT filter to maintain the opening status of the device.
344     Mutex::Autolock autoLock(mFilterLock);
345 
346     for (std::map<int, int>::iterator it(mPidFilters.begin());
347                 it != mPidFilters.end(); it++) {
348         close(it->second);
349     }
350     mPidFilters.clear();
351     // Close mDvrFd to make sure there is not buffer from previous channel left.
352     closeDvbDvr();
353 }
354 
closePatFilter()355 void DvbManager::closePatFilter() {
356     Mutex::Autolock autoLock(mFilterLock);
357 
358     if (mPatFilterFd != -1) {
359         close(mPatFilterFd);
360         mPatFilterFd = -1;
361     }
362 }
363 
openDvbDvr(JNIEnv * env,jobject thiz)364 int DvbManager::openDvbDvr(JNIEnv *env, jobject thiz) {
365     if ((mDvrFd = openDvbDvrFromSystemApi(env, thiz)) < 0) {
366         ALOGD("Can't open DVR file : %s", strerror(errno));
367         return -1;
368     }
369     return 0;
370 }
371 
closeDvbFe()372 void DvbManager::closeDvbFe() {
373     if (mFeFd != -1) {
374         close(mFeFd);
375         mFeFd = -1;
376     }
377 }
378 
closeDvbDvr()379 void DvbManager::closeDvbDvr() {
380     if (mDvrFd != -1) {
381         close(mDvrFd);
382         mDvrFd = -1;
383     }
384 }
385 
reset()386 void DvbManager::reset() {
387     mFeHasLock = false;
388     closeDvbDvr();
389     closeAllDvbPidFilter();
390     closePatFilter();
391     closeDvbFe();
392 }
393 
resetExceptFe()394 void DvbManager::resetExceptFe() {
395     mFeHasLock = false;
396     closeDvbDvr();
397     closeAllDvbPidFilter();
398     closePatFilter();
399 }
400 
readTsStream(JNIEnv * env,jobject thiz,uint8_t * tsBuffer,int tsBufferSize,int timeout_ms)401 int DvbManager::readTsStream(JNIEnv *env, jobject thiz,
402         uint8_t *tsBuffer, int tsBufferSize, int timeout_ms) {
403     if (!mFeHasLock) {
404         usleep(DVB_ERROR_RETRY_INTERVAL_MS);
405         return -1;
406     }
407 
408     if (mDvrFd == -1) {
409         openDvbDvr(env, thiz);
410     }
411 
412     struct pollfd pollFd;
413     pollFd.fd = mDvrFd;
414     pollFd.events = POLLIN|POLLPRI|POLLERR;
415     pollFd.revents = 0;
416     int poll_result = poll(&pollFd, NUM_POLLFDS, timeout_ms);
417     if (poll_result == 0) {
418         return 0;
419     } else if (poll_result == -1 || pollFd.revents & POLLERR) {
420         ALOGD("Can't read DVR : %s", strerror(errno));
421         // TODO: Find how to recover this situation correctly.
422         closeDvbDvr();
423         usleep(DVB_ERROR_RETRY_INTERVAL_MS);
424         return -1;
425     }
426     return read(mDvrFd, tsBuffer, tsBufferSize);
427 }
428 
setHasPendingTune(bool hasPendingTune)429 void DvbManager::setHasPendingTune(bool hasPendingTune) {
430     mHasPendingTune = hasPendingTune;
431 }
432 
getDeliverySystemType(JNIEnv * env,jobject thiz)433 int DvbManager::getDeliverySystemType(JNIEnv *env, jobject thiz) {
434     if (mDeliverySystemType != -1) {
435         return mDeliverySystemType;
436     }
437     if (mFeFd == -1) {
438         if ((mFeFd = openDvbFeFromSystemApi(env, thiz)) < 0) {
439             ALOGD("Can't open FE file : %s", strerror(errno));
440             return DELIVERY_SYSTEM_UNDEFINED;
441         }
442     }
443     struct dtv_property testProps[1] = {
444         { .cmd = DTV_DELIVERY_SYSTEM }
445     };
446     struct dtv_properties feProp = {
447         .num = 1, .props = testProps
448     };
449     mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
450     if (ioctl(mFeFd, FE_GET_PROPERTY, &feProp) == -1) {
451         mDvbApiVersion = DVB_API_VERSION3;
452         if (openDvbFe(env, thiz) == 0) {
453             struct dvb_frontend_info info;
454             if (ioctl(mFeFd, FE_GET_INFO, &info) == 0) {
455                 switch (info.type) {
456                     case FE_QPSK:
457                         mDeliverySystemType = DELIVERY_SYSTEM_DVBS;
458                         break;
459                     case FE_QAM:
460                         mDeliverySystemType = DELIVERY_SYSTEM_DVBC;
461                         break;
462                     case FE_OFDM:
463                         mDeliverySystemType = DELIVERY_SYSTEM_DVBT;
464                         break;
465                     case FE_ATSC:
466                         mDeliverySystemType = DELIVERY_SYSTEM_ATSC;
467                         break;
468                     default:
469                         mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
470                         break;
471                 }
472             }
473         }
474     } else {
475         mDvbApiVersion = DVB_API_VERSION5;
476         switch (feProp.props[0].u.data) {
477             case SYS_DVBT:
478                 mDeliverySystemType = DELIVERY_SYSTEM_DVBT;
479                 break;
480             case SYS_DVBT2:
481                 mDeliverySystemType = DELIVERY_SYSTEM_DVBT2;
482                 break;
483             case SYS_DVBS:
484                 mDeliverySystemType = DELIVERY_SYSTEM_DVBS;
485                 break;
486             case SYS_DVBS2:
487                 mDeliverySystemType = DELIVERY_SYSTEM_DVBS2;
488                 break;
489             case SYS_DVBC_ANNEX_A:
490             case SYS_DVBC_ANNEX_B:
491             case SYS_DVBC_ANNEX_C:
492                 mDeliverySystemType = DELIVERY_SYSTEM_DVBC;
493                 break;
494             case SYS_ATSC:
495                 mDeliverySystemType = DELIVERY_SYSTEM_ATSC;
496                 break;
497             default:
498                 mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED;
499                 break;
500         }
501     }
502     return mDeliverySystemType;
503 }