14 #include <linux/dvb/dmx.h>
15 #include <linux/dvb/frontend.h>
16 #include <sys/ioctl.h>
24 #define FE_CAN_TURBO_FEC 0x8000000 // TODO: remove this once it is defined in the driver
26 #define DVBS_TUNE_TIMEOUT 9000 //ms
27 #define DVBS_LOCK_TIMEOUT 2000 //ms
28 #define DVBC_TUNE_TIMEOUT 9000 //ms
29 #define DVBC_LOCK_TIMEOUT 2000 //ms
30 #define DVBT_TUNE_TIMEOUT 9000 //ms
31 #define DVBT_LOCK_TIMEOUT 2000 //ms
32 #define ATSC_TUNE_TIMEOUT 9000 //ms
33 #define ATSC_LOCK_TIMEOUT 2000 //ms
35 #define SCR_RANDOM_TIMEOUT 500 // ms (add random value up to this when tuning SCR device to avoid lockups)
40 { 0, INVERSION_OFF,
trNOOP(
"off") },
41 { 1, INVERSION_ON,
trNOOP(
"on") },
42 { 999, INVERSION_AUTO,
trNOOP(
"auto") },
47 { 5, 5000000,
"5 MHz" },
48 { 6, 6000000,
"6 MHz" },
49 { 7, 7000000,
"7 MHz" },
50 { 8, 8000000,
"8 MHz" },
51 { 10, 10000000,
"10 MHz" },
52 { 1712, 1712000,
"1.712 MHz" },
57 { 0, FEC_NONE,
trNOOP(
"none") },
58 { 12, FEC_1_2,
"1/2" },
59 { 23, FEC_2_3,
"2/3" },
60 { 34, FEC_3_4,
"3/4" },
61 { 35, FEC_3_5,
"3/5" },
62 { 45, FEC_4_5,
"4/5" },
63 { 56, FEC_5_6,
"5/6" },
64 { 67, FEC_6_7,
"6/7" },
65 { 78, FEC_7_8,
"7/8" },
66 { 89, FEC_8_9,
"8/9" },
67 { 910, FEC_9_10,
"9/10" },
68 { 999, FEC_AUTO,
trNOOP(
"auto") },
73 { 16, QAM_16,
"QAM16" },
74 { 32, QAM_32,
"QAM32" },
75 { 64, QAM_64,
"QAM64" },
76 { 128, QAM_128,
"QAM128" },
77 { 256, QAM_256,
"QAM256" },
80 { 6, APSK_16,
"16APSK" },
81 { 7, APSK_32,
"32APSK" },
82 { 10, VSB_8,
"VSB8" },
83 { 11, VSB_16,
"VSB16" },
84 { 12, DQPSK,
"DQPSK" },
85 { 999, QAM_AUTO,
trNOOP(
"auto") },
89 #define DVB_SYSTEM_1 0 // see also nit.c
90 #define DVB_SYSTEM_2 1
105 { 1, TRANSMISSION_MODE_1K,
"1K" },
106 { 2, TRANSMISSION_MODE_2K,
"2K" },
107 { 4, TRANSMISSION_MODE_4K,
"4K" },
108 { 8, TRANSMISSION_MODE_8K,
"8K" },
109 { 16, TRANSMISSION_MODE_16K,
"16K" },
110 { 32, TRANSMISSION_MODE_32K,
"32K" },
111 { 999, TRANSMISSION_MODE_AUTO,
trNOOP(
"auto") },
116 { 4, GUARD_INTERVAL_1_4,
"1/4" },
117 { 8, GUARD_INTERVAL_1_8,
"1/8" },
118 { 16, GUARD_INTERVAL_1_16,
"1/16" },
119 { 32, GUARD_INTERVAL_1_32,
"1/32" },
120 { 128, GUARD_INTERVAL_1_128,
"1/128" },
121 { 19128, GUARD_INTERVAL_19_128,
"19/128" },
122 { 19256, GUARD_INTERVAL_19_256,
"19/256" },
123 { 999, GUARD_INTERVAL_AUTO,
trNOOP(
"auto") },
128 { 0, HIERARCHY_NONE,
trNOOP(
"none") },
129 { 1, HIERARCHY_1,
"1" },
130 { 2, HIERARCHY_2,
"2" },
131 { 4, HIERARCHY_4,
"4" },
132 { 999, HIERARCHY_AUTO,
trNOOP(
"auto") },
137 { 0, ROLLOFF_AUTO,
trNOOP(
"auto") },
138 { 20, ROLLOFF_20,
"0.20" },
139 { 25, ROLLOFF_25,
"0.25" },
140 { 35, ROLLOFF_35,
"0.35" },
171 *String =
tr(Map[n].userString);
205 guard = GUARD_INTERVAL_AUTO;
214 return Value >= 0 && Value != 999 ? sprintf(p,
"%c%d", Name, Value) : 0;
219 #define ST(s) if (strchr(s, Type) && (strchr(s, '0' + system + 1) || strchr(s, '*')))
243 int n = strtol(s, &p, 10);
244 if (!errno && p != s) {
250 esyslog(
"ERROR: invalid value for parameter '%c'", *(s - 1));
257 switch (toupper(*s)) {
273 default:
esyslog(
"ERROR: unknown parameter key '%c'", *s);
282 #define TUNER_POLL_TIMEOUT 10 // ms
313 virtual void Action(
void);
326 bool Locked(
int TimeoutMs = 0);
390 while (t->bondedTuner !=
this)
393 t->bondedTuner = NULL;
408 return diseqc->Commands();
412 bool VoltOff = dtp.Polarization() ==
'V' || dtp.Polarization() ==
'R';
424 if (t->device->Priority() >
IDLEPRIORITY || ConsiderOccupied && t->device->Occupied()) {
425 if (strcmp(BondingParams, t->GetBondingParams()) != 0)
471 if (BondedMaster ==
this) {
500 if (isLocked || !TimeoutMs)
513 dvb_frontend_event Event;
514 while (ioctl(
fd_frontend, FE_GET_EVENT, &Event) == 0)
523 if (ioctl(
fd_frontend, FE_READ_STATUS, &Status) != -1)
539 if (ioctl(
fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
544 uint16_t MaxSignal = 0xFFFF;
548 case 0x13C21019: MaxSignal = 670;
break;
550 int s = int(Signal) * 100 / MaxSignal;
553 #ifdef DEBUG_SIGNALSTRENGTH
559 #define LOCK_THRESHOLD 5 // indicates that all 5 FE_HAS_* flags are set
566 if ((Status & FE_HAS_LOCK) == 0) {
567 if ((Status & FE_HAS_SIGNAL) == 0)
569 if ((Status & FE_HAS_CARRIER) == 0)
571 if ((Status & FE_HAS_VITERBI) == 0)
573 if ((Status & FE_HAS_SYNC) == 0)
582 if (errno == EOPNOTSUPP) {
595 if (errno == EOPNOTSUPP) {
606 if (ioctl(
fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
608 if (errno == EOPNOTSUPP) {
616 uint16_t MaxSnr = 0xFFFF;
620 case 0x13C21019: MaxSnr = 200;
break;
622 int a = int(Snr) * 100 / MaxSnr;
623 int b = 100 - (Unc * 10 + (Ber / 256) * 5);
629 #ifdef DEBUG_SIGNALQUALITY
630 fprintf(stderr,
"FE %d/%d: %08X Q = %04X %04X %d %5d %5d %3d%%\n",
adapter,
frontend,
subsystemId, MaxSnr, Snr, HasSnr, HasBer ?
int(Ber) : -1, HasUnc ?
int(Unc) : -1, q);
639 while (f && f < 1000000)
653 struct dvb_diseqc_master_cmd cmd;
654 const char *CurrentAction = NULL;
656 cmd.msg_len =
sizeof(cmd.msg);
668 default:
esyslog(
"ERROR: unknown diseqc command %d", da);
685 int ds = SYS_UNDEFINED;
689 ds = SYS_DVBC_ANNEX_AC;
690 else if (Channel->
IsSat())
692 else if (Channel->
IsTerr())
695 esyslog(
"ERROR: can't determine frontend type for channel %d", Channel->
Number());
701 #define MAXFRONTENDCMDS 16
702 #define SETCMD(c, d) { Frontend[CmdSeq.num].cmd = (c);\
703 Frontend[CmdSeq.num].u.data = (d);\
704 if (CmdSeq.num++ > MAXFRONTENDCMDS) {\
705 esyslog("ERROR: too many tuning commands on frontend %d/%d", adapter, frontend);\
710 memset(&Frontend, 0,
sizeof(Frontend));
711 dtv_properties CmdSeq;
712 memset(&CmdSeq, 0,
sizeof(CmdSeq));
713 CmdSeq.props = Frontend;
715 if (ioctl(
fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
725 if (frontendType == SYS_UNDEFINED)
728 SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
729 if (frontendType == SYS_DVBS || frontendType == SYS_DVBS2) {
733 frequency -= diseqc->Lof();
751 int tone = SEC_TONE_OFF;
760 int volt = (dtp.Polarization() ==
'V' || dtp.Polarization() ==
'R') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
763 volt = SEC_VOLTAGE_13;
768 frequency = abs(frequency);
771 SETCMD(DTV_FREQUENCY, frequency * 1000UL);
772 SETCMD(DTV_MODULATION, dtp.Modulation());
774 SETCMD(DTV_INNER_FEC, dtp.CoderateH());
775 SETCMD(DTV_INVERSION, dtp.Inversion());
776 if (frontendType == SYS_DVBS2) {
778 SETCMD(DTV_PILOT, PILOT_AUTO);
779 SETCMD(DTV_ROLLOFF, dtp.RollOff());
783 SETCMD(DTV_ROLLOFF, ROLLOFF_35);
789 else if (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B) {
792 SETCMD(DTV_INVERSION, dtp.Inversion());
794 SETCMD(DTV_INNER_FEC, dtp.CoderateH());
795 SETCMD(DTV_MODULATION, dtp.Modulation());
800 else if (frontendType == SYS_DVBT || frontendType == SYS_DVBT2) {
803 SETCMD(DTV_INVERSION, dtp.Inversion());
804 SETCMD(DTV_BANDWIDTH_HZ, dtp.Bandwidth());
805 SETCMD(DTV_CODE_RATE_HP, dtp.CoderateH());
806 SETCMD(DTV_CODE_RATE_LP, dtp.CoderateL());
807 SETCMD(DTV_MODULATION, dtp.Modulation());
808 SETCMD(DTV_TRANSMISSION_MODE, dtp.Transmission());
809 SETCMD(DTV_GUARD_INTERVAL, dtp.Guard());
810 SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
811 if (frontendType == SYS_DVBT2) {
813 SETCMD(DTV_DVBT2_PLP_ID, dtp.PlpId());
819 else if (frontendType == SYS_ATSC) {
822 SETCMD(DTV_INVERSION, dtp.Inversion());
823 SETCMD(DTV_MODULATION, dtp.Modulation());
829 esyslog(
"ERROR: attempt to set channel with unknown DVB frontend type");
833 if (ioctl(
fd_frontend, FE_SET_PROPERTY, &CmdSeq) < 0) {
843 bool LostLock =
false;
844 fe_status_t Status = (fe_status_t)0;
846 fe_status_t NewStatus;
870 if (Status & FE_REINIT) {
877 else if (Status & FE_HAS_LOCK) {
939 #define ST(s) if (strchr(s, type))
954 default:
return NULL;
1014 if (fd_frontend >= 0) {
1042 int fd = open(FileName, Mode);
1043 if (fd < 0 && ReportError)
1051 if (access(FileName, F_OK) == 0) {
1052 int f = open(FileName, O_RDONLY);
1057 else if (errno != ENODEV && errno != EINVAL)
1060 else if (errno != ENOENT)
1068 dsyslog(
"probing %s", *FileName);
1070 if (dp->Probe(Adapter, Frontend))
1073 dsyslog(
"creating cDvbDevice");
1104 while ((a = DvbDir.
Next()) != NULL) {
1108 if (AdapterDir.
Ok()) {
1110 while ((f = AdapterDir.
Next()) != NULL) {
1122 if (Nodes.
Size() > 0) {
1124 for (
int i = 0; i < Nodes.
Size(); i++) {
1127 if (2 == sscanf(Nodes[i],
"%d %d", &Adapter, &Frontend)) {
1128 if (
Exists(Adapter, Frontend)) {
1131 if (
Probe(Adapter, Frontend))
1143 isyslog(
"found %d DVB device%s", Found, Found > 1 ?
"s" :
"");
1145 isyslog(
"no DVB device found");
1152 if (ioctl(fd_frontend, FE_GET_INFO, &
frontendInfo) < 0) {
1156 #if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) >= 0x0505
1158 memset(&Frontend, 0,
sizeof(Frontend));
1159 dtv_properties CmdSeq;
1160 memset(&CmdSeq, 0,
sizeof(CmdSeq));
1162 SETCMD(DTV_ENUM_DELSYS, 0);
1163 int Result = ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq);
1165 for (uint i = 0; i < Frontend[0].u.buffer.len; i++) {
1174 esyslog(
"ERROR: can't query delivery systems on frontend %d/%d - falling back to legacy mode",
adapter,
frontend);
1208 ms =
"unknown modulations";
1232 int ErrorDevice = 0;
1235 if (
cDvbDevice *DvbDevice1 = dynamic_cast<cDvbDevice *>(Device1)) {
1236 if (
cDvbDevice *DvbDevice2 = dynamic_cast<cDvbDevice *>(Device2)) {
1237 if (!DvbDevice1->Bond(DvbDevice2))
1241 ErrorDevice = d + 1;
1244 ErrorDevice = i + 1;
1246 esyslog(
"ERROR: device '%d' in device bondings '%s' is not a cDvbDevice", ErrorDevice, Bondings);
1251 ErrorDevice = d + 1;
1254 ErrorDevice = i + 1;
1256 esyslog(
"ERROR: unknown device '%d' in device bondings '%s'", ErrorDevice, Bondings);
1277 if (Device !=
this) {
1287 esyslog(
"ERROR: can't bond device %d with device %d (only DVB-S(2) devices can be bonded)",
CardIndex() + 1, Device->
CardIndex() + 1);
1304 while (d->bondedDevice !=
this)
1305 d = d->bondedDevice;
1307 d->bondedDevice = NULL;
1330 dmx_pes_filter_params pesFilterParams;
1331 memset(&pesFilterParams, 0,
sizeof(pesFilterParams));
1333 if (Handle->
handle < 0) {
1335 if (Handle->
handle < 0) {
1340 pesFilterParams.pid = Handle->
pid;
1341 pesFilterParams.input = DMX_IN_FRONTEND;
1342 pesFilterParams.output = DMX_OUT_TS_TAP;
1343 pesFilterParams.pes_type= DMX_PES_OTHER;
1344 pesFilterParams.flags = DMX_IMMEDIATE_START;
1345 if (ioctl(Handle->
handle, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
1350 else if (!Handle->
used) {
1353 pesFilterParams.pid = 0x1FFF;
1354 pesFilterParams.input = DMX_IN_FRONTEND;
1355 pesFilterParams.output = DMX_OUT_DECODER;
1356 pesFilterParams.pes_type= DMX_PES_OTHER;
1357 pesFilterParams.flags = DMX_IMMEDIATE_START;
1358 CHECK(ioctl(Handle->
handle, DMX_SET_PES_FILTER, &pesFilterParams));
1370 int f = open(FileName, O_RDWR | O_NONBLOCK);
1372 dmx_sct_filter_params sctFilterParams;
1373 memset(&sctFilterParams, 0,
sizeof(sctFilterParams));
1374 sctFilterParams.pid = Pid;
1375 sctFilterParams.timeout = 0;
1376 sctFilterParams.flags = DMX_IMMEDIATE_START;
1377 sctFilterParams.filter.filter[0] = Tid;
1378 sctFilterParams.filter.mask[0] = Mask;
1379 if (ioctl(f, DMX_SET_FILTER, &sctFilterParams) >= 0)
1382 esyslog(
"ERROR: can't set filter (pid=%d, tid=%02X, mask=%02X): %m", Pid, Tid, Mask);
1387 esyslog(
"ERROR: can't open filter handle on '%s'", *FileName);
1421 dtp.Modulation() == QPSK && !(
frontendInfo.caps & FE_CAN_QPSK) ||
1422 dtp.Modulation() == QAM_16 && !(
frontendInfo.caps & FE_CAN_QAM_16) ||
1423 dtp.Modulation() == QAM_32 && !(
frontendInfo.caps & FE_CAN_QAM_32) ||
1424 dtp.Modulation() == QAM_64 && !(
frontendInfo.caps & FE_CAN_QAM_64) ||
1425 dtp.Modulation() == QAM_128 && !(
frontendInfo.caps & FE_CAN_QAM_128) ||
1426 dtp.Modulation() == QAM_256 && !(
frontendInfo.caps & FE_CAN_QAM_256) ||
1427 dtp.Modulation() == QAM_AUTO && !(
frontendInfo.caps & FE_CAN_QAM_AUTO) ||
1428 dtp.Modulation() == VSB_8 && !(
frontendInfo.caps & FE_CAN_8VSB) ||
1429 dtp.Modulation() == VSB_16 && !(
frontendInfo.caps & FE_CAN_16VSB) ||
1440 bool result =
false;
1442 bool needsDetachReceivers =
false;
1446 result = hasPriority;
1455 needsDetachReceivers =
true;
1481 if (NeedsDetachReceivers)
1482 *NeedsDetachReceivers = needsDetachReceivers;
1571 d->cDevice::DetachAllReceivers();
1583 DvbDeviceProbes.
Add(
this);
1588 DvbDeviceProbes.
Del(
this,
false);
1593 uint32_t SubsystemId = 0;
1596 if (stat(FileName, &st) == 0) {
1600 while ((e = d.
Next()) != NULL) {
1601 if (strstr(e->d_name,
"frontend")) {
1603 if (FILE *f = fopen(FileName,
"r")) {
1605 char *s = ReadLine.
Read(f);
1609 if (s && 2 == sscanf(s,
"%u:%u", &Major, &Minor)) {
1610 if (((Major << 8) | Minor) == st.st_rdev) {
1611 FileName =
cString::sprintf(
"/sys/class/dvb/%s/device/subsystem_vendor", e->d_name);
1612 if ((f = fopen(FileName,
"r")) != NULL) {
1613 if (
char *s = ReadLine.
Read(f))
1614 SubsystemId = strtoul(s, NULL, 0) << 16;
1617 FileName =
cString::sprintf(
"/sys/class/dvb/%s/device/subsystem_device", e->d_name);
1618 if ((f = fopen(FileName,
"r")) != NULL) {
1619 if (
char *s = ReadLine.
Read(f))
1620 SubsystemId |= strtoul(s, NULL, 0);